polyphony 0.58 → 0.61
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 +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
|