polyphony 0.43.9 → 0.45.1
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 +8 -1
- data/CHANGELOG.md +40 -0
- data/Gemfile.lock +16 -6
- data/Rakefile +1 -1
- data/TODO.md +14 -13
- 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/redis_client.rb +3 -1
- data/examples/adapters/redis_pubsub_perf.rb +11 -8
- data/examples/adapters/sequel_mysql.rb +23 -0
- data/examples/adapters/sequel_mysql_pool.rb +33 -0
- data/examples/adapters/sequel_pg.rb +24 -0
- data/examples/core/{02-awaiting-fibers.rb → await.rb} +0 -0
- data/examples/core/{xx-channels.rb → channels.rb} +0 -0
- data/examples/core/deferring-an-operation.rb +16 -0
- data/examples/core/{xx-erlang-style-genserver.rb → erlang-style-genserver.rb} +16 -9
- data/examples/core/{xx-forking.rb → forking.rb} +1 -1
- data/examples/core/handling-signals.rb +11 -0
- data/examples/core/{03-interrupting.rb → interrupt.rb} +0 -0
- data/examples/core/{xx-pingpong.rb → pingpong.rb} +7 -5
- data/examples/core/{xx-recurrent-timer.rb → recurrent-timer.rb} +1 -1
- data/examples/core/{xx-resource_delegate.rb → resource_delegate.rb} +3 -4
- data/examples/core/{01-spinning-up-fibers.rb → spin.rb} +1 -1
- data/examples/core/{xx-spin_error_backtrace.rb → spin_error_backtrace.rb} +1 -1
- data/examples/core/{xx-supervise-process.rb → supervise-process.rb} +8 -5
- data/examples/core/supervisor.rb +20 -0
- data/examples/core/{xx-thread-sleep.rb → thread-sleep.rb} +0 -0
- data/examples/core/{xx-thread_pool.rb → thread_pool.rb} +0 -0
- data/examples/core/{xx-throttling.rb → throttling.rb} +0 -0
- data/examples/core/{xx-timeout.rb → timeout.rb} +0 -0
- data/examples/core/{xx-using-a-mutex.rb → using-a-mutex.rb} +0 -0
- data/examples/core/{xx-worker-thread.rb → worker-thread.rb} +2 -2
- data/examples/io/{xx-backticks.rb → backticks.rb} +0 -0
- data/examples/io/{xx-echo_client.rb → echo_client.rb} +1 -1
- data/examples/io/{xx-echo_client_from_stdin.rb → echo_client_from_stdin.rb} +2 -2
- data/examples/io/{xx-echo_pipe.rb → echo_pipe.rb} +1 -1
- data/examples/io/{xx-echo_server.rb → echo_server.rb} +0 -0
- data/examples/io/{xx-echo_server_with_timeout.rb → echo_server_with_timeout.rb} +1 -1
- data/examples/io/{xx-echo_stdin.rb → echo_stdin.rb} +0 -0
- data/examples/io/{xx-happy-eyeballs.rb → happy-eyeballs.rb} +0 -0
- data/examples/io/{xx-httparty.rb → httparty.rb} +4 -13
- data/examples/io/{xx-irb.rb → irb.rb} +0 -0
- data/examples/io/{xx-net-http.rb → net-http.rb} +0 -0
- data/examples/io/{xx-open.rb → open.rb} +0 -0
- data/examples/io/pry.rb +18 -0
- data/examples/io/rack_server.rb +71 -0
- data/examples/io/{xx-system.rb → system.rb} +1 -1
- data/examples/io/{xx-tcpserver.rb → tcpserver.rb} +0 -0
- data/examples/io/{xx-tcpsocket.rb → tcpsocket.rb} +0 -0
- data/examples/io/tunnel.rb +6 -1
- data/examples/io/{xx-zip.rb → zip.rb} +0 -0
- data/examples/performance/fiber_transfer.rb +2 -1
- data/examples/performance/fs_read.rb +5 -6
- data/examples/{io/xx-switch.rb → performance/switch.rb} +2 -1
- data/examples/performance/thread-vs-fiber/{xx-httparty_multi.rb → httparty_multi.rb} +3 -4
- data/examples/performance/thread-vs-fiber/{xx-httparty_threaded.rb → httparty_threaded.rb} +0 -0
- data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +1 -1
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +1 -1
- data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +1 -1
- data/examples/performance/thread-vs-fiber/threaded_server.rb +1 -5
- data/examples/performance/thread_pool_perf.rb +6 -7
- data/ext/polyphony/backend.h +40 -0
- data/ext/polyphony/event.c +3 -3
- data/ext/polyphony/extconf.rb +1 -1
- data/ext/polyphony/{libev_agent.c → libev_backend.c} +272 -265
- data/ext/polyphony/polyphony.c +4 -2
- data/ext/polyphony/polyphony.h +15 -28
- data/ext/polyphony/polyphony_ext.c +3 -4
- data/ext/polyphony/queue.c +32 -12
- data/ext/polyphony/ring_buffer.c +0 -1
- data/ext/polyphony/thread.c +58 -44
- data/lib/polyphony.rb +25 -38
- data/lib/polyphony/adapters/fs.rb +1 -1
- 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/redis.rb +1 -1
- data/lib/polyphony/adapters/sequel.rb +45 -0
- data/lib/polyphony/core/exceptions.rb +11 -0
- data/lib/polyphony/core/global_api.rb +15 -10
- data/lib/polyphony/core/resource_pool.rb +20 -7
- data/lib/polyphony/core/sync.rb +46 -8
- data/lib/polyphony/core/throttler.rb +1 -1
- data/lib/polyphony/extensions/core.rb +38 -25
- data/lib/polyphony/extensions/fiber.rb +5 -1
- data/lib/polyphony/extensions/io.rb +45 -12
- data/lib/polyphony/extensions/openssl.rb +6 -6
- data/lib/polyphony/extensions/socket.rb +22 -23
- 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 +7 -3
- data/test/helper.rb +1 -1
- data/test/{test_agent.rb → test_backend.rb} +22 -22
- data/test/test_fiber.rb +21 -5
- data/test/test_io.rb +17 -1
- data/test/test_kernel.rb +5 -0
- data/test/test_resource_pool.rb +50 -16
- data/test/test_signal.rb +5 -29
- data/test/test_socket.rb +17 -0
- data/test/test_sync.rb +52 -0
- data/test/test_throttler.rb +1 -0
- metadata +125 -96
- data/.gitbook.yaml +0 -4
- data/examples/adapters/concurrent-ruby.rb +0 -9
- data/examples/core/04-handling-signals.rb +0 -19
- data/examples/core/xx-agent.rb +0 -102
- data/examples/core/xx-at_exit.rb +0 -29
- data/examples/core/xx-caller.rb +0 -12
- data/examples/core/xx-daemon.rb +0 -14
- data/examples/core/xx-deadlock.rb +0 -8
- data/examples/core/xx-deferring-an-operation.rb +0 -14
- data/examples/core/xx-exception-backtrace.rb +0 -40
- data/examples/core/xx-fork-cleanup.rb +0 -22
- data/examples/core/xx-fork-spin.rb +0 -42
- data/examples/core/xx-fork-terminate.rb +0 -27
- data/examples/core/xx-move_on.rb +0 -23
- data/examples/core/xx-queue-async.rb +0 -120
- data/examples/core/xx-readpartial.rb +0 -18
- data/examples/core/xx-signals.rb +0 -16
- data/examples/core/xx-sleep-forever.rb +0 -9
- data/examples/core/xx-sleeping.rb +0 -25
- data/examples/core/xx-snooze-starve.rb +0 -16
- data/examples/core/xx-spin-fork.rb +0 -49
- data/examples/core/xx-state-machine.rb +0 -51
- data/examples/core/xx-stop.rb +0 -20
- data/examples/core/xx-supervisors.rb +0 -21
- data/examples/core/xx-thread-selector-sleep.rb +0 -51
- data/examples/core/xx-thread-selector-snooze.rb +0 -46
- data/examples/core/xx-thread-snooze.rb +0 -34
- data/examples/core/xx-timer-gc.rb +0 -17
- data/examples/core/xx-trace.rb +0 -79
- data/examples/performance/xx-array.rb +0 -11
- data/examples/performance/xx-fiber-switch.rb +0 -9
- data/examples/performance/xx-snooze.rb +0 -15
- data/examples/xx-spin.rb +0 -32
data/ext/polyphony/polyphony.c
CHANGED
|
@@ -7,8 +7,8 @@ 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
|
-
ID ID_raise;
|
|
12
12
|
ID ID_ivar_running;
|
|
13
13
|
ID ID_ivar_thread;
|
|
14
14
|
ID ID_runnable;
|
|
@@ -21,6 +21,8 @@ ID ID_R;
|
|
|
21
21
|
ID ID_W;
|
|
22
22
|
ID ID_RW;
|
|
23
23
|
|
|
24
|
+
backend_interface_t backend_interface;
|
|
25
|
+
|
|
24
26
|
VALUE Polyphony_snooze(VALUE self) {
|
|
25
27
|
VALUE ret;
|
|
26
28
|
VALUE fiber = rb_fiber_current();
|
|
@@ -58,10 +60,10 @@ void Init_Polyphony() {
|
|
|
58
60
|
ID_clear = rb_intern("clear");
|
|
59
61
|
ID_each = rb_intern("each");
|
|
60
62
|
ID_inspect = rb_intern("inspect");
|
|
63
|
+
ID_invoke = rb_intern("invoke");
|
|
61
64
|
ID_ivar_running = rb_intern("@running");
|
|
62
65
|
ID_ivar_thread = rb_intern("@thread");
|
|
63
66
|
ID_new = rb_intern("new");
|
|
64
|
-
ID_raise = rb_intern("raise");
|
|
65
67
|
ID_runnable = rb_intern("runnable");
|
|
66
68
|
ID_runnable_value = rb_intern("runnable_value");
|
|
67
69
|
ID_signal = rb_intern("signal");
|
data/ext/polyphony/polyphony.h
CHANGED
|
@@ -4,20 +4,29 @@
|
|
|
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
|
+
extern backend_interface_t backend_interface;
|
|
28
|
+
#define __BACKEND__ (backend_interface)
|
|
29
|
+
|
|
21
30
|
extern VALUE mPolyphony;
|
|
22
31
|
extern VALUE cQueue;
|
|
23
32
|
extern VALUE cEvent;
|
|
@@ -28,7 +37,8 @@ extern ID ID_clear;
|
|
|
28
37
|
extern ID ID_each;
|
|
29
38
|
extern ID ID_fiber_trace;
|
|
30
39
|
extern ID ID_inspect;
|
|
31
|
-
extern ID
|
|
40
|
+
extern ID ID_invoke;
|
|
41
|
+
extern ID ID_ivar_backend;
|
|
32
42
|
extern ID ID_ivar_running;
|
|
33
43
|
extern ID ID_ivar_thread;
|
|
34
44
|
extern ID ID_new;
|
|
@@ -56,26 +66,9 @@ enum {
|
|
|
56
66
|
FIBER_STATE_SCHEDULED = 2
|
|
57
67
|
};
|
|
58
68
|
|
|
59
|
-
// watcher flags
|
|
60
|
-
enum {
|
|
61
|
-
// a watcher's active field will be set to this after fork
|
|
62
|
-
GYRO_WATCHER_POST_FORK = 0xFF
|
|
63
|
-
};
|
|
64
|
-
|
|
65
69
|
VALUE Fiber_auto_watcher(VALUE self);
|
|
66
70
|
void Fiber_make_runnable(VALUE fiber, VALUE value);
|
|
67
71
|
|
|
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
72
|
VALUE Queue_push(VALUE self, VALUE value);
|
|
80
73
|
VALUE Queue_unshift(VALUE self, VALUE value);
|
|
81
74
|
VALUE Queue_shift(VALUE self);
|
|
@@ -85,13 +78,7 @@ VALUE Queue_delete(VALUE self, VALUE value);
|
|
|
85
78
|
long Queue_len(VALUE self);
|
|
86
79
|
void Queue_trace(VALUE self);
|
|
87
80
|
|
|
88
|
-
VALUE Polyphony_snooze(VALUE self);
|
|
89
|
-
|
|
90
81
|
VALUE Thread_schedule_fiber(VALUE thread, VALUE fiber, VALUE value);
|
|
91
82
|
VALUE Thread_switch_fiber(VALUE thread);
|
|
92
83
|
|
|
93
|
-
int io_setstrbuf(VALUE *str, long len);
|
|
94
|
-
void io_set_read_length(VALUE str, long n, int shrinkable);
|
|
95
|
-
VALUE io_enc_str(VALUE str, rb_io_t *fptr);
|
|
96
|
-
|
|
97
84
|
#endif /* POLYPHONY_H */
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
void Init_Fiber();
|
|
4
4
|
void Init_Polyphony();
|
|
5
|
-
void
|
|
5
|
+
void Init_LibevBackend();
|
|
6
6
|
void Init_Queue();
|
|
7
7
|
void Init_Event();
|
|
8
8
|
void Init_Thread();
|
|
@@ -12,12 +12,11 @@ void Init_polyphony_ext() {
|
|
|
12
12
|
ev_set_allocator(xrealloc);
|
|
13
13
|
|
|
14
14
|
Init_Polyphony();
|
|
15
|
-
|
|
15
|
+
|
|
16
|
+
Init_LibevBackend();
|
|
16
17
|
Init_Queue();
|
|
17
18
|
Init_Event();
|
|
18
|
-
|
|
19
19
|
Init_Fiber();
|
|
20
20
|
Init_Thread();
|
|
21
|
-
|
|
22
21
|
Init_Tracing();
|
|
23
22
|
}
|
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,25 @@ 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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
+
TEST_RESUME_EXCEPTION(switchpoint_result);
|
|
91
93
|
RB_GC_GUARD(switchpoint_result);
|
|
94
|
+
|
|
95
|
+
if (queue->values.count > 0)
|
|
96
|
+
return ring_buffer_shift(&queue->values);
|
|
92
97
|
}
|
|
93
98
|
|
|
94
|
-
return
|
|
99
|
+
return Qnil;
|
|
95
100
|
}
|
|
96
101
|
|
|
97
102
|
VALUE Queue_shift_no_wait(VALUE self) {
|
|
@@ -146,7 +151,7 @@ VALUE Queue_flush_waiters(VALUE self, VALUE value) {
|
|
|
146
151
|
while(1) {
|
|
147
152
|
VALUE fiber = ring_buffer_shift(&queue->shift_queue);
|
|
148
153
|
if (fiber == Qnil) return self;
|
|
149
|
-
|
|
154
|
+
|
|
150
155
|
Fiber_make_runnable(fiber, value);
|
|
151
156
|
}
|
|
152
157
|
}
|
|
@@ -158,6 +163,13 @@ VALUE Queue_empty_p(VALUE self) {
|
|
|
158
163
|
return (queue->values.count == 0) ? Qtrue : Qfalse;
|
|
159
164
|
}
|
|
160
165
|
|
|
166
|
+
VALUE Queue_pending_p(VALUE self) {
|
|
167
|
+
Queue_t *queue;
|
|
168
|
+
GetQueue(self, queue);
|
|
169
|
+
|
|
170
|
+
return (queue->shift_queue.count > 0) ? Qtrue : Qfalse;
|
|
171
|
+
}
|
|
172
|
+
|
|
161
173
|
VALUE Queue_size_m(VALUE self) {
|
|
162
174
|
Queue_t *queue;
|
|
163
175
|
GetQueue(self, queue);
|
|
@@ -165,6 +177,13 @@ VALUE Queue_size_m(VALUE self) {
|
|
|
165
177
|
return INT2NUM(queue->values.count);
|
|
166
178
|
}
|
|
167
179
|
|
|
180
|
+
void Queue_trace(VALUE self) {
|
|
181
|
+
Queue_t *queue;
|
|
182
|
+
GetQueue(self, queue);
|
|
183
|
+
|
|
184
|
+
printf("run queue size: %d count: %d\n", queue->values.size, queue->values.count);
|
|
185
|
+
}
|
|
186
|
+
|
|
168
187
|
void Init_Queue() {
|
|
169
188
|
cQueue = rb_define_class_under(mPolyphony, "Queue", rb_cData);
|
|
170
189
|
rb_define_alloc_func(cQueue, Queue_allocate);
|
|
@@ -183,5 +202,6 @@ void Init_Queue() {
|
|
|
183
202
|
rb_define_method(cQueue, "shift_all", Queue_shift_all, 0);
|
|
184
203
|
rb_define_method(cQueue, "flush_waiters", Queue_flush_waiters, 1);
|
|
185
204
|
rb_define_method(cQueue, "empty?", Queue_empty_p, 0);
|
|
205
|
+
rb_define_method(cQueue, "pending?", Queue_pending_p, 0);
|
|
186
206
|
rb_define_method(cQueue, "size", Queue_size_m, 0);
|
|
187
207
|
}
|
data/ext/polyphony/ring_buffer.c
CHANGED
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;
|
|
@@ -12,7 +12,7 @@ ID ID_stop;
|
|
|
12
12
|
|
|
13
13
|
static VALUE Thread_setup_fiber_scheduling(VALUE self) {
|
|
14
14
|
VALUE queue = rb_funcall(cQueue, ID_new, 0);
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
rb_ivar_set(self, ID_ivar_main_fiber, rb_fiber_current());
|
|
17
17
|
rb_ivar_set(self, ID_run_queue, queue);
|
|
18
18
|
|
|
@@ -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,23 +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
|
-
|
|
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;
|
|
64
|
+
}
|
|
65
|
+
|
|
56
66
|
rb_ivar_set(fiber, ID_runnable_value, value);
|
|
57
|
-
|
|
58
|
-
if (rb_ivar_get(fiber, ID_runnable) != Qnil) return self;
|
|
67
|
+
COND_TRACE(3, SYM_fiber_schedule, fiber, value);
|
|
59
68
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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);
|
|
63
73
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
+
}
|
|
72
83
|
}
|
|
73
84
|
return self;
|
|
74
85
|
}
|
|
@@ -78,7 +89,7 @@ VALUE Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value)
|
|
|
78
89
|
|
|
79
90
|
if (rb_fiber_alive_p(fiber) != Qtrue) return self;
|
|
80
91
|
|
|
81
|
-
|
|
92
|
+
COND_TRACE(3, SYM_fiber_schedule, fiber, value);
|
|
82
93
|
rb_ivar_set(fiber, ID_runnable_value, value);
|
|
83
94
|
|
|
84
95
|
queue = rb_ivar_get(self, ID_run_queue);
|
|
@@ -99,8 +110,8 @@ VALUE Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value)
|
|
|
99
110
|
// event loop. Otherwise it's gonna be stuck waiting for an event to
|
|
100
111
|
// happen, not knowing that it there's already a fiber ready to run in its
|
|
101
112
|
// run queue.
|
|
102
|
-
VALUE
|
|
103
|
-
|
|
113
|
+
VALUE backend = rb_ivar_get(self, ID_ivar_backend);
|
|
114
|
+
__BACKEND__.wakeup(backend);
|
|
104
115
|
}
|
|
105
116
|
return self;
|
|
106
117
|
}
|
|
@@ -110,43 +121,39 @@ VALUE Thread_switch_fiber(VALUE self) {
|
|
|
110
121
|
VALUE queue = rb_ivar_get(self, ID_run_queue);
|
|
111
122
|
VALUE next_fiber;
|
|
112
123
|
VALUE value;
|
|
113
|
-
VALUE
|
|
124
|
+
VALUE backend = rb_ivar_get(self, ID_ivar_backend);
|
|
114
125
|
int ref_count;
|
|
115
|
-
int
|
|
126
|
+
int backend_was_polled = 0;
|
|
116
127
|
|
|
117
|
-
if (__tracing_enabled__)
|
|
118
|
-
|
|
119
|
-
rb_funcall(rb_cObject, ID_fiber_trace, 2, SYM_fiber_switchpoint, current_fiber);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
128
|
+
if (__tracing_enabled__ && (rb_ivar_get(current_fiber, ID_ivar_running) != Qfalse))
|
|
129
|
+
TRACE(2, SYM_fiber_switchpoint, current_fiber);
|
|
122
130
|
|
|
123
|
-
ref_count =
|
|
131
|
+
ref_count = __BACKEND__.ref_count(backend);
|
|
124
132
|
while (1) {
|
|
125
133
|
next_fiber = Queue_shift_no_wait(queue);
|
|
126
134
|
if (next_fiber != Qnil) {
|
|
127
|
-
if (
|
|
128
|
-
// this
|
|
129
|
-
|
|
130
|
-
LibevAgent_poll(agent, Qtrue, current_fiber, queue);
|
|
135
|
+
if (backend_was_polled == 0 && ref_count > 0) {
|
|
136
|
+
// this prevents event starvation in case the run queue never empties
|
|
137
|
+
__BACKEND__.poll(backend, Qtrue, current_fiber, queue);
|
|
131
138
|
}
|
|
132
139
|
break;
|
|
133
140
|
}
|
|
134
141
|
if (ref_count == 0) break;
|
|
135
142
|
|
|
136
|
-
|
|
137
|
-
|
|
143
|
+
__BACKEND__.poll(backend, Qnil, current_fiber, queue);
|
|
144
|
+
backend_was_polled = 1;
|
|
138
145
|
}
|
|
139
146
|
|
|
140
147
|
if (next_fiber == Qnil) return Qnil;
|
|
141
148
|
|
|
142
149
|
// run next fiber
|
|
143
150
|
value = rb_ivar_get(next_fiber, ID_runnable_value);
|
|
144
|
-
|
|
151
|
+
COND_TRACE(3, SYM_fiber_run, next_fiber, value);
|
|
145
152
|
|
|
146
153
|
rb_ivar_set(next_fiber, ID_runnable, Qnil);
|
|
147
154
|
RB_GC_GUARD(next_fiber);
|
|
148
155
|
RB_GC_GUARD(value);
|
|
149
|
-
return (next_fiber == current_fiber) ?
|
|
156
|
+
return (next_fiber == current_fiber) ?
|
|
150
157
|
value : rb_funcall(next_fiber, ID_transfer, 1, value);
|
|
151
158
|
}
|
|
152
159
|
|
|
@@ -164,12 +171,12 @@ VALUE Thread_reset_fiber_scheduling(VALUE self) {
|
|
|
164
171
|
}
|
|
165
172
|
|
|
166
173
|
VALUE Thread_fiber_break_out_of_ev_loop(VALUE self, VALUE fiber, VALUE resume_obj) {
|
|
167
|
-
VALUE
|
|
174
|
+
VALUE backend = rb_ivar_get(self, ID_ivar_backend);
|
|
168
175
|
if (fiber != Qnil) {
|
|
169
176
|
Thread_schedule_fiber_with_priority(self, fiber, resume_obj);
|
|
170
177
|
}
|
|
171
178
|
|
|
172
|
-
if (
|
|
179
|
+
if (__BACKEND__.wakeup(backend) == Qnil) {
|
|
173
180
|
// we're not inside the ev_loop, so we just do a switchpoint
|
|
174
181
|
Thread_switch_fiber(self);
|
|
175
182
|
}
|
|
@@ -177,6 +184,11 @@ VALUE Thread_fiber_break_out_of_ev_loop(VALUE self, VALUE fiber, VALUE resume_ob
|
|
|
177
184
|
return self;
|
|
178
185
|
}
|
|
179
186
|
|
|
187
|
+
VALUE Thread_debug(VALUE self) {
|
|
188
|
+
rb_ivar_set(self, rb_intern("@__debug__"), Qtrue);
|
|
189
|
+
return self;
|
|
190
|
+
}
|
|
191
|
+
|
|
180
192
|
void Init_Thread() {
|
|
181
193
|
rb_define_method(rb_cThread, "setup_fiber_scheduling", Thread_setup_fiber_scheduling, 0);
|
|
182
194
|
rb_define_method(rb_cThread, "reset_fiber_scheduling", Thread_reset_fiber_scheduling, 0);
|
|
@@ -189,8 +201,10 @@ void Init_Thread() {
|
|
|
189
201
|
rb_define_method(rb_cThread, "switch_fiber", Thread_switch_fiber, 0);
|
|
190
202
|
rb_define_method(rb_cThread, "run_queue_trace", Thread_run_queue_trace, 0);
|
|
191
203
|
|
|
204
|
+
rb_define_method(rb_cThread, "debug!", Thread_debug, 0);
|
|
205
|
+
|
|
192
206
|
ID_deactivate_all_watchers_post_fork = rb_intern("deactivate_all_watchers_post_fork");
|
|
193
|
-
|
|
207
|
+
ID_ivar_backend = rb_intern("@backend");
|
|
194
208
|
ID_ivar_join_wait_queue = rb_intern("@join_wait_queue");
|
|
195
209
|
ID_ivar_main_fiber = rb_intern("@main_fiber");
|
|
196
210
|
ID_ivar_result = rb_intern("@result");
|
data/lib/polyphony.rb
CHANGED
|
@@ -3,50 +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
19
|
|
|
27
|
-
#
|
|
20
|
+
# Polyphony API
|
|
28
21
|
module Polyphony
|
|
29
22
|
class << self
|
|
30
|
-
def wait_for_signal(sig)
|
|
31
|
-
raise "should be reimplemented"
|
|
32
|
-
|
|
33
|
-
fiber = Fiber.current
|
|
34
|
-
# Polyphony.ref
|
|
35
|
-
old_trap = trap(sig) do
|
|
36
|
-
# Polyphony.unref
|
|
37
|
-
fiber.schedule(sig)
|
|
38
|
-
trap(sig, old_trap)
|
|
39
|
-
end
|
|
40
|
-
suspend
|
|
41
|
-
|
|
42
|
-
end
|
|
43
|
-
|
|
44
23
|
def fork(&block)
|
|
45
24
|
Kernel.fork do
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
#
|
|
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.
|
|
50
36
|
spin_forked_block(&block).transfer
|
|
51
37
|
end
|
|
52
38
|
end
|
|
@@ -57,7 +43,7 @@ module Polyphony
|
|
|
57
43
|
rescue SystemExit
|
|
58
44
|
# fall through to ensure
|
|
59
45
|
rescue Exception => e
|
|
60
|
-
e.full_message
|
|
46
|
+
warn e.full_message
|
|
61
47
|
exit!
|
|
62
48
|
ensure
|
|
63
49
|
exit_forked_process
|
|
@@ -65,16 +51,9 @@ module Polyphony
|
|
|
65
51
|
end
|
|
66
52
|
|
|
67
53
|
def run_forked_block(&block)
|
|
68
|
-
# A race condition can arise if a TERM or INT signal is received before
|
|
69
|
-
# the forked process has finished initializing. To prevent this we restore
|
|
70
|
-
# the default signal handlers, and then reinstall the custom Polyphony
|
|
71
|
-
# handlers just before running the given block.
|
|
72
|
-
trap('SIGTERM', 'DEFAULT')
|
|
73
|
-
trap('SIGINT', 'DEFAULT')
|
|
74
|
-
|
|
75
54
|
Thread.current.setup
|
|
76
55
|
Fiber.current.setup_main_fiber
|
|
77
|
-
Thread.current.
|
|
56
|
+
Thread.current.backend.post_fork
|
|
78
57
|
|
|
79
58
|
install_terminating_signal_handlers
|
|
80
59
|
|
|
@@ -123,12 +102,20 @@ module Polyphony
|
|
|
123
102
|
# processes (see Polyphony.fork).
|
|
124
103
|
at_exit do
|
|
125
104
|
next unless @original_pid == ::Process.pid
|
|
126
|
-
|
|
105
|
+
|
|
127
106
|
Polyphony.terminate_threads
|
|
128
107
|
Fiber.current.shutdown_all_children
|
|
129
108
|
end
|
|
130
109
|
end
|
|
131
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
|
|
132
119
|
end
|
|
133
120
|
|
|
134
121
|
Polyphony.install_terminating_signal_handlers
|