polyphony 0.59.1 → 0.60
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 +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
|