polyphony 0.43.8 → 0.45.0
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/.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
|