polyphony 0.58 → 0.61
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -0
- data/Gemfile.lock +15 -29
- data/examples/core/message_based_supervision.rb +51 -0
- data/examples/io/echo_server.rb +16 -7
- data/ext/polyphony/backend_common.c +160 -7
- data/ext/polyphony/backend_common.h +34 -2
- data/ext/polyphony/backend_io_uring.c +119 -40
- 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 +109 -31
- data/ext/polyphony/extconf.rb +2 -2
- data/ext/polyphony/fiber.c +1 -34
- data/ext/polyphony/polyphony.c +12 -19
- data/ext/polyphony/polyphony.h +9 -20
- data/ext/polyphony/polyphony_ext.c +0 -4
- data/ext/polyphony/queue.c +12 -12
- data/ext/polyphony/runqueue.c +21 -98
- data/ext/polyphony/runqueue.h +26 -0
- data/ext/polyphony/thread.c +6 -113
- data/lib/polyphony/core/timer.rb +2 -2
- data/lib/polyphony/extensions/fiber.rb +102 -82
- 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/extensions/thread.rb +1 -1
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +0 -7
- data/test/test_backend.rb +46 -9
- 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.rb +5 -11
- data/test/test_thread_pool.rb +1 -1
- data/test/test_trace.rb +28 -49
- metadata +5 -109
- data/ext/polyphony/tracing.c +0 -11
- data/lib/polyphony/adapters/trace.rb +0 -138
@@ -0,0 +1,26 @@
|
|
1
|
+
#ifndef RUNQUEUE_H
|
2
|
+
#define RUNQUEUE_H
|
3
|
+
|
4
|
+
#include "polyphony.h"
|
5
|
+
#include "runqueue_ring_buffer.h"
|
6
|
+
|
7
|
+
typedef struct runqueue {
|
8
|
+
runqueue_ring_buffer entries;
|
9
|
+
unsigned int high_watermark;
|
10
|
+
} runqueue_t;
|
11
|
+
|
12
|
+
void runqueue_initialize(runqueue_t *runqueue);
|
13
|
+
void runqueue_finalize(runqueue_t *runqueue);
|
14
|
+
void runqueue_mark(runqueue_t *runqueue);
|
15
|
+
|
16
|
+
void runqueue_push(runqueue_t *runqueue, VALUE fiber, VALUE value, int reschedule);
|
17
|
+
void runqueue_unshift(runqueue_t *runqueue, VALUE fiber, VALUE value, int reschedule);
|
18
|
+
runqueue_entry runqueue_shift(runqueue_t *runqueue);
|
19
|
+
void runqueue_delete(runqueue_t *runqueue, VALUE fiber);
|
20
|
+
int runqueue_index_of(runqueue_t *runqueue, VALUE fiber);
|
21
|
+
void runqueue_clear(runqueue_t *runqueue);
|
22
|
+
long runqueue_len(runqueue_t *runqueue);
|
23
|
+
long runqueue_max_len(runqueue_t *runqueue);
|
24
|
+
int runqueue_empty_p(runqueue_t *runqueue);
|
25
|
+
|
26
|
+
#endif /* RUNQUEUE_H */
|
data/ext/polyphony/thread.c
CHANGED
@@ -1,73 +1,24 @@
|
|
1
1
|
#include "polyphony.h"
|
2
|
+
#include "backend_common.h"
|
2
3
|
|
3
4
|
ID ID_deactivate_all_watchers_post_fork;
|
4
5
|
ID ID_ivar_backend;
|
5
6
|
ID ID_ivar_join_wait_queue;
|
6
7
|
ID ID_ivar_main_fiber;
|
7
8
|
ID ID_ivar_terminated;
|
8
|
-
ID ID_ivar_runqueue;
|
9
9
|
ID ID_stop;
|
10
10
|
|
11
11
|
static VALUE Thread_setup_fiber_scheduling(VALUE self) {
|
12
|
-
VALUE runqueue = rb_funcall(cRunqueue, ID_new, 0);
|
13
|
-
|
14
12
|
rb_ivar_set(self, ID_ivar_main_fiber, rb_fiber_current());
|
15
|
-
rb_ivar_set(self, ID_ivar_runqueue, runqueue);
|
16
|
-
|
17
13
|
return self;
|
18
14
|
}
|
19
15
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
static VALUE Thread_fiber_scheduling_stats(VALUE self) {
|
24
|
-
VALUE backend = rb_ivar_get(self, ID_ivar_backend);
|
25
|
-
VALUE stats = rb_hash_new();
|
26
|
-
VALUE runqueue = rb_ivar_get(self, ID_ivar_runqueue);
|
27
|
-
long pending_count;
|
28
|
-
|
29
|
-
long scheduled_count = Runqueue_len(runqueue);
|
30
|
-
rb_hash_aset(stats, SYM_scheduled_fibers, INT2NUM(scheduled_count));
|
31
|
-
|
32
|
-
pending_count = Backend_pending_count(backend);
|
33
|
-
rb_hash_aset(stats, SYM_pending_watchers, INT2NUM(pending_count));
|
34
|
-
|
35
|
-
return stats;
|
36
|
-
}
|
37
|
-
|
38
|
-
void schedule_fiber(VALUE self, VALUE fiber, VALUE value, int prioritize) {
|
39
|
-
VALUE runqueue;
|
40
|
-
int already_runnable;
|
41
|
-
|
42
|
-
if (rb_fiber_alive_p(fiber) != Qtrue) return;
|
43
|
-
already_runnable = rb_ivar_get(fiber, ID_ivar_runnable) != Qnil;
|
44
|
-
|
45
|
-
COND_TRACE(3, SYM_fiber_schedule, fiber, value);
|
46
|
-
runqueue = rb_ivar_get(self, ID_ivar_runqueue);
|
47
|
-
(prioritize ? Runqueue_unshift : Runqueue_push)(runqueue, fiber, value, already_runnable);
|
48
|
-
if (!already_runnable) {
|
49
|
-
rb_ivar_set(fiber, ID_ivar_runnable, Qtrue);
|
50
|
-
if (rb_thread_current() != self) {
|
51
|
-
// If the fiber scheduling is done across threads, we need to make sure the
|
52
|
-
// target thread is woken up in case it is in the middle of running its
|
53
|
-
// event selector. Otherwise it's gonna be stuck waiting for an event to
|
54
|
-
// happen, not knowing that it there's already a fiber ready to run in its
|
55
|
-
// run queue.
|
56
|
-
VALUE backend = rb_ivar_get(self, ID_ivar_backend);
|
57
|
-
Backend_wakeup(backend);
|
58
|
-
}
|
59
|
-
}
|
60
|
-
}
|
61
|
-
|
62
|
-
VALUE Thread_fiber_scheduling_index(VALUE self, VALUE fiber) {
|
63
|
-
VALUE runqueue = rb_ivar_get(self, ID_ivar_runqueue);
|
64
|
-
|
65
|
-
return INT2NUM(Runqueue_index_of(runqueue, fiber));
|
16
|
+
inline void schedule_fiber(VALUE self, VALUE fiber, VALUE value, int prioritize) {
|
17
|
+
Backend_schedule_fiber(self, rb_ivar_get(self, ID_ivar_backend), fiber, value, prioritize);
|
66
18
|
}
|
67
19
|
|
68
20
|
VALUE Thread_fiber_unschedule(VALUE self, VALUE fiber) {
|
69
|
-
|
70
|
-
Runqueue_delete(runqueue, fiber);
|
21
|
+
Backend_unschedule_fiber(rb_ivar_get(self, ID_ivar_backend), fiber);
|
71
22
|
return self;
|
72
23
|
}
|
73
24
|
|
@@ -82,65 +33,15 @@ VALUE Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value)
|
|
82
33
|
}
|
83
34
|
|
84
35
|
VALUE Thread_switch_fiber(VALUE self) {
|
85
|
-
|
86
|
-
VALUE runqueue = rb_ivar_get(self, ID_ivar_runqueue);
|
87
|
-
runqueue_entry next;
|
88
|
-
VALUE backend = rb_ivar_get(self, ID_ivar_backend);
|
89
|
-
unsigned int pending_ops_count = Backend_pending_count(backend);
|
90
|
-
unsigned int backend_was_polled = 0;
|
91
|
-
unsigned int idle_tasks_run_count = 0;
|
92
|
-
|
93
|
-
if (__tracing_enabled__ && (rb_ivar_get(current_fiber, ID_ivar_running) != Qfalse))
|
94
|
-
TRACE(2, SYM_fiber_switchpoint, current_fiber);
|
95
|
-
|
96
|
-
while (1) {
|
97
|
-
next = Runqueue_shift(runqueue);
|
98
|
-
if (next.fiber != Qnil) {
|
99
|
-
// Polling for I/O op completion is normally done when the run queue is
|
100
|
-
// empty, but if the runqueue never empties, we'll never get to process
|
101
|
-
// any event completions. In order to prevent this, an anti-starve
|
102
|
-
// mechanism is employed, under the following conditions:
|
103
|
-
// - a blocking poll was not yet performed
|
104
|
-
// - there are pending blocking operations
|
105
|
-
// - the runqueue has signalled that a non-blocking poll should be
|
106
|
-
// performed
|
107
|
-
// - the run queue length high watermark has reached its threshold (currently 128)
|
108
|
-
// - the run queue switch counter has reached its threshold (currently 64)
|
109
|
-
if (!backend_was_polled && pending_ops_count && Runqueue_should_poll_nonblocking(runqueue)) {
|
110
|
-
// this prevents event starvation in case the run queue never empties
|
111
|
-
Backend_poll(backend, Qtrue, current_fiber, runqueue);
|
112
|
-
}
|
113
|
-
break;
|
114
|
-
}
|
115
|
-
|
116
|
-
if (!idle_tasks_run_count) {
|
117
|
-
idle_tasks_run_count++;
|
118
|
-
Backend_run_idle_tasks(backend);
|
119
|
-
}
|
120
|
-
if (pending_ops_count == 0) break;
|
121
|
-
Backend_poll(backend, Qnil, current_fiber, runqueue);
|
122
|
-
backend_was_polled = 1;
|
123
|
-
}
|
124
|
-
|
125
|
-
if (next.fiber == Qnil) return Qnil;
|
126
|
-
|
127
|
-
// run next fiber
|
128
|
-
COND_TRACE(3, SYM_fiber_run, next.fiber, next.value);
|
129
|
-
|
130
|
-
rb_ivar_set(next.fiber, ID_ivar_runnable, Qnil);
|
131
|
-
RB_GC_GUARD(next.fiber);
|
132
|
-
RB_GC_GUARD(next.value);
|
133
|
-
return (next.fiber == current_fiber) ?
|
134
|
-
next.value : FIBER_TRANSFER(next.fiber, next.value);
|
36
|
+
return Backend_switch_fiber(rb_ivar_get(self, ID_ivar_backend));
|
135
37
|
}
|
136
38
|
|
137
39
|
VALUE Thread_fiber_schedule_and_wakeup(VALUE self, VALUE fiber, VALUE resume_obj) {
|
138
|
-
VALUE backend = rb_ivar_get(self, ID_ivar_backend);
|
139
40
|
if (fiber != Qnil) {
|
140
41
|
Thread_schedule_fiber_with_priority(self, fiber, resume_obj);
|
141
42
|
}
|
142
43
|
|
143
|
-
if (Backend_wakeup(
|
44
|
+
if (Backend_wakeup(rb_ivar_get(self, ID_ivar_backend)) == Qnil) {
|
144
45
|
// we're not inside the ev_loop, so we just do a switchpoint
|
145
46
|
Thread_switch_fiber(self);
|
146
47
|
}
|
@@ -159,14 +60,12 @@ VALUE Thread_class_backend(VALUE _self) {
|
|
159
60
|
|
160
61
|
void Init_Thread() {
|
161
62
|
rb_define_method(rb_cThread, "setup_fiber_scheduling", Thread_setup_fiber_scheduling, 0);
|
162
|
-
rb_define_method(rb_cThread, "fiber_scheduling_stats", Thread_fiber_scheduling_stats, 0);
|
163
63
|
rb_define_method(rb_cThread, "schedule_and_wakeup", Thread_fiber_schedule_and_wakeup, 2);
|
164
64
|
|
165
65
|
rb_define_method(rb_cThread, "schedule_fiber", Thread_schedule_fiber, 2);
|
166
66
|
rb_define_method(rb_cThread, "schedule_fiber_with_priority",
|
167
67
|
Thread_schedule_fiber_with_priority, 2);
|
168
68
|
rb_define_method(rb_cThread, "switch_fiber", Thread_switch_fiber, 0);
|
169
|
-
rb_define_method(rb_cThread, "fiber_scheduling_index", Thread_fiber_scheduling_index, 1);
|
170
69
|
rb_define_method(rb_cThread, "fiber_unschedule", Thread_fiber_unschedule, 1);
|
171
70
|
|
172
71
|
rb_define_singleton_method(rb_cThread, "backend", Thread_class_backend, 0);
|
@@ -178,11 +77,5 @@ void Init_Thread() {
|
|
178
77
|
ID_ivar_join_wait_queue = rb_intern("@join_wait_queue");
|
179
78
|
ID_ivar_main_fiber = rb_intern("@main_fiber");
|
180
79
|
ID_ivar_terminated = rb_intern("@terminated");
|
181
|
-
ID_ivar_runqueue = rb_intern("@runqueue");
|
182
80
|
ID_stop = rb_intern("stop");
|
183
|
-
|
184
|
-
SYM_scheduled_fibers = ID2SYM(rb_intern("scheduled_fibers"));
|
185
|
-
SYM_pending_watchers = ID2SYM(rb_intern("pending_watchers"));
|
186
|
-
rb_global_variable(&SYM_scheduled_fibers);
|
187
|
-
rb_global_variable(&SYM_pending_watchers);
|
188
81
|
}
|
data/lib/polyphony/core/timer.rb
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
module Polyphony
|
4
4
|
# Implements a common timer for running multiple timeouts
|
5
5
|
class Timer
|
6
|
-
def initialize(resolution:)
|
7
|
-
@fiber = spin_loop(interval: resolution) { update }
|
6
|
+
def initialize(tag = nil, resolution:)
|
7
|
+
@fiber = spin_loop(tag, interval: resolution) { update }
|
8
8
|
@timeouts = {}
|
9
9
|
end
|
10
10
|
|
@@ -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
|
@@ -262,7 +273,7 @@ module Polyphony
|
|
262
273
|
@parent = parent
|
263
274
|
@caller = caller
|
264
275
|
@block = block
|
265
|
-
|
276
|
+
Thread.backend.trace(:fiber_create, self)
|
266
277
|
schedule
|
267
278
|
end
|
268
279
|
|
@@ -304,17 +315,16 @@ 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
|
|
312
321
|
def finalize(result, uncaught_exception = false)
|
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
|