polyphony 0.34 → 0.41
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|