polyphony 0.34 → 0.41
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/.github/workflows/test.yml +11 -2
- data/.gitignore +2 -2
- data/.rubocop.yml +30 -0
- data/CHANGELOG.md +34 -0
- data/Gemfile +0 -11
- data/Gemfile.lock +11 -10
- data/README.md +2 -1
- data/Rakefile +6 -2
- data/TODO.md +18 -95
- data/docs/_includes/head.html +40 -0
- data/docs/_includes/nav.html +5 -5
- data/docs/api-reference.md +1 -1
- data/docs/api-reference/fiber.md +18 -0
- data/docs/api-reference/gyro-async.md +57 -0
- data/docs/api-reference/gyro-child.md +29 -0
- data/docs/api-reference/gyro-queue.md +44 -0
- data/docs/api-reference/gyro-timer.md +51 -0
- data/docs/api-reference/gyro.md +25 -0
- data/docs/index.md +10 -7
- data/docs/main-concepts/design-principles.md +67 -9
- data/docs/main-concepts/extending.md +1 -1
- data/docs/main-concepts/fiber-scheduling.md +55 -72
- data/examples/core/xx-agent.rb +102 -0
- data/examples/core/xx-fork-cleanup.rb +22 -0
- data/examples/core/xx-sleeping.rb +14 -6
- data/examples/core/xx-timer-gc.rb +17 -0
- data/examples/io/tunnel.rb +48 -0
- data/examples/io/xx-irb.rb +1 -1
- data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +7 -6
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +14 -25
- data/ext/{gyro → polyphony}/extconf.rb +2 -2
- data/ext/polyphony/fiber.c +112 -0
- data/ext/{gyro → polyphony}/libev.c +0 -0
- data/ext/{gyro → polyphony}/libev.h +0 -0
- data/ext/polyphony/libev_agent.c +503 -0
- data/ext/polyphony/libev_queue.c +214 -0
- data/ext/polyphony/polyphony.c +89 -0
- data/ext/{gyro/gyro.h → polyphony/polyphony.h} +49 -59
- data/ext/polyphony/polyphony_ext.c +23 -0
- data/ext/{gyro → polyphony}/socket.c +21 -19
- data/ext/{gyro → polyphony}/thread.c +55 -119
- data/ext/{gyro → polyphony}/tracing.c +1 -1
- data/lib/polyphony.rb +37 -44
- data/lib/polyphony/adapters/fs.rb +1 -4
- data/lib/polyphony/adapters/irb.rb +2 -2
- data/lib/polyphony/adapters/postgres.rb +6 -5
- data/lib/polyphony/adapters/process.rb +27 -23
- data/lib/polyphony/adapters/trace.rb +110 -105
- data/lib/polyphony/core/channel.rb +35 -35
- data/lib/polyphony/core/exceptions.rb +29 -29
- data/lib/polyphony/core/global_api.rb +94 -91
- data/lib/polyphony/core/resource_pool.rb +83 -83
- data/lib/polyphony/core/sync.rb +16 -16
- data/lib/polyphony/core/thread_pool.rb +49 -37
- data/lib/polyphony/core/throttler.rb +30 -23
- data/lib/polyphony/event.rb +27 -0
- data/lib/polyphony/extensions/core.rb +23 -14
- data/lib/polyphony/extensions/fiber.rb +269 -267
- data/lib/polyphony/extensions/io.rb +56 -26
- data/lib/polyphony/extensions/openssl.rb +5 -9
- data/lib/polyphony/extensions/socket.rb +29 -10
- data/lib/polyphony/extensions/thread.rb +19 -12
- data/lib/polyphony/net.rb +64 -60
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +3 -6
- data/test/helper.rb +14 -1
- data/test/stress.rb +17 -12
- data/test/test_agent.rb +77 -0
- data/test/{test_async.rb → test_event.rb} +17 -9
- data/test/test_ext.rb +25 -4
- data/test/test_fiber.rb +23 -14
- data/test/test_global_api.rb +5 -5
- data/test/test_io.rb +46 -24
- data/test/test_queue.rb +74 -0
- data/test/test_signal.rb +3 -40
- data/test/test_socket.rb +33 -0
- data/test/test_thread.rb +38 -16
- data/test/test_thread_pool.rb +3 -3
- data/test/test_throttler.rb +0 -1
- data/test/test_trace.rb +6 -5
- metadata +34 -39
- data/ext/gyro/async.c +0 -158
- data/ext/gyro/child.c +0 -117
- data/ext/gyro/gyro.c +0 -203
- data/ext/gyro/gyro_ext.c +0 -31
- data/ext/gyro/io.c +0 -447
- data/ext/gyro/queue.c +0 -142
- data/ext/gyro/selector.c +0 -183
- data/ext/gyro/signal.c +0 -108
- data/ext/gyro/timer.c +0 -154
- data/test/test_timer.rb +0 -56
@@ -1,4 +1,4 @@
|
|
1
|
-
#include "
|
1
|
+
#include "polyphony.h"
|
2
2
|
#include <sys/socket.h>
|
3
3
|
|
4
4
|
static VALUE cTCPSocket;
|
@@ -71,8 +71,8 @@ static VALUE BasicSocket_send(int argc, VALUE *argv, VALUE sock) {
|
|
71
71
|
ssize_t n;
|
72
72
|
rb_blocking_function_t *func;
|
73
73
|
const char *funcname;
|
74
|
-
VALUE
|
75
|
-
|
74
|
+
VALUE agent = Qnil;
|
75
|
+
|
76
76
|
rb_scan_args(argc, argv, "21", &arg.mesg, &flags, &to);
|
77
77
|
|
78
78
|
StringValue(arg.mesg);
|
@@ -94,10 +94,9 @@ static VALUE BasicSocket_send(int argc, VALUE *argv, VALUE sock) {
|
|
94
94
|
arg.fd = fptr->fd;
|
95
95
|
arg.flags = NUM2INT(flags);
|
96
96
|
while ((n = (ssize_t)func(&arg)) < 0) {
|
97
|
-
if (
|
98
|
-
|
99
|
-
|
100
|
-
Gyro_IO_await(write_watcher);
|
97
|
+
if (NIL_P(agent))
|
98
|
+
agent = rb_ivar_get(rb_thread_current(), ID_ivar_agent);
|
99
|
+
LibevAgent_wait_io(agent, sock, Qtrue);
|
101
100
|
}
|
102
101
|
return SSIZET2NUM(n);
|
103
102
|
}
|
@@ -113,7 +112,7 @@ static VALUE BasicSocket_recv(int argc, VALUE *argv, VALUE sock) {
|
|
113
112
|
rb_io_t *fptr;
|
114
113
|
long n;
|
115
114
|
int shrinkable;
|
116
|
-
VALUE
|
115
|
+
VALUE agent = Qnil;
|
117
116
|
|
118
117
|
|
119
118
|
VALUE str = argc >= 3 ? argv[2] : Qnil;
|
@@ -132,9 +131,9 @@ static VALUE BasicSocket_recv(int argc, VALUE *argv, VALUE sock) {
|
|
132
131
|
if (n < 0) {
|
133
132
|
int e = errno;
|
134
133
|
if (e == EWOULDBLOCK || e == EAGAIN) {
|
135
|
-
if (
|
136
|
-
|
137
|
-
|
134
|
+
if (NIL_P(agent))
|
135
|
+
agent = rb_ivar_get(rb_thread_current(), ID_ivar_agent);
|
136
|
+
LibevAgent_wait_io(agent, sock, Qnil);
|
138
137
|
}
|
139
138
|
else
|
140
139
|
rb_syserr_fail(e, strerror(e));
|
@@ -158,7 +157,7 @@ static VALUE Socket_accept(VALUE sock) {
|
|
158
157
|
int fd;
|
159
158
|
struct sockaddr addr;
|
160
159
|
socklen_t len = (socklen_t)sizeof addr;
|
161
|
-
VALUE
|
160
|
+
VALUE agent = Qnil;
|
162
161
|
|
163
162
|
GetOpenFile(sock, fptr);
|
164
163
|
rb_io_set_nonblock(fptr);
|
@@ -169,9 +168,9 @@ static VALUE Socket_accept(VALUE sock) {
|
|
169
168
|
if (fd < 0) {
|
170
169
|
int e = errno;
|
171
170
|
if (e == EWOULDBLOCK || e == EAGAIN) {
|
172
|
-
if (
|
173
|
-
|
174
|
-
|
171
|
+
if (NIL_P(agent))
|
172
|
+
agent = rb_ivar_get(rb_thread_current(), ID_ivar_agent);
|
173
|
+
LibevAgent_wait_io(agent, sock, Qnil);
|
175
174
|
}
|
176
175
|
else
|
177
176
|
rb_syserr_fail(e, strerror(e));
|
@@ -197,15 +196,18 @@ static VALUE Socket_accept(VALUE sock) {
|
|
197
196
|
}
|
198
197
|
|
199
198
|
void Init_Socket() {
|
199
|
+
VALUE cBasicSocket;
|
200
|
+
VALUE cSocket;
|
201
|
+
|
200
202
|
rb_require("socket");
|
201
|
-
|
203
|
+
cBasicSocket = rb_const_get(rb_cObject, rb_intern("BasicSocket"));
|
202
204
|
|
203
205
|
rb_define_method(cBasicSocket, "send", BasicSocket_send, -1);
|
204
206
|
rb_define_method(cBasicSocket, "recv", BasicSocket_recv, -1);
|
205
207
|
|
206
|
-
|
207
|
-
|
208
|
-
rb_define_method(cSocket, "accept", Socket_accept, 0);
|
208
|
+
cSocket = rb_const_get(rb_cObject, rb_intern("Socket"));
|
209
|
+
|
210
|
+
// rb_define_method(cSocket, "accept", Socket_accept, 0);
|
209
211
|
|
210
212
|
cTCPSocket = rb_const_get(rb_cObject, rb_intern("TCPSocket"));
|
211
213
|
}
|
@@ -1,64 +1,26 @@
|
|
1
|
-
#include "
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
static ID ID_run_queue;
|
17
|
-
// static ID ID_run_queue_head;
|
18
|
-
// static ID ID_run_queue_tail;
|
19
|
-
static ID ID_runnable_next;
|
20
|
-
static ID ID_stop;
|
21
|
-
|
22
|
-
VALUE event_selector_factory_proc(RB_BLOCK_CALL_FUNC_ARGLIST(args, klass)) {
|
23
|
-
return rb_funcall(klass, ID_new, 1, rb_ary_entry(args, 0));
|
24
|
-
}
|
25
|
-
|
26
|
-
static VALUE Thread_event_selector_set_proc(VALUE self, VALUE proc) {
|
27
|
-
if (!rb_obj_is_proc(proc)) {
|
28
|
-
proc = rb_proc_new(event_selector_factory_proc, proc);
|
29
|
-
}
|
30
|
-
rb_ivar_set(self, ID_ivar_event_selector_proc, proc);
|
31
|
-
return self;
|
32
|
-
}
|
33
|
-
|
34
|
-
static VALUE Thread_create_event_selector(VALUE self, VALUE thread) {
|
35
|
-
VALUE selector_proc = rb_ivar_get(self, ID_ivar_event_selector_proc);
|
36
|
-
if (selector_proc == Qnil) {
|
37
|
-
rb_raise(rb_eRuntimeError, "No event_selector_proc defined");
|
38
|
-
}
|
39
|
-
|
40
|
-
return rb_funcall(selector_proc, ID_call, 1, thread);
|
41
|
-
}
|
1
|
+
#include "polyphony.h"
|
2
|
+
|
3
|
+
ID ID_deactivate_all_watchers_post_fork;
|
4
|
+
ID ID_empty;
|
5
|
+
ID ID_fiber_ref_count;
|
6
|
+
ID ID_ivar_agent;
|
7
|
+
ID ID_ivar_join_wait_queue;
|
8
|
+
ID ID_ivar_main_fiber;
|
9
|
+
ID ID_ivar_result;
|
10
|
+
ID ID_ivar_terminated;
|
11
|
+
ID ID_pop;
|
12
|
+
ID ID_push;
|
13
|
+
ID ID_run_queue;
|
14
|
+
ID ID_runnable_next;
|
15
|
+
ID ID_stop;
|
42
16
|
|
43
17
|
static VALUE Thread_setup_fiber_scheduling(VALUE self) {
|
18
|
+
VALUE queue;
|
19
|
+
|
44
20
|
rb_ivar_set(self, ID_ivar_main_fiber, rb_fiber_current());
|
45
21
|
rb_ivar_set(self, ID_fiber_ref_count, INT2NUM(0));
|
46
|
-
|
22
|
+
queue = rb_ary_new();
|
47
23
|
rb_ivar_set(self, ID_run_queue, queue);
|
48
|
-
VALUE selector = rb_funcall(rb_cThread, ID_create_event_selector, 1, self);
|
49
|
-
rb_ivar_set(self, ID_ivar_event_selector, selector);
|
50
|
-
|
51
|
-
return self;
|
52
|
-
}
|
53
|
-
|
54
|
-
static VALUE Thread_stop_event_selector(VALUE self) {
|
55
|
-
VALUE selector = rb_ivar_get(self, ID_ivar_event_selector);
|
56
|
-
if (selector != Qnil) {
|
57
|
-
rb_funcall(selector, ID_stop, 0);
|
58
|
-
}
|
59
|
-
// Nullify the selector in order to prevent running the
|
60
|
-
// selector after the thread is done running.
|
61
|
-
rb_ivar_set(self, ID_ivar_event_selector, Qnil);
|
62
24
|
|
63
25
|
return self;
|
64
26
|
}
|
@@ -90,23 +52,24 @@ static VALUE SYM_scheduled_fibers;
|
|
90
52
|
static VALUE SYM_pending_watchers;
|
91
53
|
|
92
54
|
static VALUE Thread_fiber_scheduling_stats(VALUE self) {
|
55
|
+
VALUE agent = rb_ivar_get(self,ID_ivar_agent);
|
93
56
|
VALUE stats = rb_hash_new();
|
94
57
|
VALUE queue = rb_ivar_get(self, ID_run_queue);
|
95
|
-
|
96
|
-
|
58
|
+
long pending_count;
|
59
|
+
|
97
60
|
long scheduled_count = RARRAY_LEN(queue);
|
98
61
|
rb_hash_aset(stats, SYM_scheduled_fibers, INT2NUM(scheduled_count));
|
99
62
|
|
100
|
-
|
63
|
+
pending_count = LibevAgent_pending_count(agent);
|
101
64
|
rb_hash_aset(stats, SYM_pending_watchers, INT2NUM(pending_count));
|
102
65
|
|
103
66
|
return stats;
|
104
67
|
}
|
105
68
|
|
106
69
|
VALUE Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
|
107
|
-
|
108
|
-
|
109
|
-
|
70
|
+
VALUE queue;
|
71
|
+
|
72
|
+
if (rb_fiber_alive_p(fiber) != Qtrue) return self;
|
110
73
|
|
111
74
|
FIBER_TRACE(3, SYM_fiber_schedule, fiber, value);
|
112
75
|
// if fiber is already scheduled, just set the scheduled value, then return
|
@@ -115,7 +78,7 @@ VALUE Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
|
|
115
78
|
return self;
|
116
79
|
}
|
117
80
|
|
118
|
-
|
81
|
+
queue = rb_ivar_get(self, ID_run_queue);
|
119
82
|
rb_ary_push(queue, fiber);
|
120
83
|
rb_ivar_set(fiber, ID_runnable, Qtrue);
|
121
84
|
|
@@ -125,22 +88,21 @@ VALUE Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
|
|
125
88
|
// event selector. Otherwise it's gonna be stuck waiting for an event to
|
126
89
|
// happen, not knowing that it there's already a fiber ready to run in its
|
127
90
|
// run queue.
|
128
|
-
VALUE
|
129
|
-
|
130
|
-
Gyro_Selector_break_out_of_ev_loop(selector);
|
131
|
-
}
|
91
|
+
VALUE agent = rb_ivar_get(self,ID_ivar_agent);
|
92
|
+
LibevAgent_break(agent);
|
132
93
|
}
|
133
94
|
return self;
|
134
95
|
}
|
135
96
|
|
136
97
|
VALUE Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value) {
|
137
|
-
|
138
|
-
|
139
|
-
|
98
|
+
VALUE queue;
|
99
|
+
|
100
|
+
if (rb_fiber_alive_p(fiber) != Qtrue) return self;
|
101
|
+
|
140
102
|
FIBER_TRACE(3, SYM_fiber_schedule, fiber, value);
|
141
103
|
rb_ivar_set(fiber, ID_runnable_value, value);
|
142
104
|
|
143
|
-
|
105
|
+
queue = rb_ivar_get(self, ID_run_queue);
|
144
106
|
|
145
107
|
// if fiber is already scheduled, remove it from the run queue
|
146
108
|
if (rb_ivar_get(fiber, ID_runnable) != Qnil) {
|
@@ -155,56 +117,49 @@ VALUE Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value)
|
|
155
117
|
if (rb_thread_current() != self) {
|
156
118
|
// if the fiber scheduling is done across threads, we need to make sure the
|
157
119
|
// target thread is woken up in case it is in the middle of running its
|
158
|
-
// event
|
120
|
+
// event loop. Otherwise it's gonna be stuck waiting for an event to
|
159
121
|
// happen, not knowing that it there's already a fiber ready to run in its
|
160
122
|
// run queue.
|
161
|
-
VALUE
|
162
|
-
|
163
|
-
Gyro_Selector_break_out_of_ev_loop(selector);
|
164
|
-
}
|
123
|
+
VALUE agent = rb_ivar_get(self, ID_ivar_agent);
|
124
|
+
LibevAgent_break(agent);
|
165
125
|
}
|
166
126
|
return self;
|
167
127
|
}
|
168
128
|
|
169
129
|
VALUE Thread_switch_fiber(VALUE self) {
|
170
130
|
VALUE current_fiber = rb_fiber_current();
|
131
|
+
VALUE queue = rb_ivar_get(self, ID_run_queue);
|
132
|
+
VALUE next_fiber;
|
133
|
+
VALUE value;
|
134
|
+
VALUE agent = rb_ivar_get(self, ID_ivar_agent);
|
135
|
+
int ref_count;
|
136
|
+
|
171
137
|
if (__tracing_enabled__) {
|
172
138
|
if (rb_ivar_get(current_fiber, ID_ivar_running) != Qfalse) {
|
173
139
|
rb_funcall(rb_cObject, ID_fiber_trace, 2, SYM_fiber_switchpoint, current_fiber);
|
174
140
|
}
|
175
141
|
}
|
176
|
-
VALUE queue = rb_ivar_get(self, ID_run_queue);
|
177
|
-
VALUE selector = rb_ivar_get(self, ID_ivar_event_selector);
|
178
|
-
|
179
|
-
VALUE next_fiber;
|
180
142
|
|
181
143
|
while (1) {
|
144
|
+
ref_count = Thread_fiber_ref_count(self);
|
182
145
|
next_fiber = rb_ary_shift(queue);
|
183
|
-
// if (break_flag != 0) {
|
184
|
-
// return Qnil;
|
185
|
-
// }
|
186
|
-
int ref_count = Thread_fiber_ref_count(self);
|
187
146
|
if (next_fiber != Qnil) {
|
188
147
|
if (ref_count > 0) {
|
189
148
|
// this mechanism prevents event starvation in case the run queue never
|
190
149
|
// empties
|
191
|
-
|
150
|
+
LibevAgent_poll(agent, Qtrue, current_fiber, queue);
|
192
151
|
}
|
193
152
|
break;
|
194
153
|
}
|
195
|
-
if (ref_count == 0)
|
196
|
-
break;
|
197
|
-
}
|
154
|
+
if (ref_count == 0) break;
|
198
155
|
|
199
|
-
|
156
|
+
LibevAgent_poll(agent, Qnil, current_fiber, queue);
|
200
157
|
}
|
201
158
|
|
202
|
-
if (next_fiber == Qnil)
|
203
|
-
return Qnil;
|
204
|
-
}
|
159
|
+
if (next_fiber == Qnil) return Qnil;
|
205
160
|
|
206
161
|
// run next fiber
|
207
|
-
|
162
|
+
value = rb_ivar_get(next_fiber, ID_runnable_value);
|
208
163
|
FIBER_TRACE(3, SYM_fiber_run, next_fiber, value);
|
209
164
|
|
210
165
|
rb_ivar_set(next_fiber, ID_runnable, Qnil);
|
@@ -220,33 +175,23 @@ VALUE Thread_reset_fiber_scheduling(VALUE self) {
|
|
220
175
|
return self;
|
221
176
|
}
|
222
177
|
|
223
|
-
VALUE
|
224
|
-
VALUE
|
225
|
-
Gyro_Selector_post_fork(selector);
|
226
|
-
|
227
|
-
return self;
|
228
|
-
}
|
229
|
-
|
230
|
-
VALUE Fiber_await() {
|
178
|
+
VALUE Polyphony_switchpoint() {
|
179
|
+
VALUE ret;
|
231
180
|
VALUE thread = rb_thread_current();
|
232
181
|
Thread_ref(thread);
|
233
|
-
|
182
|
+
ret = Thread_switch_fiber(thread);
|
234
183
|
Thread_unref(thread);
|
235
184
|
RB_GC_GUARD(ret);
|
236
185
|
return ret;
|
237
186
|
}
|
238
187
|
|
239
|
-
VALUE Thread_current_event_selector() {
|
240
|
-
return rb_ivar_get(rb_thread_current(), ID_ivar_event_selector);
|
241
|
-
}
|
242
|
-
|
243
188
|
VALUE Thread_fiber_break_out_of_ev_loop(VALUE self, VALUE fiber, VALUE resume_obj) {
|
244
|
-
VALUE
|
189
|
+
VALUE agent = rb_ivar_get(self, ID_ivar_agent);
|
245
190
|
if (fiber != Qnil) {
|
246
191
|
Thread_schedule_fiber_with_priority(self, fiber, resume_obj);
|
247
192
|
}
|
248
193
|
|
249
|
-
if (
|
194
|
+
if (LibevAgent_break(agent) == Qnil) {
|
250
195
|
// we're not inside the ev_loop, so we just do a switchpoint
|
251
196
|
Thread_switch_fiber(self);
|
252
197
|
}
|
@@ -255,18 +200,10 @@ VALUE Thread_fiber_break_out_of_ev_loop(VALUE self, VALUE fiber, VALUE resume_ob
|
|
255
200
|
}
|
256
201
|
|
257
202
|
void Init_Thread() {
|
258
|
-
cQueue = rb_const_get(rb_cObject, rb_intern("Queue"));
|
259
|
-
|
260
|
-
rb_define_singleton_method(rb_cThread, "event_selector=", Thread_event_selector_set_proc, 1);
|
261
|
-
rb_define_singleton_method(rb_cThread, "create_event_selector", Thread_create_event_selector, 1);
|
262
|
-
|
263
203
|
rb_define_method(rb_cThread, "fiber_ref", Thread_ref, 0);
|
264
204
|
rb_define_method(rb_cThread, "fiber_unref", Thread_unref, 0);
|
265
205
|
|
266
|
-
rb_define_method(rb_cThread, "post_fork", Thread_post_fork, 0);
|
267
|
-
|
268
206
|
rb_define_method(rb_cThread, "setup_fiber_scheduling", Thread_setup_fiber_scheduling, 0);
|
269
|
-
rb_define_method(rb_cThread, "stop_event_selector", Thread_stop_event_selector, 0);
|
270
207
|
rb_define_method(rb_cThread, "reset_fiber_scheduling", Thread_reset_fiber_scheduling, 0);
|
271
208
|
rb_define_method(rb_cThread, "fiber_scheduling_stats", Thread_fiber_scheduling_stats, 0);
|
272
209
|
rb_define_method(rb_cThread, "break_out_of_ev_loop", Thread_fiber_break_out_of_ev_loop, 2);
|
@@ -276,11 +213,10 @@ void Init_Thread() {
|
|
276
213
|
Thread_schedule_fiber_with_priority, 2);
|
277
214
|
rb_define_method(rb_cThread, "switch_fiber", Thread_switch_fiber, 0);
|
278
215
|
|
279
|
-
|
216
|
+
ID_deactivate_all_watchers_post_fork = rb_intern("deactivate_all_watchers_post_fork");
|
280
217
|
ID_empty = rb_intern("empty?");
|
281
218
|
ID_fiber_ref_count = rb_intern("fiber_ref_count");
|
282
|
-
|
283
|
-
ID_ivar_event_selector_proc = rb_intern("@event_selector_proc");
|
219
|
+
ID_ivar_agent = rb_intern("@agent");
|
284
220
|
ID_ivar_join_wait_queue = rb_intern("@join_wait_queue");
|
285
221
|
ID_ivar_main_fiber = rb_intern("@main_fiber");
|
286
222
|
ID_ivar_result = rb_intern("@result");
|
data/lib/polyphony.rb
CHANGED
@@ -1,50 +1,35 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'fiber'
|
4
|
+
require_relative './polyphony_ext'
|
4
5
|
|
5
|
-
|
6
|
+
module Polyphony
|
7
|
+
# Map Queue to Libev queue implementation
|
8
|
+
Queue = LibevQueue
|
9
|
+
end
|
6
10
|
|
7
|
-
|
8
|
-
require_relative './
|
11
|
+
require_relative './polyphony/extensions/core'
|
12
|
+
require_relative './polyphony/extensions/thread'
|
13
|
+
require_relative './polyphony/extensions/fiber'
|
14
|
+
require_relative './polyphony/extensions/io'
|
9
15
|
|
10
|
-
Thread.event_selector = Gyro::Selector
|
11
16
|
Thread.current.setup_fiber_scheduling
|
17
|
+
Thread.current.agent = Polyphony::LibevAgent.new
|
12
18
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
19
|
+
require_relative './polyphony/core/global_api'
|
20
|
+
require_relative './polyphony/core/resource_pool'
|
21
|
+
require_relative './polyphony/net'
|
22
|
+
require_relative './polyphony/adapters/process'
|
23
|
+
require_relative './polyphony/event'
|
17
24
|
|
18
25
|
# Main Polyphony API
|
19
26
|
module Polyphony
|
20
|
-
GlobalAPI = import './polyphony/core/global_api'
|
21
|
-
::Object.include GlobalAPI
|
22
|
-
|
23
|
-
exceptions = import './polyphony/core/exceptions'
|
24
|
-
Cancel = exceptions::Cancel
|
25
|
-
MoveOn = exceptions::MoveOn
|
26
|
-
Restart = exceptions::Restart
|
27
|
-
Terminate = exceptions::Terminate
|
28
|
-
|
29
|
-
Net = import './polyphony/net'
|
30
|
-
|
31
|
-
auto_import(
|
32
|
-
Channel: './polyphony/core/channel',
|
33
|
-
FS: './polyphony/adapters/fs',
|
34
|
-
Process: './polyphony/adapters/process',
|
35
|
-
ResourcePool: './polyphony/core/resource_pool',
|
36
|
-
Sync: './polyphony/core/sync',
|
37
|
-
ThreadPool: './polyphony/core/thread_pool',
|
38
|
-
Throttler: './polyphony/core/throttler',
|
39
|
-
Trace: './polyphony/adapters/trace'
|
40
|
-
)
|
41
|
-
|
42
27
|
class << self
|
43
28
|
def wait_for_signal(sig)
|
44
29
|
fiber = Fiber.current
|
45
|
-
|
30
|
+
Polyphony.ref
|
46
31
|
old_trap = trap(sig) do
|
47
|
-
|
32
|
+
Polyphony.unref
|
48
33
|
fiber.schedule(sig)
|
49
34
|
trap(sig, old_trap)
|
50
35
|
end
|
@@ -53,10 +38,10 @@ module Polyphony
|
|
53
38
|
|
54
39
|
def fork(&block)
|
55
40
|
Kernel.fork do
|
56
|
-
# Since the fiber doing the fork will become the main fiber of the
|
57
|
-
# forked process, we leave it behind by transferring to a new fiber
|
58
|
-
# created in the context of the forked process, which rescues *all*
|
59
|
-
# exceptions, including Interrupt and SystemExit.
|
41
|
+
# # Since the fiber doing the fork will become the main fiber of the
|
42
|
+
# # forked process, we leave it behind by transferring to a new fiber
|
43
|
+
# # created in the context of the forked process, which rescues *all*
|
44
|
+
# # exceptions, including Interrupt and SystemExit.
|
60
45
|
spin_forked_block(&block).transfer
|
61
46
|
end
|
62
47
|
end
|
@@ -64,12 +49,13 @@ module Polyphony
|
|
64
49
|
def spin_forked_block(&block)
|
65
50
|
Fiber.new do
|
66
51
|
run_forked_block(&block)
|
67
|
-
|
68
|
-
|
69
|
-
exit_forked_process
|
52
|
+
rescue SystemExit
|
53
|
+
# fall through to ensure
|
70
54
|
rescue Exception => e
|
71
55
|
e.full_message
|
72
56
|
exit!
|
57
|
+
ensure
|
58
|
+
exit_forked_process
|
73
59
|
end
|
74
60
|
end
|
75
61
|
|
@@ -81,9 +67,9 @@ module Polyphony
|
|
81
67
|
trap('SIGTERM', 'DEFAULT')
|
82
68
|
trap('SIGINT', 'DEFAULT')
|
83
69
|
|
84
|
-
Thread.current.post_fork
|
85
70
|
Thread.current.setup
|
86
71
|
Fiber.current.setup_main_fiber
|
72
|
+
Thread.current.agent.post_fork
|
87
73
|
|
88
74
|
install_terminating_signal_handlers
|
89
75
|
|
@@ -91,14 +77,13 @@ module Polyphony
|
|
91
77
|
end
|
92
78
|
|
93
79
|
def exit_forked_process
|
80
|
+
terminate_threads
|
94
81
|
Fiber.current.shutdown_all_children
|
82
|
+
|
95
83
|
# Since fork could be called from any fiber, we explicitly call exit here.
|
96
84
|
# Otherwise, the fiber might want to pass execution to another fiber that
|
97
85
|
# previously transferred execution to the forking fiber, but doesn't exist
|
98
86
|
# anymore...
|
99
|
-
#
|
100
|
-
# The call to exit will invoke the at_exit handler we use to terminate the
|
101
|
-
# (forked) main fiber's child fibers.
|
102
87
|
exit
|
103
88
|
end
|
104
89
|
|
@@ -118,6 +103,14 @@ module Polyphony
|
|
118
103
|
install_terminating_signal_handler('SIGTERM', ::SystemExit)
|
119
104
|
install_terminating_signal_handler('SIGINT', ::Interrupt)
|
120
105
|
end
|
106
|
+
|
107
|
+
def terminate_threads
|
108
|
+
threads = Thread.list - [Thread.current]
|
109
|
+
return if threads.empty?
|
110
|
+
|
111
|
+
threads.each(&:kill)
|
112
|
+
threads.each(&:join)
|
113
|
+
end
|
121
114
|
end
|
122
115
|
end
|
123
116
|
|