polyphony 0.25 → 0.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/Gemfile.lock +2 -2
- data/TODO.md +15 -16
- data/ext/gyro/child.c +5 -1
- data/ext/gyro/gyro.c +0 -4
- data/ext/gyro/gyro.h +0 -2
- data/ext/gyro/gyro_ext.c +0 -1
- data/ext/gyro/io.c +51 -19
- data/ext/gyro/libev.c +2 -0
- data/lib/polyphony.rb +0 -1
- data/lib/polyphony/extensions/core.rb +9 -9
- data/lib/polyphony/extensions/fiber.rb +20 -5
- data/lib/polyphony/extensions/io.rb +15 -2
- data/lib/polyphony/extensions/openssl.rb +4 -4
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +3 -2
- data/test/coverage.rb +16 -7
- data/test/test_ext.rb +175 -0
- data/test/test_fiber.rb +171 -90
- data/test/test_global_api.rb +16 -0
- data/test/test_supervisor.rb +3 -3
- data/test/test_timer.rb +1 -1
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d97b4ef9db1f6463947a3d5e0e300ee0155bd576ee878a4866557a2cc97cf785
|
4
|
+
data.tar.gz: 8fa0f7e075ce3c7bd9cf28270d06b0fbfe399657571aeaa23773400cb6535f58
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ac3eb507f068f54f70f5e1a79d964c39943f7ac7cfa945da02847f59ec53fc23739b2633428258d0caaf91bfdacc25cfcb6fa73175f8d43c7ed38fdd81f0a360
|
7
|
+
data.tar.gz: 75402a0ca525da7aa02fbe933c4f76328d9272ecb9dd7e404003b85a92e327882ca50d63eed49140593628cf0989d1a8cf7aac6abab5ad766124b1e5e7d00348
|
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
data/TODO.md
CHANGED
@@ -1,27 +1,26 @@
|
|
1
|
-
## 0.
|
1
|
+
## 0.26 Real IO#gets and IO#read
|
2
2
|
|
3
|
-
-
|
4
|
-
|
5
|
-
-
|
6
|
-
-
|
7
|
-
-
|
8
|
-
-
|
9
|
-
-
|
10
|
-
modest increase in throughput, as well as significantly less memory usage.
|
11
|
-
- Handle calls to `#sleep` without duration (should just `#suspend`)
|
12
|
-
|
13
|
-
## 0.26 Move Other interface code into separate gem
|
14
|
-
|
15
|
-
- Pull out redis/postgres code, put into new `polyphony-contrib` gem
|
3
|
+
- More tests
|
4
|
+
- Implement some basic stuff missing:
|
5
|
+
- override `IO#eof?` since it too reads into buffer
|
6
|
+
- real `IO#gets` (with buffering)
|
7
|
+
- `IO#read` (read to EOF)
|
8
|
+
- `IO.foreach`
|
9
|
+
- `Process.waitpid`
|
16
10
|
|
17
11
|
## 0.27 Working Sinatra application
|
18
12
|
|
13
|
+
- Pull out redis/postgres code, put into new `polyphony-xxx` gems
|
19
14
|
- app with database access (postgresql)
|
20
15
|
- benchmarks!
|
21
16
|
|
22
|
-
## 0.28
|
17
|
+
## 0.28 Sidekick
|
18
|
+
|
19
|
+
Plan of action:
|
23
20
|
|
24
|
-
-
|
21
|
+
- fork sidekiq, make adjustments to Polyphony code
|
22
|
+
- test performance
|
23
|
+
- proceed from there
|
25
24
|
|
26
25
|
## 0.29 Testing && Docs
|
27
26
|
|
data/ext/gyro/child.c
CHANGED
@@ -89,7 +89,11 @@ void Gyro_Child_callback(struct ev_loop *ev_loop, struct ev_child *ev_child, int
|
|
89
89
|
|
90
90
|
if (child->fiber != Qnil) {
|
91
91
|
VALUE fiber = child->fiber;
|
92
|
-
|
92
|
+
int exit_status = ev_child->rstatus >> 8; // weird, why should we do this?
|
93
|
+
|
94
|
+
VALUE resume_value = rb_ary_new_from_args(
|
95
|
+
2, INT2NUM(ev_child->rpid), INT2NUM(exit_status)
|
96
|
+
);
|
93
97
|
child->fiber = Qnil;
|
94
98
|
Gyro_schedule_fiber(fiber, resume_value);
|
95
99
|
}
|
data/ext/gyro/gyro.c
CHANGED
@@ -31,13 +31,11 @@ ID ID_clear;
|
|
31
31
|
ID ID_each;
|
32
32
|
ID ID_inspect;
|
33
33
|
ID ID_raise;
|
34
|
-
ID ID_read_watcher;
|
35
34
|
ID ID_running;
|
36
35
|
ID ID_scheduled;
|
37
36
|
ID ID_scheduled_next;
|
38
37
|
ID ID_scheduled_value;
|
39
38
|
ID ID_transfer;
|
40
|
-
ID ID_write_watcher;
|
41
39
|
ID ID_R;
|
42
40
|
ID ID_W;
|
43
41
|
ID ID_RW;
|
@@ -75,13 +73,11 @@ void Init_Gyro() {
|
|
75
73
|
ID_each = rb_intern("each");
|
76
74
|
ID_inspect = rb_intern("inspect");
|
77
75
|
ID_raise = rb_intern("raise");
|
78
|
-
ID_read_watcher = rb_intern("read_watcher");
|
79
76
|
ID_running = rb_intern("@running");
|
80
77
|
ID_scheduled = rb_intern("scheduled");
|
81
78
|
ID_scheduled_next = rb_intern("scheduled_next");
|
82
79
|
ID_scheduled_value = rb_intern("scheduled_value");
|
83
80
|
ID_transfer = rb_intern("transfer");
|
84
|
-
ID_write_watcher = rb_intern("write_watcher");
|
85
81
|
ID_R = rb_intern("r");
|
86
82
|
ID_W = rb_intern("w");
|
87
83
|
ID_RW = rb_intern("rw");
|
data/ext/gyro/gyro.h
CHANGED
@@ -44,10 +44,8 @@ extern ID ID_clear;
|
|
44
44
|
extern ID ID_each;
|
45
45
|
extern ID ID_inspect;
|
46
46
|
extern ID ID_raise;
|
47
|
-
extern ID ID_read_watcher;
|
48
47
|
extern ID ID_scheduled_value;
|
49
48
|
extern ID ID_transfer;
|
50
|
-
extern ID ID_write_watcher;
|
51
49
|
extern ID ID_R;
|
52
50
|
extern ID ID_W;
|
53
51
|
extern ID ID_RW;
|
data/ext/gyro/gyro_ext.c
CHANGED
data/ext/gyro/io.c
CHANGED
@@ -32,6 +32,11 @@ static VALUE IO_readpartial(int argc, VALUE *argv, VALUE io);
|
|
32
32
|
static VALUE IO_write(int argc, VALUE *argv, VALUE io);
|
33
33
|
static VALUE IO_write_chevron(VALUE io, VALUE str);
|
34
34
|
|
35
|
+
ID ID_read_watcher;
|
36
|
+
ID ID_write_watcher;
|
37
|
+
VALUE SYM_r;
|
38
|
+
VALUE SYM_w;
|
39
|
+
|
35
40
|
void Init_Gyro_IO() {
|
36
41
|
cGyro_IO = rb_define_class_under(mGyro, "IO", rb_cData);
|
37
42
|
rb_define_alloc_func(cGyro_IO, Gyro_IO_allocate);
|
@@ -48,6 +53,11 @@ void Init_Gyro_IO() {
|
|
48
53
|
rb_define_method(cIO, "<<", IO_write_chevron, 1);
|
49
54
|
rb_define_method(cIO, "read_watcher", IO_read_watcher, 0);
|
50
55
|
rb_define_method(cIO, "write_watcher", IO_write_watcher, 0);
|
56
|
+
|
57
|
+
ID_read_watcher = rb_intern("@read_watcher");
|
58
|
+
ID_write_watcher = rb_intern("@write_watcher");
|
59
|
+
SYM_r = ID2SYM(rb_intern("r"));
|
60
|
+
SYM_w = ID2SYM(rb_intern("w"));
|
51
61
|
}
|
52
62
|
|
53
63
|
static const rb_data_type_t Gyro_IO_type = {
|
@@ -286,11 +296,28 @@ static VALUE IO_read(int argc, VALUE *argv, VALUE io) {
|
|
286
296
|
return str;
|
287
297
|
}
|
288
298
|
|
299
|
+
#define READ_DATA_PENDING_COUNT(fptr) ((fptr)->rbuf.len)
|
300
|
+
#define MEMMOVE(p1,p2,type,n) memmove((p1), (p2), sizeof(type)*(size_t)(n))
|
301
|
+
|
302
|
+
static long
|
303
|
+
read_buffered_data(char *ptr, long len, rb_io_t *fptr)
|
304
|
+
{
|
305
|
+
int n;
|
306
|
+
|
307
|
+
n = READ_DATA_PENDING_COUNT(fptr);
|
308
|
+
if (n <= 0) return 0;
|
309
|
+
if (n > len) n = (int)len;
|
310
|
+
MEMMOVE(ptr, fptr->rbuf.ptr+fptr->rbuf.off, char, n);
|
311
|
+
fptr->rbuf.off += n;
|
312
|
+
fptr->rbuf.len -= n;
|
313
|
+
return n;
|
314
|
+
}
|
315
|
+
|
289
316
|
static VALUE IO_readpartial(int argc, VALUE *argv, VALUE io) {
|
290
317
|
VALUE underlying_io = rb_iv_get(io, "@io");
|
291
318
|
if (!NIL_P(underlying_io)) io = underlying_io;
|
292
319
|
|
293
|
-
long len = argc
|
320
|
+
long len = argc >= 1 ? NUM2LONG(argv[0]) : 8192;
|
294
321
|
|
295
322
|
rb_io_t *fptr;
|
296
323
|
long n;
|
@@ -312,21 +339,24 @@ static VALUE IO_readpartial(int argc, VALUE *argv, VALUE io) {
|
|
312
339
|
if (len == 0)
|
313
340
|
return str;
|
314
341
|
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
if (
|
320
|
-
|
321
|
-
|
322
|
-
|
342
|
+
n = read_buffered_data(RSTRING_PTR(str), len, fptr);
|
343
|
+
if (n <= 0) {
|
344
|
+
while (1) {
|
345
|
+
n = read(fptr->fd, RSTRING_PTR(str), len);
|
346
|
+
if (n < 0) {
|
347
|
+
int e = errno;
|
348
|
+
if (e == EWOULDBLOCK || e == EAGAIN) {
|
349
|
+
if (read_watcher == Qnil)
|
350
|
+
read_watcher = IO_read_watcher(io);
|
351
|
+
Gyro_IO_await(read_watcher);
|
352
|
+
}
|
353
|
+
else
|
354
|
+
rb_syserr_fail(e, strerror(e));
|
355
|
+
// rb_syserr_fail_path(e, fptr->pathv);
|
323
356
|
}
|
324
357
|
else
|
325
|
-
|
326
|
-
// rb_syserr_fail_path(e, fptr->pathv);
|
358
|
+
break;
|
327
359
|
}
|
328
|
-
else
|
329
|
-
break;
|
330
360
|
}
|
331
361
|
|
332
362
|
io_set_read_length(str, n, shrinkable);
|
@@ -406,19 +436,21 @@ static VALUE IO_write_chevron(VALUE io, VALUE str) {
|
|
406
436
|
}
|
407
437
|
|
408
438
|
VALUE IO_read_watcher(VALUE self) {
|
409
|
-
VALUE watcher =
|
439
|
+
VALUE watcher = rb_ivar_get(self, ID_read_watcher);
|
410
440
|
if (watcher == Qnil) {
|
411
|
-
|
412
|
-
|
441
|
+
VALUE args[] = {self, SYM_r};
|
442
|
+
watcher = rb_class_new_instance(2, args, cGyro_IO);
|
443
|
+
rb_ivar_set(self, ID_read_watcher, watcher);
|
413
444
|
}
|
414
445
|
return watcher;
|
415
446
|
}
|
416
447
|
|
417
448
|
VALUE IO_write_watcher(VALUE self) {
|
418
|
-
VALUE watcher =
|
449
|
+
VALUE watcher = rb_ivar_get(self, ID_write_watcher);
|
419
450
|
if (watcher == Qnil) {
|
420
|
-
|
421
|
-
|
451
|
+
VALUE args[] = {self, SYM_w};
|
452
|
+
watcher = rb_class_new_instance(2, args, cGyro_IO);
|
453
|
+
rb_ivar_set(self, ID_write_watcher, watcher);
|
422
454
|
}
|
423
455
|
return watcher;
|
424
456
|
}
|
data/ext/gyro/libev.c
ADDED
data/lib/polyphony.rb
CHANGED
@@ -58,8 +58,6 @@ module ::Kernel
|
|
58
58
|
|
59
59
|
alias_method :orig_backtick, :`
|
60
60
|
def `(cmd)
|
61
|
-
# $stdout.orig_puts '*' * 60
|
62
|
-
# $stdout.orig_puts caller.join("\n")
|
63
61
|
Open3.popen3(cmd) do |i, o, e, _t|
|
64
62
|
i.close
|
65
63
|
while (l = e.readpartial(8192))
|
@@ -70,25 +68,29 @@ module ::Kernel
|
|
70
68
|
end
|
71
69
|
|
72
70
|
ARGV_GETS_LOOP = proc do |calling_fiber|
|
73
|
-
ARGV.
|
71
|
+
while (fn = ARGV.shift)
|
74
72
|
File.open(fn, 'r') do |f|
|
75
73
|
while (line = f.gets)
|
76
74
|
calling_fiber = calling_fiber.transfer(line)
|
77
75
|
end
|
78
76
|
end
|
79
77
|
end
|
78
|
+
nil
|
80
79
|
rescue Exception => e
|
81
80
|
calling_fiber.transfer(e)
|
82
81
|
end
|
83
82
|
|
84
83
|
alias_method :orig_gets, :gets
|
85
84
|
def gets(*_args)
|
86
|
-
|
85
|
+
if !ARGV.empty? || @gets_fiber
|
86
|
+
@gets_fiber ||= Fiber.new(&ARGV_GETS_LOOP)
|
87
|
+
result = @gets_fiber.alive? && @gets_fiber.safe_transfer(Fiber.current)
|
88
|
+
return result if result
|
87
89
|
|
88
|
-
|
89
|
-
|
90
|
+
@gets_fiber = nil
|
91
|
+
end
|
90
92
|
|
91
|
-
|
93
|
+
$stdin.gets
|
92
94
|
end
|
93
95
|
|
94
96
|
alias_method :orig_system, :system
|
@@ -100,8 +102,6 @@ module ::Kernel
|
|
100
102
|
end
|
101
103
|
end
|
102
104
|
true
|
103
|
-
rescue SystemCallError
|
104
|
-
nil
|
105
105
|
end
|
106
106
|
end
|
107
107
|
|
@@ -8,7 +8,7 @@ Exceptions = import '../core/exceptions'
|
|
8
8
|
module FiberControl
|
9
9
|
def await
|
10
10
|
if @running == false
|
11
|
-
return @result.is_a?(Exception) ? (raise @result) : @result
|
11
|
+
return @result.is_a?(Exception) ? (Kernel.raise @result) : @result
|
12
12
|
end
|
13
13
|
|
14
14
|
@waiting_fiber = Fiber.current
|
@@ -32,6 +32,21 @@ module FiberControl
|
|
32
32
|
schedule Exceptions::Cancel.new
|
33
33
|
snooze
|
34
34
|
end
|
35
|
+
|
36
|
+
def raise(*args)
|
37
|
+
error = error_from_raise_args(args)
|
38
|
+
schedule error
|
39
|
+
snooze
|
40
|
+
end
|
41
|
+
|
42
|
+
def error_from_raise_args(args)
|
43
|
+
case (arg = args.shift)
|
44
|
+
when String then RuntimeError.new(arg)
|
45
|
+
when Class then arg.new(args.shift)
|
46
|
+
when Exception then arg
|
47
|
+
else RuntimeError.new
|
48
|
+
end
|
49
|
+
end
|
35
50
|
end
|
36
51
|
|
37
52
|
# Messaging functionality
|
@@ -109,7 +124,7 @@ class ::Fiber
|
|
109
124
|
end
|
110
125
|
|
111
126
|
def run(first_value)
|
112
|
-
raise first_value if first_value.is_a?(Exception)
|
127
|
+
Kernel.raise first_value if first_value.is_a?(Exception)
|
113
128
|
|
114
129
|
@running = true
|
115
130
|
self.class.map[self] = true
|
@@ -156,11 +171,11 @@ class ::Fiber
|
|
156
171
|
end
|
157
172
|
|
158
173
|
def caller
|
159
|
-
@caller
|
174
|
+
spin_caller = @caller || []
|
160
175
|
if @calling_fiber
|
161
|
-
|
176
|
+
spin_caller + @calling_fiber.caller
|
162
177
|
else
|
163
|
-
|
178
|
+
spin_caller
|
164
179
|
end
|
165
180
|
end
|
166
181
|
end
|
@@ -163,13 +163,26 @@ class ::IO
|
|
163
163
|
|
164
164
|
alias_method :orig_write_nonblock, :write_nonblock
|
165
165
|
def write_nonblock(string, _options = {})
|
166
|
-
# STDOUT << '>'
|
167
166
|
write(string, 0)
|
168
167
|
end
|
169
168
|
|
170
169
|
alias_method :orig_read_nonblock, :read_nonblock
|
171
170
|
def read_nonblock(maxlen, buf = nil, _options = nil)
|
172
|
-
# STDOUT << '<'
|
173
171
|
buf ? readpartial(maxlen, buf) : readpartial(maxlen)
|
174
172
|
end
|
173
|
+
|
174
|
+
alias_method :orig_read, :read
|
175
|
+
def read(length = nil, outbuf = nil)
|
176
|
+
if length
|
177
|
+
return outbuf ? readpartial(length) : readpartial(length, outbuf)
|
178
|
+
end
|
179
|
+
|
180
|
+
until eof?
|
181
|
+
result = outbuf ? readpartial(8192, outbuf) : readpartial(8192)
|
182
|
+
break unless result
|
183
|
+
|
184
|
+
outbuf = result
|
185
|
+
end
|
186
|
+
outbuf
|
187
|
+
end
|
175
188
|
end
|
@@ -23,8 +23,8 @@ class ::OpenSSL::SSL::SSLSocket
|
|
23
23
|
write_watcher = nil
|
24
24
|
loop do
|
25
25
|
case (result = read_nonblock(maxlen, buf, exception: false))
|
26
|
-
when :wait_readable then (read_watcher ||=
|
27
|
-
when :wait_writable then (write_watcher ||=
|
26
|
+
when :wait_readable then (read_watcher ||= io.read_watcher).await
|
27
|
+
when :wait_writable then (write_watcher ||= io.write_watcher).await
|
28
28
|
else result
|
29
29
|
end
|
30
30
|
end
|
@@ -61,8 +61,8 @@ class ::OpenSSL::SSL::SSLSocket
|
|
61
61
|
write_watcher = nil
|
62
62
|
loop do
|
63
63
|
case (result = write_nonblock(buf, exception: false))
|
64
|
-
when :wait_readable then (read_watcher ||=
|
65
|
-
when :wait_writable then (write_watcher ||=
|
64
|
+
when :wait_readable then (read_watcher ||= io.read_watcher).await
|
65
|
+
when :wait_writable then (write_watcher ||= io.write_watcher).await
|
66
66
|
else result
|
67
67
|
end
|
68
68
|
end
|
data/lib/polyphony/version.rb
CHANGED
data/polyphony.gemspec
CHANGED
@@ -8,9 +8,10 @@ Gem::Specification.new do |s|
|
|
8
8
|
s.author = 'Sharon Rosner'
|
9
9
|
s.email = 'ciconia@gmail.com'
|
10
10
|
s.files = `git ls-files`.split
|
11
|
-
s.homepage = '
|
11
|
+
s.homepage = 'https://dfab.gitbook.io/polyphony/'
|
12
12
|
s.metadata = {
|
13
|
-
"source_code_uri" => "https://github.com/digital-fabric/polyphony"
|
13
|
+
"source_code_uri" => "https://github.com/digital-fabric/polyphony",
|
14
|
+
"documentation_uri" => "https://dfab.gitbook.io/polyphony/"
|
14
15
|
}
|
15
16
|
s.rdoc_options = ["--title", "polyphony", "--main", "README.md"]
|
16
17
|
s.extra_rdoc_files = ["README.md"]
|
data/test/coverage.rb
CHANGED
@@ -3,13 +3,11 @@
|
|
3
3
|
require 'coverage'
|
4
4
|
require 'simplecov'
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
12
|
-
|
6
|
+
# Since we load code using Modulation, and the stock coverage gem does not
|
7
|
+
# calculate coverage for code loaded using `Kernel#eval` et al, we need to use
|
8
|
+
# the TracePoint API in order to trace execution. Here we monkey-patch the two
|
9
|
+
# main Coverage class methods, start and result to use TracePoint. Otherwise we
|
10
|
+
# let SimpleCov do its business.
|
13
11
|
module Coverage
|
14
12
|
EXCLUDE = %w{coverage eg helper run
|
15
13
|
}.map { |n| File.expand_path("test/#{n}.rb") }
|
@@ -42,4 +40,15 @@ module Coverage
|
|
42
40
|
end
|
43
41
|
end
|
44
42
|
|
43
|
+
class << SimpleCov::LinesClassifier
|
44
|
+
alias_method :orig_whitespace_line?, :whitespace_line?
|
45
|
+
def whitespace_line?(line)
|
46
|
+
# apparently TracePoint tracing does not cover lines including only keywords
|
47
|
+
# such as begin end etc, so here we mark those lines as whitespace, so they
|
48
|
+
# won't count towards the coverage score.
|
49
|
+
line.strip =~ /^(begin|end|ensure|else|\})|(\s*rescue\s.+)$/ ||
|
50
|
+
orig_whitespace_line?(line)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
45
54
|
SimpleCov.start
|
data/test/test_ext.rb
ADDED
@@ -0,0 +1,175 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'helper'
|
4
|
+
|
5
|
+
class ExceptionTest < MiniTest::Test
|
6
|
+
def test_sanitize
|
7
|
+
prev_disable = Exception.__disable_sanitized_backtrace__
|
8
|
+
Exception.__disable_sanitized_backtrace__ = false
|
9
|
+
|
10
|
+
begin
|
11
|
+
lineno = __LINE__ + 1
|
12
|
+
spin { raise 'foo' }
|
13
|
+
suspend
|
14
|
+
rescue => e
|
15
|
+
end
|
16
|
+
|
17
|
+
assert_kind_of Exception, e
|
18
|
+
backtrace = e.backtrace
|
19
|
+
location = "#{__FILE__}:#{lineno}"
|
20
|
+
assert_match /#{location}/, backtrace[0]
|
21
|
+
polyphony_re = /^#{Exception::POLYPHONY_DIR}/
|
22
|
+
assert_equal [], backtrace.select { |l| l =~ polyphony_re }
|
23
|
+
|
24
|
+
Exception.__disable_sanitized_backtrace__ = true
|
25
|
+
begin
|
26
|
+
lineno = __LINE__ + 1
|
27
|
+
spin { raise 'foo' }
|
28
|
+
suspend
|
29
|
+
rescue => e
|
30
|
+
end
|
31
|
+
|
32
|
+
assert_kind_of Exception, e
|
33
|
+
backtrace = e.backtrace
|
34
|
+
location = "#{__FILE__}:#{lineno}"
|
35
|
+
assert_match /#{location}/, backtrace[0]
|
36
|
+
assert_match /lib\/polyphony\/extensions\/fiber.rb/, backtrace[1]
|
37
|
+
assert_match /lib\/polyphony\/extensions\/fiber.rb/, backtrace[2]
|
38
|
+
ensure
|
39
|
+
Exception.__disable_sanitized_backtrace__ = prev_disable
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
class ProcessTest < MiniTest::Test
|
45
|
+
def test_detach
|
46
|
+
pid = Polyphony.fork { sleep 0.05; exit! 42 }
|
47
|
+
buffer = []
|
48
|
+
spin { 3.times { |i| buffer << i; snooze } }
|
49
|
+
w = Process.detach(pid)
|
50
|
+
|
51
|
+
assert_kind_of Fiber, w
|
52
|
+
result = w.await
|
53
|
+
|
54
|
+
assert_equal [0, 1, 2], buffer
|
55
|
+
assert_equal [pid, 42], result
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class KernelTest < MiniTest::Test
|
60
|
+
def test_backticks
|
61
|
+
buffer = []
|
62
|
+
spin { 3.times { |i| buffer << i; snooze } }
|
63
|
+
data = `sleep 0.01; echo hello`
|
64
|
+
|
65
|
+
assert_equal [0, 1, 2], buffer
|
66
|
+
assert_equal "hello\n", data
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_backticks_stderr
|
70
|
+
prev_stderr = $stderr
|
71
|
+
$stderr = err_io = StringIO.new
|
72
|
+
|
73
|
+
data = `>&2 echo "error"`
|
74
|
+
$stderr.rewind
|
75
|
+
$stderr = prev_stderr
|
76
|
+
|
77
|
+
assert_nil data
|
78
|
+
assert_equal "error\n", err_io.read
|
79
|
+
ensure
|
80
|
+
$stderr = prev_stderr
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_gets
|
84
|
+
prev_stdin = $stdin
|
85
|
+
i, o = IO.pipe
|
86
|
+
$stdin = i
|
87
|
+
|
88
|
+
spin { o << "hello\n" }
|
89
|
+
s = gets
|
90
|
+
|
91
|
+
assert_equal "hello\n", s
|
92
|
+
ensure
|
93
|
+
$stdin = prev_stdin
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_gets_from_argv
|
97
|
+
prev_stdin = $stdin
|
98
|
+
|
99
|
+
ARGV << __FILE__
|
100
|
+
ARGV << __FILE__
|
101
|
+
|
102
|
+
contents = IO.read(__FILE__).lines
|
103
|
+
count = contents.size
|
104
|
+
|
105
|
+
buffer = []
|
106
|
+
|
107
|
+
(count * 2).times { buffer << gets }
|
108
|
+
assert_equal contents * 2, buffer
|
109
|
+
|
110
|
+
i, o = IO.pipe
|
111
|
+
$stdin = i
|
112
|
+
|
113
|
+
spin { o << "hello\n" }
|
114
|
+
s = gets
|
115
|
+
|
116
|
+
assert_equal "hello\n", s
|
117
|
+
ensure
|
118
|
+
$stdin = prev_stdin
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_gets_from_bad_argv
|
122
|
+
prev_stdin = $stdin
|
123
|
+
|
124
|
+
ARGV << 'foobar'
|
125
|
+
|
126
|
+
begin
|
127
|
+
gets
|
128
|
+
rescue => e
|
129
|
+
end
|
130
|
+
|
131
|
+
assert_kind_of Errno::ENOENT, e
|
132
|
+
ensure
|
133
|
+
$stdin = prev_stdin
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_system
|
137
|
+
prev_stdout = $stdout
|
138
|
+
$stdout = out_io = StringIO.new
|
139
|
+
|
140
|
+
buffer = []
|
141
|
+
spin { 3.times { |i| buffer << i; snooze } }
|
142
|
+
system('sleep 0.01; echo hello')
|
143
|
+
out_io.rewind
|
144
|
+
$stdout = prev_stdout
|
145
|
+
|
146
|
+
assert_equal [0, 1, 2], buffer
|
147
|
+
assert_equal "hello\n", out_io.read
|
148
|
+
ensure
|
149
|
+
$stdout = prev_stdout
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
class TimeoutTest < MiniTest::Test
|
154
|
+
def test_that_timeout_yields_to_other_fibers
|
155
|
+
buffer = []
|
156
|
+
spin { 3.times { |i| buffer << i; snooze } }
|
157
|
+
assert_raises(Timeout::Error) { Timeout.timeout(0.05) { sleep 1 } }
|
158
|
+
assert_equal [0, 1, 2], buffer
|
159
|
+
end
|
160
|
+
|
161
|
+
class MyTimeout < Exception
|
162
|
+
end
|
163
|
+
|
164
|
+
def test_that_timeout_method_accepts_custom_error_class_and_message
|
165
|
+
buffer = []
|
166
|
+
spin { 3.times { |i| buffer << i; snooze } }
|
167
|
+
begin
|
168
|
+
Timeout.timeout(0.05, MyTimeout, 'foo') { sleep 1 }
|
169
|
+
rescue Exception => e
|
170
|
+
end
|
171
|
+
|
172
|
+
assert_kind_of MyTimeout, e
|
173
|
+
assert_equal 'foo', e.message
|
174
|
+
end
|
175
|
+
end
|
data/test/test_fiber.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
require_relative 'helper'
|
4
4
|
|
5
5
|
class FiberTest < MiniTest::Test
|
6
|
-
def
|
6
|
+
def test_spin_initial_state
|
7
7
|
result = nil
|
8
8
|
f = Fiber.spin { result = 42 }
|
9
9
|
assert_nil result
|
@@ -13,7 +13,7 @@ class FiberTest < MiniTest::Test
|
|
13
13
|
f&.stop
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
16
|
+
def test_await
|
17
17
|
result = nil
|
18
18
|
f = Fiber.spin do
|
19
19
|
snooze
|
@@ -25,14 +25,14 @@ class FiberTest < MiniTest::Test
|
|
25
25
|
f&.stop
|
26
26
|
end
|
27
27
|
|
28
|
-
def
|
28
|
+
def test_await_return_value
|
29
29
|
f = Fiber.spin { %i[foo bar] }
|
30
30
|
assert_equal %i[foo bar], f.await
|
31
31
|
ensure
|
32
32
|
f&.stop
|
33
33
|
end
|
34
34
|
|
35
|
-
def
|
35
|
+
def test_await_with_error
|
36
36
|
result = nil
|
37
37
|
f = Fiber.spin { raise 'foo' }
|
38
38
|
begin
|
@@ -46,7 +46,123 @@ class FiberTest < MiniTest::Test
|
|
46
46
|
f&.stop
|
47
47
|
end
|
48
48
|
|
49
|
-
def
|
49
|
+
def test_raise
|
50
|
+
result = []
|
51
|
+
error = nil
|
52
|
+
f = Fiber.spin do
|
53
|
+
result << 1
|
54
|
+
2.times { snooze }
|
55
|
+
result << 2
|
56
|
+
end
|
57
|
+
defer { f.raise }
|
58
|
+
assert_equal 0, result.size
|
59
|
+
begin
|
60
|
+
f.await
|
61
|
+
rescue Exception => e
|
62
|
+
error = e
|
63
|
+
end
|
64
|
+
assert_equal 1, result.size
|
65
|
+
assert_equal 1, result[0]
|
66
|
+
assert_kind_of RuntimeError, error
|
67
|
+
ensure
|
68
|
+
f&.stop
|
69
|
+
end
|
70
|
+
|
71
|
+
class MyError < RuntimeError
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_raise_with_error_class
|
75
|
+
result = []
|
76
|
+
error = nil
|
77
|
+
f = Fiber.spin do
|
78
|
+
result << 1
|
79
|
+
2.times { snooze }
|
80
|
+
result << 2
|
81
|
+
end
|
82
|
+
defer { f.raise MyError }
|
83
|
+
assert_equal 0, result.size
|
84
|
+
begin
|
85
|
+
f.await
|
86
|
+
rescue Exception => e
|
87
|
+
error = e
|
88
|
+
end
|
89
|
+
assert_equal 1, result.size
|
90
|
+
assert_equal 1, result[0]
|
91
|
+
assert_kind_of MyError, error
|
92
|
+
ensure
|
93
|
+
f&.stop
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_raise_with_error_class_and_message
|
97
|
+
result = []
|
98
|
+
error = nil
|
99
|
+
f = Fiber.spin do
|
100
|
+
result << 1
|
101
|
+
2.times { snooze }
|
102
|
+
result << 2
|
103
|
+
end
|
104
|
+
defer { f.raise(MyError, 'foo') }
|
105
|
+
assert_equal 0, result.size
|
106
|
+
begin
|
107
|
+
f.await
|
108
|
+
rescue Exception => e
|
109
|
+
error = e
|
110
|
+
end
|
111
|
+
assert_equal 1, result.size
|
112
|
+
assert_equal 1, result[0]
|
113
|
+
assert_kind_of MyError, error
|
114
|
+
assert_equal 'foo', error.message
|
115
|
+
ensure
|
116
|
+
f&.stop
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_raise_with_message
|
120
|
+
result = []
|
121
|
+
error = nil
|
122
|
+
f = Fiber.spin do
|
123
|
+
result << 1
|
124
|
+
2.times { snooze }
|
125
|
+
result << 2
|
126
|
+
end
|
127
|
+
defer { f.raise 'foo' }
|
128
|
+
assert_equal 0, result.size
|
129
|
+
begin
|
130
|
+
f.await
|
131
|
+
rescue Exception => e
|
132
|
+
error = e
|
133
|
+
end
|
134
|
+
assert_equal 1, result.size
|
135
|
+
assert_equal 1, result[0]
|
136
|
+
assert_kind_of RuntimeError, error
|
137
|
+
assert_equal 'foo', error.message
|
138
|
+
ensure
|
139
|
+
f&.stop
|
140
|
+
end
|
141
|
+
|
142
|
+
def test_raise_with_exception
|
143
|
+
result = []
|
144
|
+
error = nil
|
145
|
+
f = Fiber.spin do
|
146
|
+
result << 1
|
147
|
+
2.times { snooze }
|
148
|
+
result << 2
|
149
|
+
end
|
150
|
+
defer { f.raise MyError.new('bar') }
|
151
|
+
assert_equal 0, result.size
|
152
|
+
begin
|
153
|
+
f.await
|
154
|
+
rescue Exception => e
|
155
|
+
error = e
|
156
|
+
end
|
157
|
+
assert_equal 1, result.size
|
158
|
+
assert_equal 1, result[0]
|
159
|
+
assert_kind_of MyError, error
|
160
|
+
assert_equal 'bar', error.message
|
161
|
+
ensure
|
162
|
+
f&.stop
|
163
|
+
end
|
164
|
+
|
165
|
+
def test_cancel
|
50
166
|
result = []
|
51
167
|
error = nil
|
52
168
|
f = Fiber.spin do
|
@@ -68,7 +184,7 @@ class FiberTest < MiniTest::Test
|
|
68
184
|
f&.stop
|
69
185
|
end
|
70
186
|
|
71
|
-
def
|
187
|
+
def test_interrupt
|
72
188
|
# that is, stopped without exception
|
73
189
|
result = []
|
74
190
|
f = Fiber.spin do
|
@@ -77,7 +193,7 @@ class FiberTest < MiniTest::Test
|
|
77
193
|
result << 2
|
78
194
|
3
|
79
195
|
end
|
80
|
-
defer { f.
|
196
|
+
defer { f.interrupt(42) }
|
81
197
|
|
82
198
|
await_result = f.await
|
83
199
|
assert_equal 1, result.size
|
@@ -86,56 +202,38 @@ class FiberTest < MiniTest::Test
|
|
86
202
|
f&.stop
|
87
203
|
end
|
88
204
|
|
89
|
-
def
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
result = f2.await
|
205
|
+
def test_stop
|
206
|
+
# that is, stopped without exception
|
207
|
+
result = []
|
208
|
+
f = Fiber.spin do
|
209
|
+
result << 1
|
210
|
+
2.times { snooze }
|
211
|
+
result << 2
|
212
|
+
3
|
98
213
|
end
|
99
|
-
|
100
|
-
assert_equal 42, result
|
101
|
-
ensure
|
102
|
-
f1&.stop
|
103
|
-
f2&.stop
|
104
|
-
end
|
214
|
+
defer { f.stop(42) }
|
105
215
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
snooze
|
110
|
-
result = 42
|
111
|
-
end
|
112
|
-
defer { f.interrupt }
|
113
|
-
suspend
|
114
|
-
assert_nil result
|
216
|
+
await_result = f.await
|
217
|
+
assert_equal 1, result.size
|
218
|
+
assert_equal 42, await_result
|
115
219
|
ensure
|
116
220
|
f&.stop
|
117
221
|
end
|
118
222
|
|
119
|
-
def
|
120
|
-
result =
|
121
|
-
f = spin do
|
122
|
-
|
123
|
-
result = 42
|
124
|
-
rescue Polyphony::Cancel => e
|
125
|
-
result = e
|
223
|
+
def test_interrupt_before_start
|
224
|
+
result = []
|
225
|
+
f = Fiber.spin do
|
226
|
+
result << 1
|
126
227
|
end
|
127
|
-
|
128
|
-
|
129
|
-
suspend
|
228
|
+
f.interrupt(42)
|
229
|
+
snooze
|
130
230
|
|
131
|
-
assert_kind_of Polyphony::Cancel, result
|
132
|
-
assert_kind_of Polyphony::Cancel, f.result
|
133
231
|
assert_equal :dead, f.state
|
134
|
-
|
135
|
-
f
|
232
|
+
assert_equal [], result
|
233
|
+
assert_equal 42, f.result
|
136
234
|
end
|
137
235
|
|
138
|
-
def
|
236
|
+
def test_interrupt_nested_fiber
|
139
237
|
result = nil
|
140
238
|
f2 = nil
|
141
239
|
f1 = spin do
|
@@ -175,7 +273,7 @@ class FiberTest < MiniTest::Test
|
|
175
273
|
f&.stop
|
176
274
|
end
|
177
275
|
|
178
|
-
def
|
276
|
+
def test_exception_bubbling
|
179
277
|
# error is propagated to calling fiber
|
180
278
|
raised_error = nil
|
181
279
|
spin do
|
@@ -192,17 +290,7 @@ class FiberTest < MiniTest::Test
|
|
192
290
|
assert_equal 'foo', raised_error.message
|
193
291
|
end
|
194
292
|
|
195
|
-
def
|
196
|
-
buffer = []
|
197
|
-
f = spin { buffer << 1 }
|
198
|
-
f.stop
|
199
|
-
|
200
|
-
snooze
|
201
|
-
assert !f.running?
|
202
|
-
assert_equal [], buffer
|
203
|
-
end
|
204
|
-
|
205
|
-
def test_exception_propagation_for_orphan_fiber
|
293
|
+
def test_exception_bubling_for_orphan_fiber
|
206
294
|
raised_error = nil
|
207
295
|
spin do
|
208
296
|
spin do
|
@@ -288,38 +376,6 @@ class FiberTest < MiniTest::Test
|
|
288
376
|
assert_equal [42], values
|
289
377
|
assert !f.running?
|
290
378
|
end
|
291
|
-
|
292
|
-
def test_interrupt
|
293
|
-
f = spin do
|
294
|
-
sleep 1
|
295
|
-
:foo
|
296
|
-
end
|
297
|
-
|
298
|
-
snooze
|
299
|
-
assert f.alive?
|
300
|
-
|
301
|
-
f.interrupt :bar
|
302
|
-
assert !f.running?
|
303
|
-
|
304
|
-
assert_equal :bar, f.result
|
305
|
-
end
|
306
|
-
|
307
|
-
def test_cancel
|
308
|
-
error = nil
|
309
|
-
f = spin do
|
310
|
-
sleep 1
|
311
|
-
:foo
|
312
|
-
end
|
313
|
-
|
314
|
-
snooze
|
315
|
-
f.cancel!
|
316
|
-
rescue Polyphony::Cancel => e
|
317
|
-
# cancel error should bubble up
|
318
|
-
error = e
|
319
|
-
ensure
|
320
|
-
assert error
|
321
|
-
assert_equal :dead, f.state
|
322
|
-
end
|
323
379
|
end
|
324
380
|
|
325
381
|
class MailboxTest < MiniTest::Test
|
@@ -379,4 +435,29 @@ class MailboxTest < MiniTest::Test
|
|
379
435
|
snooze
|
380
436
|
assert_equal 1, Fiber.count
|
381
437
|
end
|
438
|
+
|
439
|
+
def test_inspect
|
440
|
+
expected = format('#<Fiber:%s (root) (running)>', Fiber.current.object_id)
|
441
|
+
assert_equal expected, Fiber.current.inspect
|
442
|
+
|
443
|
+
spin_line_no = __LINE__ + 1
|
444
|
+
f = spin { :foo }
|
445
|
+
|
446
|
+
expected = format(
|
447
|
+
'#<Fiber:%s %s:%d:in `test_inspect\' (scheduled)>',
|
448
|
+
f.object_id,
|
449
|
+
__FILE__,
|
450
|
+
spin_line_no
|
451
|
+
)
|
452
|
+
assert_equal expected, f.inspect
|
453
|
+
|
454
|
+
f.await
|
455
|
+
expected = format(
|
456
|
+
'#<Fiber:%s %s:%d:in `test_inspect\' (dead)>',
|
457
|
+
f.object_id,
|
458
|
+
__FILE__,
|
459
|
+
spin_line_no
|
460
|
+
)
|
461
|
+
assert_equal expected, f.inspect
|
462
|
+
end
|
382
463
|
end
|
data/test/test_global_api.rb
CHANGED
@@ -294,4 +294,20 @@ class MoveOnAfterTest < MiniTest::Test
|
|
294
294
|
f.stop
|
295
295
|
assert (4..5).include?(buffer.size)
|
296
296
|
end
|
297
|
+
|
298
|
+
def test_sleep
|
299
|
+
t0 = Time.now
|
300
|
+
sleep 0.05
|
301
|
+
elapsed = Time.now - t0
|
302
|
+
puts "* elapsed: #{elapsed.inspect}"
|
303
|
+
assert (0.045..0.8).include? elapsed
|
304
|
+
|
305
|
+
f = spin { sleep }
|
306
|
+
snooze
|
307
|
+
assert f.running?
|
308
|
+
snooze
|
309
|
+
assert f.running?
|
310
|
+
f.stop
|
311
|
+
assert !f.running?
|
312
|
+
end
|
297
313
|
end
|
data/test/test_supervisor.rb
CHANGED
@@ -56,14 +56,14 @@ class SupervisorTest < MiniTest::Test
|
|
56
56
|
foo_f = bar_f = baz_f = nil
|
57
57
|
result, f = Polyphony::Supervisor.new.select { |s|
|
58
58
|
foo_f = s.spin { sleep 0.01; buffer << :foo; :foo }
|
59
|
-
bar_f = s.spin { sleep 0.
|
60
|
-
baz_f = s.spin { sleep 0.
|
59
|
+
bar_f = s.spin { sleep 0.03; buffer << :bar; :bar }
|
60
|
+
baz_f = s.spin { sleep 0.05; buffer << :baz; :baz }
|
61
61
|
}
|
62
62
|
|
63
63
|
assert_equal :foo, result
|
64
64
|
assert_equal foo_f, f
|
65
65
|
|
66
|
-
sleep 0.
|
66
|
+
sleep 0.05
|
67
67
|
assert !bar_f.running?
|
68
68
|
assert !baz_f.running?
|
69
69
|
assert_equal [:foo], buffer
|
data/test/test_timer.rb
CHANGED
@@ -45,6 +45,6 @@ class TimerTest < MiniTest::Test
|
|
45
45
|
}
|
46
46
|
suspend
|
47
47
|
deltas = times.each_with_object([]) { |t, a| a << t - last; last = t }
|
48
|
-
assert_equal 0, deltas.filter { |d| (d - 0.01).abs >= 0.
|
48
|
+
assert_equal 0, deltas.filter { |d| (d - 0.01).abs >= 0.006 }.size
|
49
49
|
end
|
50
50
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: polyphony
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.26'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-01-
|
11
|
+
date: 2020-01-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: modulation
|
@@ -254,6 +254,7 @@ files:
|
|
254
254
|
- ext/gyro/gyro.h
|
255
255
|
- ext/gyro/gyro_ext.c
|
256
256
|
- ext/gyro/io.c
|
257
|
+
- ext/gyro/libev.c
|
257
258
|
- ext/gyro/libev.h
|
258
259
|
- ext/gyro/signal.c
|
259
260
|
- ext/gyro/socket.c
|
@@ -303,6 +304,7 @@ files:
|
|
303
304
|
- test/run.rb
|
304
305
|
- test/test_async.rb
|
305
306
|
- test/test_cancel_scope.rb
|
307
|
+
- test/test_ext.rb
|
306
308
|
- test/test_fiber.rb
|
307
309
|
- test/test_global_api.rb
|
308
310
|
- test/test_gyro.rb
|
@@ -312,11 +314,12 @@ files:
|
|
312
314
|
- test/test_signal.rb
|
313
315
|
- test/test_supervisor.rb
|
314
316
|
- test/test_timer.rb
|
315
|
-
homepage:
|
317
|
+
homepage: https://dfab.gitbook.io/polyphony/
|
316
318
|
licenses:
|
317
319
|
- MIT
|
318
320
|
metadata:
|
319
321
|
source_code_uri: https://github.com/digital-fabric/polyphony
|
322
|
+
documentation_uri: https://dfab.gitbook.io/polyphony/
|
320
323
|
post_install_message:
|
321
324
|
rdoc_options:
|
322
325
|
- "--title"
|
@@ -336,7 +339,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
336
339
|
- !ruby/object:Gem::Version
|
337
340
|
version: '0'
|
338
341
|
requirements: []
|
339
|
-
rubygems_version: 3.
|
342
|
+
rubygems_version: 3.1.2
|
340
343
|
signing_key:
|
341
344
|
specification_version: 4
|
342
345
|
summary: 'Polyphony: Fiber-based Concurrency for Ruby'
|