polyphony 0.43.8 → 0.45.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +7 -1
- data/CHANGELOG.md +38 -0
- data/Gemfile.lock +13 -11
- data/README.md +20 -5
- data/Rakefile +1 -1
- data/TODO.md +16 -14
- data/bin/stress.rb +28 -0
- data/docs/_posts/2020-07-26-polyphony-0.44.md +77 -0
- data/docs/api-reference/thread.md +1 -1
- data/docs/getting-started/overview.md +14 -14
- data/docs/getting-started/tutorial.md +1 -1
- data/examples/adapters/sequel_mysql.rb +23 -0
- data/examples/adapters/sequel_mysql_pool.rb +33 -0
- data/examples/core/{xx-agent.rb → xx-backend.rb} +5 -5
- data/examples/core/xx-channels.rb +4 -2
- data/examples/core/xx-using-a-mutex.rb +2 -1
- data/examples/io/xx-pry.rb +18 -0
- data/examples/io/xx-rack_server.rb +71 -0
- data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +1 -1
- data/ext/polyphony/backend.h +41 -0
- data/ext/polyphony/event.c +86 -0
- data/ext/polyphony/extconf.rb +1 -1
- data/ext/polyphony/fiber.c +0 -5
- data/ext/polyphony/{libev_agent.c → libev_backend.c} +234 -228
- data/ext/polyphony/polyphony.c +4 -0
- data/ext/polyphony/polyphony.h +16 -16
- data/ext/polyphony/polyphony_ext.c +4 -2
- data/ext/polyphony/queue.c +52 -12
- data/ext/polyphony/thread.c +55 -42
- data/lib/polyphony.rb +25 -39
- data/lib/polyphony/adapters/irb.rb +2 -17
- data/lib/polyphony/adapters/mysql2.rb +19 -0
- data/lib/polyphony/adapters/postgres.rb +5 -5
- data/lib/polyphony/adapters/process.rb +2 -2
- data/lib/polyphony/adapters/readline.rb +17 -0
- data/lib/polyphony/adapters/sequel.rb +45 -0
- data/lib/polyphony/core/channel.rb +3 -34
- data/lib/polyphony/core/exceptions.rb +11 -0
- data/lib/polyphony/core/global_api.rb +11 -6
- data/lib/polyphony/core/resource_pool.rb +22 -71
- data/lib/polyphony/core/sync.rb +48 -9
- data/lib/polyphony/core/throttler.rb +1 -1
- data/lib/polyphony/extensions/core.rb +37 -19
- data/lib/polyphony/extensions/fiber.rb +5 -1
- data/lib/polyphony/extensions/io.rb +7 -8
- data/lib/polyphony/extensions/openssl.rb +6 -6
- data/lib/polyphony/extensions/socket.rb +12 -22
- data/lib/polyphony/extensions/thread.rb +6 -5
- data/lib/polyphony/net.rb +2 -1
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +6 -3
- data/test/helper.rb +1 -1
- data/test/{test_agent.rb → test_backend.rb} +22 -22
- data/test/test_event.rb +1 -0
- data/test/test_fiber.rb +21 -5
- data/test/test_io.rb +1 -1
- data/test/test_kernel.rb +5 -0
- data/test/test_queue.rb +20 -0
- data/test/test_resource_pool.rb +34 -43
- data/test/test_signal.rb +5 -29
- data/test/test_sync.rb +52 -0
- metadata +74 -30
- data/.gitbook.yaml +0 -4
- data/lib/polyphony/event.rb +0 -17
data/ext/polyphony/polyphony.c
CHANGED
@@ -7,6 +7,7 @@ ID ID_caller;
|
|
7
7
|
ID ID_clear;
|
8
8
|
ID ID_each;
|
9
9
|
ID ID_inspect;
|
10
|
+
ID ID_invoke;
|
10
11
|
ID ID_new;
|
11
12
|
ID ID_raise;
|
12
13
|
ID ID_ivar_running;
|
@@ -21,6 +22,8 @@ ID ID_R;
|
|
21
22
|
ID ID_W;
|
22
23
|
ID ID_RW;
|
23
24
|
|
25
|
+
backend_interface_t backend_interface;
|
26
|
+
|
24
27
|
VALUE Polyphony_snooze(VALUE self) {
|
25
28
|
VALUE ret;
|
26
29
|
VALUE fiber = rb_fiber_current();
|
@@ -58,6 +61,7 @@ void Init_Polyphony() {
|
|
58
61
|
ID_clear = rb_intern("clear");
|
59
62
|
ID_each = rb_intern("each");
|
60
63
|
ID_inspect = rb_intern("inspect");
|
64
|
+
ID_invoke = rb_intern("invoke");
|
61
65
|
ID_ivar_running = rb_intern("@running");
|
62
66
|
ID_ivar_thread = rb_intern("@thread");
|
63
67
|
ID_new = rb_intern("new");
|
data/ext/polyphony/polyphony.h
CHANGED
@@ -4,20 +4,30 @@
|
|
4
4
|
#include "ruby.h"
|
5
5
|
#include "ruby/io.h"
|
6
6
|
#include "libev.h"
|
7
|
+
#include "backend.h"
|
7
8
|
|
8
9
|
// debugging
|
9
10
|
#define OBJ_ID(obj) (NUM2LONG(rb_funcall(obj, rb_intern("object_id"), 0)))
|
10
|
-
#define INSPECT(obj) { VALUE s = rb_funcall(obj, rb_intern("inspect"), 0); printf("%s\n", StringValueCStr(s));}
|
11
|
-
#define
|
12
|
-
|
11
|
+
#define INSPECT(str, obj) { printf(str); VALUE s = rb_funcall(obj, rb_intern("inspect"), 0); printf("%s\n", StringValueCStr(s)); }
|
12
|
+
#define TRACE_CALLER() { VALUE c = rb_funcall(rb_mKernel, rb_intern("caller"), 0); INSPECT("caller: ", c); }
|
13
|
+
|
14
|
+
// tracing
|
15
|
+
#define TRACE(...) rb_funcall(rb_cObject, ID_fiber_trace, __VA_ARGS__)
|
16
|
+
#define COND_TRACE(...) if (__tracing_enabled__) { \
|
17
|
+
TRACE(__VA_ARGS__); \
|
13
18
|
}
|
14
19
|
|
15
20
|
#define TEST_EXCEPTION(ret) (RTEST(rb_obj_is_kind_of(ret, rb_eException)))
|
16
21
|
|
22
|
+
#define RAISE_EXCEPTION(e) rb_funcall(e, ID_invoke, 0);
|
17
23
|
#define TEST_RESUME_EXCEPTION(ret) if (RTEST(rb_obj_is_kind_of(ret, rb_eException))) { \
|
18
|
-
return
|
24
|
+
return RAISE_EXCEPTION(ret); \
|
19
25
|
}
|
20
26
|
|
27
|
+
|
28
|
+
extern backend_interface_t backend_interface;
|
29
|
+
#define __BACKEND__ (backend_interface)
|
30
|
+
|
21
31
|
extern VALUE mPolyphony;
|
22
32
|
extern VALUE cQueue;
|
23
33
|
extern VALUE cEvent;
|
@@ -28,7 +38,8 @@ extern ID ID_clear;
|
|
28
38
|
extern ID ID_each;
|
29
39
|
extern ID ID_fiber_trace;
|
30
40
|
extern ID ID_inspect;
|
31
|
-
extern ID
|
41
|
+
extern ID ID_invoke;
|
42
|
+
extern ID ID_ivar_backend;
|
32
43
|
extern ID ID_ivar_running;
|
33
44
|
extern ID ID_ivar_thread;
|
34
45
|
extern ID ID_new;
|
@@ -65,17 +76,6 @@ enum {
|
|
65
76
|
VALUE Fiber_auto_watcher(VALUE self);
|
66
77
|
void Fiber_make_runnable(VALUE fiber, VALUE value);
|
67
78
|
|
68
|
-
VALUE LibevAgent_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue);
|
69
|
-
VALUE LibevAgent_break(VALUE self);
|
70
|
-
VALUE LibevAgent_pending_count(VALUE self);
|
71
|
-
VALUE LibevAgent_wait_io(VALUE self, VALUE io, VALUE write);
|
72
|
-
|
73
|
-
VALUE LibevAgent_ref(VALUE self);
|
74
|
-
VALUE LibevAgent_unref(VALUE self);
|
75
|
-
int LibevAgent_ref_count(VALUE self);
|
76
|
-
void LibevAgent_reset_ref_count(VALUE self);
|
77
|
-
VALUE LibevAgent_wait_event(VALUE self, VALUE raise);
|
78
|
-
|
79
79
|
VALUE Queue_push(VALUE self, VALUE value);
|
80
80
|
VALUE Queue_unshift(VALUE self, VALUE value);
|
81
81
|
VALUE Queue_shift(VALUE self);
|
@@ -2,8 +2,9 @@
|
|
2
2
|
|
3
3
|
void Init_Fiber();
|
4
4
|
void Init_Polyphony();
|
5
|
-
void
|
5
|
+
void Init_LibevBackend();
|
6
6
|
void Init_Queue();
|
7
|
+
void Init_Event();
|
7
8
|
void Init_Thread();
|
8
9
|
void Init_Tracing();
|
9
10
|
|
@@ -11,8 +12,9 @@ void Init_polyphony_ext() {
|
|
11
12
|
ev_set_allocator(xrealloc);
|
12
13
|
|
13
14
|
Init_Polyphony();
|
14
|
-
|
15
|
+
Init_LibevBackend();
|
15
16
|
Init_Queue();
|
17
|
+
Init_Event();
|
16
18
|
|
17
19
|
Init_Fiber();
|
18
20
|
Init_Thread();
|
data/ext/polyphony/queue.c
CHANGED
@@ -54,6 +54,7 @@ static VALUE Queue_initialize(VALUE self) {
|
|
54
54
|
VALUE Queue_push(VALUE self, VALUE value) {
|
55
55
|
Queue_t *queue;
|
56
56
|
GetQueue(self, queue);
|
57
|
+
|
57
58
|
if (queue->shift_queue.count > 0) {
|
58
59
|
VALUE fiber = ring_buffer_shift(&queue->shift_queue);
|
59
60
|
if (fiber != Qnil) Fiber_make_runnable(fiber, Qnil);
|
@@ -77,21 +78,26 @@ VALUE Queue_shift(VALUE self) {
|
|
77
78
|
Queue_t *queue;
|
78
79
|
GetQueue(self, queue);
|
79
80
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
81
|
+
VALUE fiber = rb_fiber_current();
|
82
|
+
VALUE thread = rb_thread_current();
|
83
|
+
VALUE backend = rb_ivar_get(thread, ID_ivar_backend);
|
84
|
+
|
85
|
+
while (1) {
|
84
86
|
ring_buffer_push(&queue->shift_queue, fiber);
|
85
|
-
|
86
|
-
|
87
|
-
|
87
|
+
if (queue->values.count > 0) Fiber_make_runnable(fiber, Qnil);
|
88
|
+
|
89
|
+
VALUE switchpoint_result = __BACKEND__.wait_event(backend, Qnil);
|
90
|
+
ring_buffer_delete(&queue->shift_queue, fiber);
|
91
|
+
|
92
|
+
if (RTEST(rb_obj_is_kind_of(switchpoint_result, rb_eException)))
|
88
93
|
return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
|
89
|
-
}
|
90
|
-
RB_GC_GUARD(agent);
|
91
94
|
RB_GC_GUARD(switchpoint_result);
|
95
|
+
|
96
|
+
if (queue->values.count > 0)
|
97
|
+
return ring_buffer_shift(&queue->values);
|
92
98
|
}
|
93
99
|
|
94
|
-
return
|
100
|
+
return Qnil;
|
95
101
|
}
|
96
102
|
|
97
103
|
VALUE Queue_shift_no_wait(VALUE self) {
|
@@ -139,6 +145,18 @@ VALUE Queue_shift_all(VALUE self) {
|
|
139
145
|
return ring_buffer_shift_all(&queue->values);
|
140
146
|
}
|
141
147
|
|
148
|
+
VALUE Queue_flush_waiters(VALUE self, VALUE value) {
|
149
|
+
Queue_t *queue;
|
150
|
+
GetQueue(self, queue);
|
151
|
+
|
152
|
+
while(1) {
|
153
|
+
VALUE fiber = ring_buffer_shift(&queue->shift_queue);
|
154
|
+
if (fiber == Qnil) return self;
|
155
|
+
|
156
|
+
Fiber_make_runnable(fiber, value);
|
157
|
+
}
|
158
|
+
}
|
159
|
+
|
142
160
|
VALUE Queue_empty_p(VALUE self) {
|
143
161
|
Queue_t *queue;
|
144
162
|
GetQueue(self, queue);
|
@@ -146,6 +164,27 @@ VALUE Queue_empty_p(VALUE self) {
|
|
146
164
|
return (queue->values.count == 0) ? Qtrue : Qfalse;
|
147
165
|
}
|
148
166
|
|
167
|
+
VALUE Queue_pending_p(VALUE self) {
|
168
|
+
Queue_t *queue;
|
169
|
+
GetQueue(self, queue);
|
170
|
+
|
171
|
+
return (queue->shift_queue.count > 0) ? Qtrue : Qfalse;
|
172
|
+
}
|
173
|
+
|
174
|
+
VALUE Queue_size_m(VALUE self) {
|
175
|
+
Queue_t *queue;
|
176
|
+
GetQueue(self, queue);
|
177
|
+
|
178
|
+
return INT2NUM(queue->values.count);
|
179
|
+
}
|
180
|
+
|
181
|
+
void Queue_trace(VALUE self) {
|
182
|
+
Queue_t *queue;
|
183
|
+
GetQueue(self, queue);
|
184
|
+
|
185
|
+
printf("run queue size: %d count: %d\n", queue->values.size, queue->values.count);
|
186
|
+
}
|
187
|
+
|
149
188
|
void Init_Queue() {
|
150
189
|
cQueue = rb_define_class_under(mPolyphony, "Queue", rb_cData);
|
151
190
|
rb_define_alloc_func(cQueue, Queue_allocate);
|
@@ -162,7 +201,8 @@ void Init_Queue() {
|
|
162
201
|
|
163
202
|
rb_define_method(cQueue, "shift_each", Queue_shift_each, 0);
|
164
203
|
rb_define_method(cQueue, "shift_all", Queue_shift_all, 0);
|
204
|
+
rb_define_method(cQueue, "flush_waiters", Queue_flush_waiters, 1);
|
165
205
|
rb_define_method(cQueue, "empty?", Queue_empty_p, 0);
|
206
|
+
rb_define_method(cQueue, "pending?", Queue_pending_p, 0);
|
207
|
+
rb_define_method(cQueue, "size", Queue_size_m, 0);
|
166
208
|
}
|
167
|
-
|
168
|
-
|
data/ext/polyphony/thread.c
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#include "polyphony.h"
|
2
2
|
|
3
3
|
ID ID_deactivate_all_watchers_post_fork;
|
4
|
-
ID
|
4
|
+
ID ID_ivar_backend;
|
5
5
|
ID ID_ivar_join_wait_queue;
|
6
6
|
ID ID_ivar_main_fiber;
|
7
7
|
ID ID_ivar_result;
|
@@ -20,20 +20,20 @@ static VALUE Thread_setup_fiber_scheduling(VALUE self) {
|
|
20
20
|
}
|
21
21
|
|
22
22
|
int Thread_fiber_ref_count(VALUE self) {
|
23
|
-
VALUE
|
24
|
-
return NUM2INT(
|
23
|
+
VALUE backend = rb_ivar_get(self, ID_ivar_backend);
|
24
|
+
return NUM2INT(__BACKEND__.ref_count(backend));
|
25
25
|
}
|
26
26
|
|
27
27
|
inline void Thread_fiber_reset_ref_count(VALUE self) {
|
28
|
-
VALUE
|
29
|
-
|
28
|
+
VALUE backend = rb_ivar_get(self, ID_ivar_backend);
|
29
|
+
__BACKEND__.reset_ref_count(backend);
|
30
30
|
}
|
31
31
|
|
32
32
|
static VALUE SYM_scheduled_fibers;
|
33
33
|
static VALUE SYM_pending_watchers;
|
34
34
|
|
35
35
|
static VALUE Thread_fiber_scheduling_stats(VALUE self) {
|
36
|
-
VALUE
|
36
|
+
VALUE backend = rb_ivar_get(self,ID_ivar_backend);
|
37
37
|
VALUE stats = rb_hash_new();
|
38
38
|
VALUE queue = rb_ivar_get(self, ID_run_queue);
|
39
39
|
long pending_count;
|
@@ -41,7 +41,7 @@ static VALUE Thread_fiber_scheduling_stats(VALUE self) {
|
|
41
41
|
long scheduled_count = RARRAY_LEN(queue);
|
42
42
|
rb_hash_aset(stats, SYM_scheduled_fibers, INT2NUM(scheduled_count));
|
43
43
|
|
44
|
-
pending_count =
|
44
|
+
pending_count = __BACKEND__.pending_count(backend);
|
45
45
|
rb_hash_aset(stats, SYM_pending_watchers, INT2NUM(pending_count));
|
46
46
|
|
47
47
|
return stats;
|
@@ -52,25 +52,34 @@ VALUE Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
|
|
52
52
|
|
53
53
|
if (rb_fiber_alive_p(fiber) != Qtrue) return self;
|
54
54
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
55
|
+
int already_runnable = rb_ivar_get(fiber, ID_runnable) != Qnil;
|
56
|
+
|
57
|
+
if (already_runnable) {
|
58
|
+
VALUE current_runnable_value = rb_ivar_get(fiber, ID_runnable_value);
|
59
|
+
|
60
|
+
// If the fiber is already runnable and the runnable value is an exception,
|
61
|
+
// we don't update the value, in order to prevent a race condition where
|
62
|
+
// exceptions will be lost (see issue #33)
|
63
|
+
if (TEST_EXCEPTION(current_runnable_value)) return self;
|
60
64
|
}
|
61
65
|
|
62
|
-
|
63
|
-
|
64
|
-
rb_ivar_set(fiber, ID_runnable, Qtrue);
|
66
|
+
rb_ivar_set(fiber, ID_runnable_value, value);
|
67
|
+
COND_TRACE(3, SYM_fiber_schedule, fiber, value);
|
65
68
|
|
66
|
-
if (
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
69
|
+
if (!already_runnable) {
|
70
|
+
queue = rb_ivar_get(self, ID_run_queue);
|
71
|
+
Queue_push(queue, fiber);
|
72
|
+
rb_ivar_set(fiber, ID_runnable, Qtrue);
|
73
|
+
|
74
|
+
if (rb_thread_current() != self) {
|
75
|
+
// If the fiber scheduling is done across threads, we need to make sure the
|
76
|
+
// target thread is woken up in case it is in the middle of running its
|
77
|
+
// event selector. Otherwise it's gonna be stuck waiting for an event to
|
78
|
+
// happen, not knowing that it there's already a fiber ready to run in its
|
79
|
+
// run queue.
|
80
|
+
VALUE backend = rb_ivar_get(self,ID_ivar_backend);
|
81
|
+
__BACKEND__.wakeup(backend);
|
82
|
+
}
|
74
83
|
}
|
75
84
|
return self;
|
76
85
|
}
|
@@ -80,7 +89,7 @@ VALUE Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value)
|
|
80
89
|
|
81
90
|
if (rb_fiber_alive_p(fiber) != Qtrue) return self;
|
82
91
|
|
83
|
-
|
92
|
+
COND_TRACE(3, SYM_fiber_schedule, fiber, value);
|
84
93
|
rb_ivar_set(fiber, ID_runnable_value, value);
|
85
94
|
|
86
95
|
queue = rb_ivar_get(self, ID_run_queue);
|
@@ -101,8 +110,8 @@ VALUE Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value)
|
|
101
110
|
// event loop. Otherwise it's gonna be stuck waiting for an event to
|
102
111
|
// happen, not knowing that it there's already a fiber ready to run in its
|
103
112
|
// run queue.
|
104
|
-
VALUE
|
105
|
-
|
113
|
+
VALUE backend = rb_ivar_get(self, ID_ivar_backend);
|
114
|
+
__BACKEND__.wakeup(backend);
|
106
115
|
}
|
107
116
|
return self;
|
108
117
|
}
|
@@ -112,38 +121,35 @@ VALUE Thread_switch_fiber(VALUE self) {
|
|
112
121
|
VALUE queue = rb_ivar_get(self, ID_run_queue);
|
113
122
|
VALUE next_fiber;
|
114
123
|
VALUE value;
|
115
|
-
VALUE
|
124
|
+
VALUE backend = rb_ivar_get(self, ID_ivar_backend);
|
116
125
|
int ref_count;
|
117
|
-
int
|
126
|
+
int backend_was_polled = 0;
|
118
127
|
|
119
|
-
if (__tracing_enabled__)
|
120
|
-
|
121
|
-
rb_funcall(rb_cObject, ID_fiber_trace, 2, SYM_fiber_switchpoint, current_fiber);
|
122
|
-
}
|
123
|
-
}
|
128
|
+
if (__tracing_enabled__ && (rb_ivar_get(current_fiber, ID_ivar_running) != Qfalse))
|
129
|
+
TRACE(2, SYM_fiber_switchpoint, current_fiber);
|
124
130
|
|
125
|
-
ref_count =
|
131
|
+
ref_count = __BACKEND__.ref_count(backend);
|
126
132
|
while (1) {
|
127
133
|
next_fiber = Queue_shift_no_wait(queue);
|
128
134
|
if (next_fiber != Qnil) {
|
129
|
-
if (
|
135
|
+
if (backend_was_polled == 0 && ref_count > 0) {
|
130
136
|
// this mechanism prevents event starvation in case the run queue never
|
131
137
|
// empties
|
132
|
-
|
138
|
+
__BACKEND__.poll(backend, Qtrue, current_fiber, queue);
|
133
139
|
}
|
134
140
|
break;
|
135
141
|
}
|
136
142
|
if (ref_count == 0) break;
|
137
143
|
|
138
|
-
|
139
|
-
|
144
|
+
__BACKEND__.poll(backend, Qnil, current_fiber, queue);
|
145
|
+
backend_was_polled = 1;
|
140
146
|
}
|
141
147
|
|
142
148
|
if (next_fiber == Qnil) return Qnil;
|
143
149
|
|
144
150
|
// run next fiber
|
145
151
|
value = rb_ivar_get(next_fiber, ID_runnable_value);
|
146
|
-
|
152
|
+
COND_TRACE(3, SYM_fiber_run, next_fiber, value);
|
147
153
|
|
148
154
|
rb_ivar_set(next_fiber, ID_runnable, Qnil);
|
149
155
|
RB_GC_GUARD(next_fiber);
|
@@ -166,12 +172,12 @@ VALUE Thread_reset_fiber_scheduling(VALUE self) {
|
|
166
172
|
}
|
167
173
|
|
168
174
|
VALUE Thread_fiber_break_out_of_ev_loop(VALUE self, VALUE fiber, VALUE resume_obj) {
|
169
|
-
VALUE
|
175
|
+
VALUE backend = rb_ivar_get(self, ID_ivar_backend);
|
170
176
|
if (fiber != Qnil) {
|
171
177
|
Thread_schedule_fiber_with_priority(self, fiber, resume_obj);
|
172
178
|
}
|
173
179
|
|
174
|
-
if (
|
180
|
+
if (__BACKEND__.wakeup(backend) == Qnil) {
|
175
181
|
// we're not inside the ev_loop, so we just do a switchpoint
|
176
182
|
Thread_switch_fiber(self);
|
177
183
|
}
|
@@ -179,6 +185,11 @@ VALUE Thread_fiber_break_out_of_ev_loop(VALUE self, VALUE fiber, VALUE resume_ob
|
|
179
185
|
return self;
|
180
186
|
}
|
181
187
|
|
188
|
+
VALUE Thread_debug(VALUE self) {
|
189
|
+
rb_ivar_set(self, rb_intern("@__debug__"), Qtrue);
|
190
|
+
return self;
|
191
|
+
}
|
192
|
+
|
182
193
|
void Init_Thread() {
|
183
194
|
rb_define_method(rb_cThread, "setup_fiber_scheduling", Thread_setup_fiber_scheduling, 0);
|
184
195
|
rb_define_method(rb_cThread, "reset_fiber_scheduling", Thread_reset_fiber_scheduling, 0);
|
@@ -191,8 +202,10 @@ void Init_Thread() {
|
|
191
202
|
rb_define_method(rb_cThread, "switch_fiber", Thread_switch_fiber, 0);
|
192
203
|
rb_define_method(rb_cThread, "run_queue_trace", Thread_run_queue_trace, 0);
|
193
204
|
|
205
|
+
rb_define_method(rb_cThread, "debug!", Thread_debug, 0);
|
206
|
+
|
194
207
|
ID_deactivate_all_watchers_post_fork = rb_intern("deactivate_all_watchers_post_fork");
|
195
|
-
|
208
|
+
ID_ivar_backend = rb_intern("@backend");
|
196
209
|
ID_ivar_join_wait_queue = rb_intern("@join_wait_queue");
|
197
210
|
ID_ivar_main_fiber = rb_intern("@main_fiber");
|
198
211
|
ID_ivar_result = rb_intern("@result");
|
data/lib/polyphony.rb
CHANGED
@@ -3,51 +3,36 @@
|
|
3
3
|
require 'fiber'
|
4
4
|
require_relative './polyphony_ext'
|
5
5
|
|
6
|
-
module Polyphony
|
7
|
-
# replace core Queue class with our own
|
8
|
-
verbose = $VERBOSE
|
9
|
-
$VERBOSE = nil
|
10
|
-
Object.const_set(:Queue, Polyphony::Queue)
|
11
|
-
$VERBOSE = verbose
|
12
|
-
end
|
13
|
-
|
14
6
|
require_relative './polyphony/extensions/core'
|
15
7
|
require_relative './polyphony/extensions/thread'
|
16
8
|
require_relative './polyphony/extensions/fiber'
|
17
9
|
require_relative './polyphony/extensions/io'
|
18
10
|
|
19
11
|
Thread.current.setup_fiber_scheduling
|
20
|
-
Thread.current.
|
12
|
+
Thread.current.backend = Polyphony::Backend.new
|
21
13
|
|
22
14
|
require_relative './polyphony/core/global_api'
|
23
15
|
require_relative './polyphony/core/resource_pool'
|
16
|
+
require_relative './polyphony/core/sync'
|
24
17
|
require_relative './polyphony/net'
|
25
18
|
require_relative './polyphony/adapters/process'
|
26
|
-
require_relative './polyphony/event'
|
27
19
|
|
28
|
-
#
|
20
|
+
# Polyphony API
|
29
21
|
module Polyphony
|
30
22
|
class << self
|
31
|
-
def wait_for_signal(sig)
|
32
|
-
raise "should be reimplemented"
|
33
|
-
|
34
|
-
fiber = Fiber.current
|
35
|
-
# Polyphony.ref
|
36
|
-
old_trap = trap(sig) do
|
37
|
-
# Polyphony.unref
|
38
|
-
fiber.schedule(sig)
|
39
|
-
trap(sig, old_trap)
|
40
|
-
end
|
41
|
-
suspend
|
42
|
-
|
43
|
-
end
|
44
|
-
|
45
23
|
def fork(&block)
|
46
24
|
Kernel.fork do
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
25
|
+
# A race condition can arise if a TERM or INT signal is received before
|
26
|
+
# the forked process has finished initializing. To prevent this we restore
|
27
|
+
# the default signal handlers, and then reinstall the custom Polyphony
|
28
|
+
# handlers just before running the given block.
|
29
|
+
trap('SIGTERM', 'DEFAULT')
|
30
|
+
trap('SIGINT', 'DEFAULT')
|
31
|
+
|
32
|
+
# Since the fiber doing the fork will become the main fiber of the
|
33
|
+
# forked process, we leave it behind by transferring to a new fiber
|
34
|
+
# created in the context of the forked process, which rescues *all*
|
35
|
+
# exceptions, including Interrupt and SystemExit.
|
51
36
|
spin_forked_block(&block).transfer
|
52
37
|
end
|
53
38
|
end
|
@@ -58,7 +43,7 @@ module Polyphony
|
|
58
43
|
rescue SystemExit
|
59
44
|
# fall through to ensure
|
60
45
|
rescue Exception => e
|
61
|
-
e.full_message
|
46
|
+
warn e.full_message
|
62
47
|
exit!
|
63
48
|
ensure
|
64
49
|
exit_forked_process
|
@@ -66,16 +51,9 @@ module Polyphony
|
|
66
51
|
end
|
67
52
|
|
68
53
|
def run_forked_block(&block)
|
69
|
-
# A race condition can arise if a TERM or INT signal is received before
|
70
|
-
# the forked process has finished initializing. To prevent this we restore
|
71
|
-
# the default signal handlers, and then reinstall the custom Polyphony
|
72
|
-
# handlers just before running the given block.
|
73
|
-
trap('SIGTERM', 'DEFAULT')
|
74
|
-
trap('SIGINT', 'DEFAULT')
|
75
|
-
|
76
54
|
Thread.current.setup
|
77
55
|
Fiber.current.setup_main_fiber
|
78
|
-
Thread.current.
|
56
|
+
Thread.current.backend.post_fork
|
79
57
|
|
80
58
|
install_terminating_signal_handlers
|
81
59
|
|
@@ -124,12 +102,20 @@ module Polyphony
|
|
124
102
|
# processes (see Polyphony.fork).
|
125
103
|
at_exit do
|
126
104
|
next unless @original_pid == ::Process.pid
|
127
|
-
|
105
|
+
|
128
106
|
Polyphony.terminate_threads
|
129
107
|
Fiber.current.shutdown_all_children
|
130
108
|
end
|
131
109
|
end
|
132
110
|
end
|
111
|
+
|
112
|
+
# replace core Queue class with our own
|
113
|
+
verbose = $VERBOSE
|
114
|
+
$VERBOSE = nil
|
115
|
+
Object.const_set(:Queue, Polyphony::Queue)
|
116
|
+
Object.const_set(:Mutex, Polyphony::Mutex)
|
117
|
+
Object.const_set(:ConditionVariable, Polyphony::ConditionVariable)
|
118
|
+
$VERBOSE = verbose
|
133
119
|
end
|
134
120
|
|
135
121
|
Polyphony.install_terminating_signal_handlers
|