polyphony 0.25 → 0.26
Sign up to get free protection for your applications and to get access to all the features.
- 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'
|