polyphony 0.59.1 → 0.60
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/Gemfile.lock +15 -29
- data/examples/core/message_based_supervision.rb +51 -0
- data/ext/polyphony/backend_common.c +18 -15
- data/ext/polyphony/backend_common.h +0 -1
- data/ext/polyphony/backend_io_uring.c +41 -19
- data/ext/polyphony/backend_io_uring_context.c +10 -1
- data/ext/polyphony/backend_io_uring_context.h +5 -3
- data/ext/polyphony/backend_libev.c +20 -15
- data/ext/polyphony/extconf.rb +2 -2
- data/ext/polyphony/fiber.c +1 -32
- data/ext/polyphony/polyphony.c +12 -12
- data/ext/polyphony/polyphony.h +4 -4
- data/ext/polyphony/queue.c +12 -12
- data/ext/polyphony/runqueue.c +0 -3
- data/lib/polyphony/extensions/fiber.rb +100 -80
- data/lib/polyphony/extensions/io.rb +10 -9
- data/lib/polyphony/extensions/openssl.rb +14 -4
- data/lib/polyphony/extensions/socket.rb +15 -15
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +0 -7
- data/test/test_backend.rb +42 -5
- data/test/test_ext.rb +1 -1
- data/test/test_fiber.rb +106 -18
- data/test/test_global_api.rb +1 -1
- data/test/test_io.rb +29 -0
- data/test/test_supervise.rb +100 -100
- data/test/test_thread_pool.rb +1 -1
- data/test/test_trace.rb +5 -4
- metadata +3 -106
@@ -22,7 +22,7 @@ typedef struct op_context {
|
|
22
22
|
struct op_context *prev;
|
23
23
|
struct op_context *next;
|
24
24
|
enum op_type type: 16;
|
25
|
-
unsigned int ref_count : 16;
|
25
|
+
unsigned int ref_count : 16;
|
26
26
|
int id;
|
27
27
|
int result;
|
28
28
|
VALUE fiber;
|
@@ -31,8 +31,10 @@ typedef struct op_context {
|
|
31
31
|
|
32
32
|
typedef struct op_context_store {
|
33
33
|
int last_id;
|
34
|
-
op_context_t
|
35
|
-
op_context_t
|
34
|
+
op_context_t *available;
|
35
|
+
op_context_t *taken;
|
36
|
+
int available_count;
|
37
|
+
int taken_count;
|
36
38
|
} op_context_store_t;
|
37
39
|
|
38
40
|
const char *op_type_to_str(enum op_type type);
|
@@ -217,7 +217,6 @@ inline struct backend_stats Backend_stats(VALUE self) {
|
|
217
217
|
|
218
218
|
return (struct backend_stats){
|
219
219
|
.scheduled_fibers = runqueue_len(&backend->base.runqueue),
|
220
|
-
.waiting_fibers = 0,
|
221
220
|
.pending_ops = backend->base.pending_count
|
222
221
|
};
|
223
222
|
}
|
@@ -261,14 +260,20 @@ VALUE libev_wait_fd(Backend_t *backend, int fd, int events, int raise_exception)
|
|
261
260
|
return switchpoint_result;
|
262
261
|
}
|
263
262
|
|
264
|
-
VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof) {
|
263
|
+
VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof, VALUE pos) {
|
265
264
|
Backend_t *backend;
|
266
265
|
struct libev_io watcher;
|
267
266
|
rb_io_t *fptr;
|
268
267
|
long dynamic_len = length == Qnil;
|
269
268
|
long len = dynamic_len ? 4096 : NUM2INT(length);
|
270
|
-
|
271
|
-
|
269
|
+
long buf_pos = NUM2INT(pos);
|
270
|
+
if (str != Qnil) {
|
271
|
+
int current_len = RSTRING_LEN(str);
|
272
|
+
if (buf_pos < 0 || buf_pos > current_len) buf_pos = current_len;
|
273
|
+
}
|
274
|
+
else buf_pos = 0;
|
275
|
+
int shrinkable = io_setstrbuf(&str, buf_pos + len);
|
276
|
+
char *buf = RSTRING_PTR(str) + buf_pos;
|
272
277
|
long total = 0;
|
273
278
|
VALUE switchpoint_result = Qnil;
|
274
279
|
int read_to_eof = RTEST(to_eof);
|
@@ -304,9 +309,9 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
|
|
304
309
|
if (total == len) {
|
305
310
|
if (!dynamic_len) break;
|
306
311
|
|
307
|
-
rb_str_resize(str, total);
|
312
|
+
rb_str_resize(str, buf_pos + total);
|
308
313
|
rb_str_modify_expand(str, len);
|
309
|
-
buf = RSTRING_PTR(str) + total;
|
314
|
+
buf = RSTRING_PTR(str) + buf_pos + total;
|
310
315
|
shrinkable = 0;
|
311
316
|
len += len;
|
312
317
|
}
|
@@ -314,7 +319,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
|
|
314
319
|
}
|
315
320
|
}
|
316
321
|
|
317
|
-
io_set_read_length(str, total, shrinkable);
|
322
|
+
io_set_read_length(str, buf_pos + total, shrinkable);
|
318
323
|
io_enc_str(str, fptr);
|
319
324
|
|
320
325
|
if (total == 0) return Qnil;
|
@@ -327,17 +332,17 @@ error:
|
|
327
332
|
return RAISE_EXCEPTION(switchpoint_result);
|
328
333
|
}
|
329
334
|
|
330
|
-
VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length) {
|
331
|
-
return Backend_read(self, io, str, length, Qnil);
|
335
|
+
VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length, VALUE pos) {
|
336
|
+
return Backend_read(self, io, str, length, Qnil, pos);
|
332
337
|
}
|
333
338
|
|
334
|
-
VALUE Backend_read_loop(VALUE self, VALUE io) {
|
339
|
+
VALUE Backend_read_loop(VALUE self, VALUE io, VALUE maxlen) {
|
335
340
|
Backend_t *backend;
|
336
341
|
struct libev_io watcher;
|
337
342
|
rb_io_t *fptr;
|
338
343
|
VALUE str;
|
339
344
|
long total;
|
340
|
-
long len =
|
345
|
+
long len = NUM2INT(maxlen);
|
341
346
|
int shrinkable;
|
342
347
|
char *buf;
|
343
348
|
VALUE switchpoint_result = Qnil;
|
@@ -1511,10 +1516,10 @@ void Init_Backend() {
|
|
1511
1516
|
rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
|
1512
1517
|
rb_define_method(cBackend, "connect", Backend_connect, 3);
|
1513
1518
|
rb_define_method(cBackend, "feed_loop", Backend_feed_loop, 3);
|
1514
|
-
rb_define_method(cBackend, "read", Backend_read,
|
1515
|
-
rb_define_method(cBackend, "read_loop", Backend_read_loop,
|
1516
|
-
rb_define_method(cBackend, "recv", Backend_recv,
|
1517
|
-
rb_define_method(cBackend, "recv_loop", Backend_read_loop,
|
1519
|
+
rb_define_method(cBackend, "read", Backend_read, 5);
|
1520
|
+
rb_define_method(cBackend, "read_loop", Backend_read_loop, 2);
|
1521
|
+
rb_define_method(cBackend, "recv", Backend_recv, 4);
|
1522
|
+
rb_define_method(cBackend, "recv_loop", Backend_read_loop, 2);
|
1518
1523
|
rb_define_method(cBackend, "recv_feed_loop", Backend_feed_loop, 3);
|
1519
1524
|
rb_define_method(cBackend, "send", Backend_send, 3);
|
1520
1525
|
rb_define_method(cBackend, "sendv", Backend_sendv, 3);
|
data/ext/polyphony/extconf.rb
CHANGED
@@ -8,8 +8,8 @@ use_pidfd_open = false
|
|
8
8
|
force_use_libev = ENV['POLYPHONY_USE_LIBEV'] != nil
|
9
9
|
linux = RUBY_PLATFORM =~ /linux/
|
10
10
|
|
11
|
-
if linux && `uname -sr` =~ /Linux 5\.(
|
12
|
-
kernel_minor_version = $1.
|
11
|
+
if linux && `uname -sr` =~ /Linux 5\.(\d+)/
|
12
|
+
kernel_minor_version = $1.to_i
|
13
13
|
use_liburing = !force_use_libev && kernel_minor_version >= 6
|
14
14
|
use_pidfd_open = kernel_minor_version >= 3
|
15
15
|
end
|
data/ext/polyphony/fiber.c
CHANGED
@@ -81,34 +81,6 @@ static VALUE Fiber_state(VALUE self) {
|
|
81
81
|
return SYM_waiting;
|
82
82
|
}
|
83
83
|
|
84
|
-
VALUE Fiber_await(VALUE self) {
|
85
|
-
VALUE result;
|
86
|
-
|
87
|
-
// we compare with false, since a fiber that has not yet started will have
|
88
|
-
// @running set to nil
|
89
|
-
if (rb_ivar_get(self, ID_ivar_running) == Qfalse) {
|
90
|
-
result = rb_ivar_get(self, ID_ivar_result);
|
91
|
-
RAISE_IF_EXCEPTION(result);
|
92
|
-
return result;
|
93
|
-
}
|
94
|
-
|
95
|
-
VALUE fiber = rb_fiber_current();
|
96
|
-
VALUE waiting_fibers = rb_ivar_get(self, ID_ivar_waiting_fibers);
|
97
|
-
if (waiting_fibers == Qnil) {
|
98
|
-
waiting_fibers = rb_hash_new();
|
99
|
-
rb_ivar_set(self, ID_ivar_waiting_fibers, waiting_fibers);
|
100
|
-
}
|
101
|
-
rb_hash_aset(waiting_fibers, fiber, Qtrue);
|
102
|
-
|
103
|
-
VALUE backend = rb_ivar_get(rb_thread_current(), ID_ivar_backend);
|
104
|
-
result = Backend_wait_event(backend, Qnil);
|
105
|
-
|
106
|
-
rb_hash_delete(waiting_fibers, fiber);
|
107
|
-
RAISE_IF_EXCEPTION(result);
|
108
|
-
RB_GC_GUARD(result);
|
109
|
-
return result;
|
110
|
-
}
|
111
|
-
|
112
84
|
VALUE Fiber_send(VALUE self, VALUE value) {
|
113
85
|
VALUE mailbox = rb_ivar_get(self, ID_ivar_mailbox);
|
114
86
|
if (mailbox == Qnil) {
|
@@ -125,7 +97,7 @@ VALUE Fiber_receive(VALUE self) {
|
|
125
97
|
mailbox = rb_funcall(cQueue, ID_new, 0);
|
126
98
|
rb_ivar_set(self, ID_ivar_mailbox, mailbox);
|
127
99
|
}
|
128
|
-
return Queue_shift(mailbox);
|
100
|
+
return Queue_shift(mailbox);
|
129
101
|
}
|
130
102
|
|
131
103
|
VALUE Fiber_mailbox(VALUE self) {
|
@@ -150,9 +122,6 @@ void Init_Fiber() {
|
|
150
122
|
rb_define_method(cFiber, "state", Fiber_state, 0);
|
151
123
|
rb_define_method(cFiber, "auto_watcher", Fiber_auto_watcher, 0);
|
152
124
|
|
153
|
-
rb_define_method(cFiber, "await", Fiber_await, 0);
|
154
|
-
rb_define_method(cFiber, "join", Fiber_await, 0);
|
155
|
-
|
156
125
|
rb_define_method(cFiber, "<<", Fiber_send, 1);
|
157
126
|
rb_define_method(cFiber, "send", Fiber_send, 1);
|
158
127
|
rb_define_method(cFiber, "receive", Fiber_receive, 0);
|
data/ext/polyphony/polyphony.c
CHANGED
@@ -58,20 +58,20 @@ VALUE Polyphony_backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE me
|
|
58
58
|
return Backend_feed_loop(BACKEND(), io, receiver, method);
|
59
59
|
}
|
60
60
|
|
61
|
-
VALUE Polyphony_backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof) {
|
62
|
-
return Backend_read(BACKEND(), io, str, length, to_eof);
|
61
|
+
VALUE Polyphony_backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof, VALUE pos) {
|
62
|
+
return Backend_read(BACKEND(), io, str, length, to_eof, pos);
|
63
63
|
}
|
64
64
|
|
65
|
-
VALUE Polyphony_backend_read_loop(VALUE self, VALUE io) {
|
66
|
-
return Backend_read_loop(BACKEND(), io);
|
65
|
+
VALUE Polyphony_backend_read_loop(VALUE self, VALUE io, VALUE maxlen) {
|
66
|
+
return Backend_read_loop(BACKEND(), io, maxlen);
|
67
67
|
}
|
68
68
|
|
69
|
-
VALUE Polyphony_backend_recv(VALUE self, VALUE io, VALUE str, VALUE length) {
|
70
|
-
return Backend_recv(BACKEND(), io, str, length);
|
69
|
+
VALUE Polyphony_backend_recv(VALUE self, VALUE io, VALUE str, VALUE length, VALUE pos) {
|
70
|
+
return Backend_recv(BACKEND(), io, str, length, pos);
|
71
71
|
}
|
72
72
|
|
73
|
-
VALUE Polyphony_backend_recv_loop(VALUE self, VALUE io) {
|
74
|
-
return Backend_recv_loop(BACKEND(), io);
|
73
|
+
VALUE Polyphony_backend_recv_loop(VALUE self, VALUE io, VALUE maxlen) {
|
74
|
+
return Backend_recv_loop(BACKEND(), io, maxlen);
|
75
75
|
}
|
76
76
|
|
77
77
|
VALUE Polyphony_backend_recv_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
|
@@ -130,10 +130,10 @@ void Init_Polyphony() {
|
|
130
130
|
rb_define_singleton_method(mPolyphony, "backend_accept_loop", Polyphony_backend_accept_loop, 2);
|
131
131
|
rb_define_singleton_method(mPolyphony, "backend_connect", Polyphony_backend_connect, 3);
|
132
132
|
rb_define_singleton_method(mPolyphony, "backend_feed_loop", Polyphony_backend_feed_loop, 3);
|
133
|
-
rb_define_singleton_method(mPolyphony, "backend_read", Polyphony_backend_read,
|
134
|
-
rb_define_singleton_method(mPolyphony, "backend_read_loop", Polyphony_backend_read_loop,
|
135
|
-
rb_define_singleton_method(mPolyphony, "backend_recv", Polyphony_backend_recv,
|
136
|
-
rb_define_singleton_method(mPolyphony, "backend_recv_loop", Polyphony_backend_recv_loop,
|
133
|
+
rb_define_singleton_method(mPolyphony, "backend_read", Polyphony_backend_read, 5);
|
134
|
+
rb_define_singleton_method(mPolyphony, "backend_read_loop", Polyphony_backend_read_loop, 2);
|
135
|
+
rb_define_singleton_method(mPolyphony, "backend_recv", Polyphony_backend_recv, 4);
|
136
|
+
rb_define_singleton_method(mPolyphony, "backend_recv_loop", Polyphony_backend_recv_loop, 2);
|
137
137
|
rb_define_singleton_method(mPolyphony, "backend_recv_feed_loop", Polyphony_backend_recv_feed_loop, 3);
|
138
138
|
rb_define_singleton_method(mPolyphony, "backend_send", Polyphony_backend_send, 3);
|
139
139
|
rb_define_singleton_method(mPolyphony, "backend_sendv", Polyphony_backend_sendv, 3);
|
data/ext/polyphony/polyphony.h
CHANGED
@@ -91,10 +91,10 @@ VALUE Backend_accept(VALUE self, VALUE server_socket, VALUE socket_class);
|
|
91
91
|
VALUE Backend_accept_loop(VALUE self, VALUE server_socket, VALUE socket_class);
|
92
92
|
VALUE Backend_connect(VALUE self, VALUE io, VALUE addr, VALUE port);
|
93
93
|
VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method);
|
94
|
-
VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof);
|
95
|
-
VALUE Backend_read_loop(VALUE self, VALUE io);
|
96
|
-
VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length);
|
97
|
-
VALUE Backend_recv_loop(VALUE self, VALUE io);
|
94
|
+
VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof, VALUE pos);
|
95
|
+
VALUE Backend_read_loop(VALUE self, VALUE io, VALUE maxlen);
|
96
|
+
VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length, VALUE pos);
|
97
|
+
VALUE Backend_recv_loop(VALUE self, VALUE io, VALUE maxlen);
|
98
98
|
VALUE Backend_recv_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method);
|
99
99
|
VALUE Backend_send(VALUE self, VALUE io, VALUE msg, VALUE flags);
|
100
100
|
VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags);
|
data/ext/polyphony/queue.c
CHANGED
@@ -57,21 +57,21 @@ static VALUE Queue_initialize(int argc, VALUE *argv, VALUE self) {
|
|
57
57
|
return self;
|
58
58
|
}
|
59
59
|
|
60
|
-
inline void
|
60
|
+
inline void queue_schedule_first_blocked_fiber(ring_buffer *queue) {
|
61
61
|
if (queue->count) {
|
62
62
|
VALUE fiber = ring_buffer_shift(queue);
|
63
63
|
if (fiber != Qnil) Fiber_make_runnable(fiber, Qnil);
|
64
64
|
}
|
65
65
|
}
|
66
66
|
|
67
|
-
inline void
|
67
|
+
inline void queue_schedule_all_blocked_fibers(ring_buffer *queue) {
|
68
68
|
while (queue->count) {
|
69
69
|
VALUE fiber = ring_buffer_shift(queue);
|
70
70
|
if (fiber != Qnil) Fiber_make_runnable(fiber, Qnil);
|
71
71
|
}
|
72
72
|
}
|
73
73
|
|
74
|
-
inline void
|
74
|
+
inline void queue_schedule_blocked_fibers_to_capacity(Queue_t *queue) {
|
75
75
|
for (unsigned int i = queue->values.count; (i < queue->capacity) && queue->push_queue.count; i++) {
|
76
76
|
VALUE fiber = ring_buffer_shift(&queue->push_queue);
|
77
77
|
if (fiber != Qnil) Fiber_make_runnable(fiber, Qnil);
|
@@ -101,7 +101,7 @@ VALUE Queue_push(VALUE self, VALUE value) {
|
|
101
101
|
|
102
102
|
if (queue->capacity) capped_queue_block_push(queue);
|
103
103
|
|
104
|
-
|
104
|
+
queue_schedule_first_blocked_fiber(&queue->shift_queue);
|
105
105
|
ring_buffer_push(&queue->values, value);
|
106
106
|
|
107
107
|
return self;
|
@@ -113,7 +113,7 @@ VALUE Queue_unshift(VALUE self, VALUE value) {
|
|
113
113
|
|
114
114
|
if (queue->capacity) capped_queue_block_push(queue);
|
115
115
|
|
116
|
-
|
116
|
+
queue_schedule_first_blocked_fiber(&queue->shift_queue);
|
117
117
|
ring_buffer_unshift(&queue->values, value);
|
118
118
|
|
119
119
|
return self;
|
@@ -140,7 +140,7 @@ VALUE Queue_shift(VALUE self) {
|
|
140
140
|
}
|
141
141
|
VALUE value = ring_buffer_shift(&queue->values);
|
142
142
|
if ((queue->capacity) && (queue->capacity > queue->values.count))
|
143
|
-
|
143
|
+
queue_schedule_first_blocked_fiber(&queue->push_queue);
|
144
144
|
RB_GC_GUARD(value);
|
145
145
|
return value;
|
146
146
|
}
|
@@ -152,7 +152,7 @@ VALUE Queue_delete(VALUE self, VALUE value) {
|
|
152
152
|
ring_buffer_delete(&queue->values, value);
|
153
153
|
|
154
154
|
if (queue->capacity && (queue->capacity > queue->values.count))
|
155
|
-
|
155
|
+
queue_schedule_first_blocked_fiber(&queue->push_queue);
|
156
156
|
|
157
157
|
return self;
|
158
158
|
}
|
@@ -164,9 +164,9 @@ VALUE Queue_cap(VALUE self, VALUE cap) {
|
|
164
164
|
queue->capacity = new_capacity;
|
165
165
|
|
166
166
|
if (queue->capacity)
|
167
|
-
|
167
|
+
queue_schedule_blocked_fibers_to_capacity(queue);
|
168
168
|
else
|
169
|
-
|
169
|
+
queue_schedule_all_blocked_fibers(&queue->push_queue);
|
170
170
|
|
171
171
|
return self;
|
172
172
|
}
|
@@ -183,7 +183,7 @@ VALUE Queue_clear(VALUE self) {
|
|
183
183
|
GetQueue(self, queue);
|
184
184
|
|
185
185
|
ring_buffer_clear(&queue->values);
|
186
|
-
if (queue->capacity)
|
186
|
+
if (queue->capacity) queue_schedule_blocked_fibers_to_capacity(queue);
|
187
187
|
|
188
188
|
return self;
|
189
189
|
}
|
@@ -200,7 +200,7 @@ VALUE Queue_shift_each(VALUE self) {
|
|
200
200
|
GetQueue(self, queue);
|
201
201
|
|
202
202
|
ring_buffer_shift_each(&queue->values);
|
203
|
-
if (queue->capacity)
|
203
|
+
if (queue->capacity) queue_schedule_blocked_fibers_to_capacity(queue);
|
204
204
|
return self;
|
205
205
|
}
|
206
206
|
|
@@ -209,7 +209,7 @@ VALUE Queue_shift_all(VALUE self) {
|
|
209
209
|
GetQueue(self, queue);
|
210
210
|
|
211
211
|
VALUE result = ring_buffer_shift_all(&queue->values);
|
212
|
-
if (queue->capacity)
|
212
|
+
if (queue->capacity) queue_schedule_blocked_fibers_to_capacity(queue);
|
213
213
|
return result;
|
214
214
|
}
|
215
215
|
|
data/ext/polyphony/runqueue.c
CHANGED
@@ -58,14 +58,11 @@ inline int runqueue_empty_p(runqueue_t *runqueue) {
|
|
58
58
|
return (runqueue->entries.count == 0);
|
59
59
|
}
|
60
60
|
|
61
|
-
static const unsigned int ANTI_STARVE_HIGH_WATERMARK_THRESHOLD = 128;
|
62
61
|
static const unsigned int ANTI_STARVE_SWITCH_COUNT_THRESHOLD = 64;
|
63
62
|
|
64
63
|
inline int runqueue_should_poll_nonblocking(runqueue_t *runqueue) {
|
65
|
-
if (runqueue->high_watermark < ANTI_STARVE_HIGH_WATERMARK_THRESHOLD) return 0;
|
66
64
|
if (runqueue->switch_count < ANTI_STARVE_SWITCH_COUNT_THRESHOLD) return 0;
|
67
65
|
|
68
|
-
// the
|
69
66
|
runqueue->switch_count = 0;
|
70
67
|
return 1;
|
71
68
|
}
|
@@ -22,9 +22,9 @@ module Polyphony
|
|
22
22
|
return self
|
23
23
|
end
|
24
24
|
|
25
|
-
parent.spin(@tag, @caller, &@block)
|
26
|
-
|
27
|
-
|
25
|
+
fiber = parent.spin(@tag, @caller, &@block)
|
26
|
+
fiber.schedule(value) unless value.nil?
|
27
|
+
fiber
|
28
28
|
end
|
29
29
|
alias_method :reset, :restart
|
30
30
|
|
@@ -66,6 +66,11 @@ module Polyphony
|
|
66
66
|
def interject(&block)
|
67
67
|
raise Polyphony::Interjection.new(block)
|
68
68
|
end
|
69
|
+
|
70
|
+
def await
|
71
|
+
Fiber.await(self).first
|
72
|
+
end
|
73
|
+
alias_method :join, :await
|
69
74
|
end
|
70
75
|
|
71
76
|
# Fiber supervision
|
@@ -78,7 +83,7 @@ module Polyphony
|
|
78
83
|
while true
|
79
84
|
supervise_perform(opts)
|
80
85
|
end
|
81
|
-
|
86
|
+
rescue Polyphony::MoveOn
|
82
87
|
# generated in #supervise_perform to stop supervisor
|
83
88
|
ensure
|
84
89
|
@on_child_done = nil
|
@@ -119,72 +124,62 @@ module Polyphony
|
|
119
124
|
def await(*fibers)
|
120
125
|
return [] if fibers.empty?
|
121
126
|
|
122
|
-
|
123
|
-
|
124
|
-
suspend
|
125
|
-
fibers.map(&:result)
|
126
|
-
ensure
|
127
|
-
await_select_cleanup(state) if state
|
128
|
-
end
|
129
|
-
alias_method :join, :await
|
130
|
-
|
131
|
-
def setup_await_select_state(fibers)
|
132
|
-
{
|
133
|
-
awaiter: Fiber.current,
|
134
|
-
pending: fibers.each_with_object({}) { |f, h| h[f] = true }
|
135
|
-
}
|
136
|
-
end
|
137
|
-
|
138
|
-
def await_setup_monitoring(fibers, state)
|
127
|
+
Fiber.current.message_on_child_termination = true
|
128
|
+
results = {}
|
139
129
|
fibers.each do |f|
|
140
|
-
f
|
130
|
+
results[f] = nil
|
131
|
+
if f.dead?
|
132
|
+
# fiber already terminated, so queue message
|
133
|
+
Fiber.current.send [f, f.result]
|
134
|
+
else
|
135
|
+
f.monitor
|
136
|
+
end
|
141
137
|
end
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
138
|
+
exception = nil
|
139
|
+
while !fibers.empty?
|
140
|
+
(fiber, result) = receive
|
141
|
+
next unless fibers.include?(fiber)
|
142
|
+
|
143
|
+
fibers.delete(fiber)
|
144
|
+
if result.is_a?(Exception)
|
145
|
+
exception ||= result
|
146
|
+
fibers.each { |f| f.terminate }
|
147
|
+
else
|
148
|
+
results[fiber] = result
|
149
|
+
end
|
152
150
|
end
|
153
|
-
|
154
|
-
|
155
|
-
def await_select_cleanup(state)
|
156
|
-
return if state[:pending].empty?
|
157
|
-
|
158
|
-
terminate = Polyphony::Terminate.new
|
159
|
-
state[:cleanup] = true
|
160
|
-
state[:pending].each_key { |f| f.schedule(terminate) }
|
161
|
-
suspend
|
162
|
-
end
|
163
|
-
|
164
|
-
def select(*fibers)
|
165
|
-
state = setup_await_select_state(fibers)
|
166
|
-
select_setup_monitoring(fibers, state)
|
167
|
-
suspend
|
151
|
+
results.values
|
168
152
|
ensure
|
169
|
-
|
153
|
+
Fiber.current.message_on_child_termination = false
|
154
|
+
raise exception if exception
|
170
155
|
end
|
156
|
+
alias_method :join, :await
|
171
157
|
|
172
|
-
def
|
158
|
+
def select(*fibers)
|
159
|
+
return nil if fibers.empty?
|
160
|
+
|
173
161
|
fibers.each do |f|
|
174
|
-
f.
|
162
|
+
if f.dead?
|
163
|
+
result = f.result
|
164
|
+
result.is_a?(Exception) ? (raise result) : (return [f, result])
|
165
|
+
end
|
175
166
|
end
|
176
|
-
end
|
177
167
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
168
|
+
Fiber.current.message_on_child_termination = true
|
169
|
+
fibers.each { |f| f.monitor }
|
170
|
+
while true
|
171
|
+
(fiber, result) = receive
|
172
|
+
next unless fibers.include?(fiber)
|
173
|
+
|
174
|
+
fibers.each { |f| f.unmonitor }
|
175
|
+
if result.is_a?(Exception)
|
176
|
+
raise result
|
177
|
+
else
|
178
|
+
return [fiber, result]
|
179
|
+
end
|
187
180
|
end
|
181
|
+
ensure
|
182
|
+
Fiber.current.message_on_child_termination = false
|
188
183
|
end
|
189
184
|
|
190
185
|
# Creates and schedules with priority an out-of-band fiber that runs the
|
@@ -209,6 +204,14 @@ module Polyphony
|
|
209
204
|
(@children ||= {}).keys
|
210
205
|
end
|
211
206
|
|
207
|
+
def add_child(child_fiber)
|
208
|
+
(@children ||= {})[child_fiber] = true
|
209
|
+
end
|
210
|
+
|
211
|
+
def remove_child(child_fiber)
|
212
|
+
@children.delete(child_fiber) if @children
|
213
|
+
end
|
214
|
+
|
212
215
|
def spin(tag = nil, orig_caller = Kernel.caller, &block)
|
213
216
|
f = Fiber.new { |v| f.run(v) }
|
214
217
|
f.prepare(tag, block, orig_caller, self)
|
@@ -218,7 +221,10 @@ module Polyphony
|
|
218
221
|
|
219
222
|
def child_done(child_fiber, result)
|
220
223
|
@children.delete(child_fiber)
|
221
|
-
|
224
|
+
|
225
|
+
if result.is_a?(Exception) && !@message_on_child_termination
|
226
|
+
schedule_with_priority(result)
|
227
|
+
end
|
222
228
|
end
|
223
229
|
|
224
230
|
def terminate_all_children(graceful = false)
|
@@ -234,14 +240,7 @@ module Polyphony
|
|
234
240
|
def await_all_children
|
235
241
|
return unless @children && !@children.empty?
|
236
242
|
|
237
|
-
|
238
|
-
@on_child_done = proc do |c, r|
|
239
|
-
results[c] = r
|
240
|
-
schedule if @children.empty?
|
241
|
-
end
|
242
|
-
suspend
|
243
|
-
@on_child_done = nil
|
244
|
-
results.values
|
243
|
+
Fiber.await(*@children.keys)
|
245
244
|
end
|
246
245
|
|
247
246
|
def shutdown_all_children(graceful = false)
|
@@ -252,6 +251,18 @@ module Polyphony
|
|
252
251
|
c.await
|
253
252
|
end
|
254
253
|
end
|
254
|
+
|
255
|
+
def detach
|
256
|
+
@parent.remove_child(self)
|
257
|
+
@parent = @thread.main_fiber
|
258
|
+
@parent.add_child(self)
|
259
|
+
end
|
260
|
+
|
261
|
+
def attach(parent)
|
262
|
+
@parent.remove_child(self)
|
263
|
+
@parent = parent
|
264
|
+
@parent.add_child(self)
|
265
|
+
end
|
255
266
|
end
|
256
267
|
|
257
268
|
# Fiber life cycle methods
|
@@ -304,8 +315,6 @@ module Polyphony
|
|
304
315
|
|
305
316
|
def restart_self(first_value)
|
306
317
|
@mailbox = nil
|
307
|
-
@when_done_procs = nil
|
308
|
-
@waiting_fibers = nil
|
309
318
|
run(first_value)
|
310
319
|
end
|
311
320
|
|
@@ -313,8 +322,9 @@ module Polyphony
|
|
313
322
|
result, uncaught_exception = finalize_children(result, uncaught_exception)
|
314
323
|
Thread.backend.trace(:fiber_terminate, self, result)
|
315
324
|
@result = result
|
316
|
-
|
325
|
+
|
317
326
|
inform_dependants(result, uncaught_exception)
|
327
|
+
@running = false
|
318
328
|
ensure
|
319
329
|
# Prevent fiber from being resumed after terminating
|
320
330
|
@thread.fiber_unschedule(self)
|
@@ -332,17 +342,27 @@ module Polyphony
|
|
332
342
|
end
|
333
343
|
|
334
344
|
def inform_dependants(result, uncaught_exception)
|
345
|
+
if @monitors
|
346
|
+
msg = [self, result]
|
347
|
+
@monitors.each { |f| f << msg }
|
348
|
+
end
|
349
|
+
|
335
350
|
@parent&.child_done(self, result)
|
336
|
-
@when_done_procs&.each { |p| p.(result) }
|
337
|
-
@waiting_fibers&.each_key { |f| f.schedule(result) }
|
338
|
-
|
339
|
-
# propagate unaught exception to parent
|
340
|
-
@parent&.schedule_with_priority(result) if uncaught_exception && !@waiting_fibers
|
341
351
|
end
|
342
352
|
|
343
|
-
|
344
|
-
|
345
|
-
|
353
|
+
attr_accessor :message_on_child_termination
|
354
|
+
|
355
|
+
def monitor
|
356
|
+
@monitors ||= []
|
357
|
+
@monitors << Fiber.current
|
358
|
+
end
|
359
|
+
|
360
|
+
def unmonitor
|
361
|
+
@monitors.delete(Fiber.current) if @monitors
|
362
|
+
end
|
363
|
+
|
364
|
+
def dead?
|
365
|
+
state == :dead
|
346
366
|
end
|
347
367
|
end
|
348
368
|
end
|