polyphony 0.39 → 0.43.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/.github/workflows/test.yml +11 -2
- data/.gitignore +2 -2
- data/.rubocop.yml +30 -0
- data/CHANGELOG.md +23 -4
- data/Gemfile.lock +15 -12
- data/README.md +2 -1
- data/Rakefile +3 -3
- data/TODO.md +27 -97
- data/docs/_config.yml +56 -7
- data/docs/_sass/custom/custom.scss +6 -26
- data/docs/_sass/overrides.scss +0 -46
- data/docs/{user-guide → _user-guide}/all-about-timers.md +0 -0
- data/docs/_user-guide/index.md +9 -0
- data/docs/{user-guide → _user-guide}/web-server.md +0 -0
- data/docs/api-reference/fiber.md +2 -2
- data/docs/api-reference/index.md +9 -0
- data/docs/api-reference/polyphony-process.md +1 -1
- data/docs/api-reference/thread.md +1 -1
- data/docs/faq.md +21 -11
- data/docs/favicon.ico +0 -0
- data/docs/getting-started/index.md +10 -0
- data/docs/getting-started/installing.md +2 -6
- data/docs/getting-started/overview.md +486 -0
- data/docs/getting-started/tutorial.md +27 -19
- data/docs/index.md +6 -2
- data/docs/main-concepts/concurrency.md +0 -5
- data/docs/main-concepts/design-principles.md +69 -21
- data/docs/main-concepts/extending.md +1 -1
- data/docs/main-concepts/index.md +9 -0
- data/docs/polyphony-logo.png +0 -0
- data/examples/core/01-spinning-up-fibers.rb +1 -0
- data/examples/core/03-interrupting.rb +4 -1
- data/examples/core/04-handling-signals.rb +19 -0
- data/examples/core/xx-agent.rb +102 -0
- data/examples/core/xx-sleeping.rb +14 -6
- 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 +13 -36
- data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +58 -0
- data/examples/performance/xx-array.rb +11 -0
- data/examples/performance/xx-fiber-switch.rb +9 -0
- data/examples/performance/xx-snooze.rb +15 -0
- data/ext/{gyro → polyphony}/extconf.rb +2 -2
- data/ext/{gyro → polyphony}/fiber.c +17 -23
- data/ext/{gyro → polyphony}/libev.c +0 -0
- data/ext/{gyro → polyphony}/libev.h +0 -0
- data/ext/polyphony/libev_agent.c +718 -0
- data/ext/polyphony/libev_queue.c +216 -0
- data/ext/polyphony/polyphony.c +73 -0
- data/ext/{gyro/gyro.h → polyphony/polyphony.h} +19 -39
- data/ext/polyphony/polyphony_ext.c +21 -0
- data/ext/polyphony/thread.c +200 -0
- data/ext/{gyro → polyphony}/tracing.c +1 -1
- data/lib/polyphony.rb +19 -14
- data/lib/polyphony/adapters/irb.rb +1 -1
- data/lib/polyphony/adapters/postgres.rb +6 -5
- data/lib/polyphony/adapters/process.rb +5 -5
- data/lib/polyphony/adapters/trace.rb +28 -28
- data/lib/polyphony/core/channel.rb +3 -3
- data/lib/polyphony/core/exceptions.rb +1 -1
- data/lib/polyphony/core/global_api.rb +13 -11
- data/lib/polyphony/core/resource_pool.rb +3 -3
- data/lib/polyphony/core/sync.rb +2 -2
- data/lib/polyphony/core/thread_pool.rb +6 -6
- data/lib/polyphony/core/throttler.rb +13 -6
- data/lib/polyphony/event.rb +27 -0
- data/lib/polyphony/extensions/core.rb +22 -14
- data/lib/polyphony/extensions/fiber.rb +4 -4
- data/lib/polyphony/extensions/io.rb +59 -25
- data/lib/polyphony/extensions/openssl.rb +36 -16
- data/lib/polyphony/extensions/socket.rb +28 -10
- data/lib/polyphony/extensions/thread.rb +16 -9
- data/lib/polyphony/net.rb +9 -9
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +4 -4
- data/test/helper.rb +12 -8
- data/test/test_agent.rb +124 -0
- data/test/{test_async.rb → test_event.rb} +15 -7
- data/test/test_ext.rb +25 -4
- data/test/test_fiber.rb +19 -10
- data/test/test_global_api.rb +11 -11
- data/test/test_io.rb +44 -29
- data/test/test_queue.rb +74 -0
- data/test/test_signal.rb +3 -40
- data/test/test_socket.rb +34 -0
- data/test/test_thread.rb +38 -17
- data/test/test_thread_pool.rb +2 -2
- data/test/test_throttler.rb +5 -3
- data/test/test_trace.rb +6 -5
- metadata +41 -43
- data/docs/_includes/nav.html +0 -51
- data/docs/_includes/prevnext.html +0 -17
- data/docs/_layouts/default.html +0 -106
- data/docs/api-reference.md +0 -11
- data/docs/api-reference/gyro-async.md +0 -57
- data/docs/api-reference/gyro-child.md +0 -29
- data/docs/api-reference/gyro-queue.md +0 -44
- data/docs/api-reference/gyro-timer.md +0 -51
- data/docs/api-reference/gyro.md +0 -25
- data/docs/getting-started.md +0 -10
- data/docs/main-concepts.md +0 -10
- data/docs/user-guide.md +0 -10
- data/examples/core/forever_sleep.rb +0 -19
- data/ext/gyro/async.c +0 -162
- data/ext/gyro/child.c +0 -141
- data/ext/gyro/gyro.c +0 -103
- data/ext/gyro/gyro_ext.c +0 -33
- data/ext/gyro/io.c +0 -489
- data/ext/gyro/queue.c +0 -142
- data/ext/gyro/selector.c +0 -228
- data/ext/gyro/signal.c +0 -133
- data/ext/gyro/socket.c +0 -210
- data/ext/gyro/thread.c +0 -308
- data/ext/gyro/timer.c +0 -151
- data/test/test_timer.rb +0 -32
@@ -0,0 +1,216 @@
|
|
1
|
+
#include "polyphony.h"
|
2
|
+
|
3
|
+
struct async_watcher {
|
4
|
+
ev_async async;
|
5
|
+
struct ev_loop *ev_loop;
|
6
|
+
VALUE fiber;
|
7
|
+
};
|
8
|
+
|
9
|
+
struct async_queue {
|
10
|
+
struct async_watcher **queue;
|
11
|
+
unsigned int len;
|
12
|
+
unsigned int count;
|
13
|
+
unsigned int push_idx;
|
14
|
+
unsigned int pop_idx;
|
15
|
+
};
|
16
|
+
|
17
|
+
void async_queue_init(struct async_queue *queue) {
|
18
|
+
queue->len = 4;
|
19
|
+
queue->count = 0;
|
20
|
+
queue->queue = malloc(sizeof(struct async_watcher *) * queue->len);
|
21
|
+
queue->push_idx = 0;
|
22
|
+
queue->pop_idx = 0;
|
23
|
+
}
|
24
|
+
|
25
|
+
void async_queue_free(struct async_queue *queue) {
|
26
|
+
free(queue->queue);
|
27
|
+
}
|
28
|
+
|
29
|
+
void async_queue_push(struct async_queue *queue, struct async_watcher *watcher) {
|
30
|
+
if (queue->push_idx == queue->len) {
|
31
|
+
queue->len = queue->len * 2;
|
32
|
+
queue->queue = realloc(queue->queue, sizeof(struct async_watcher *) * queue->len);
|
33
|
+
}
|
34
|
+
if (queue->count == 0) {
|
35
|
+
queue->push_idx = 0;
|
36
|
+
queue->pop_idx = 0;
|
37
|
+
}
|
38
|
+
queue->count++;
|
39
|
+
queue->queue[queue->push_idx++] = watcher;
|
40
|
+
}
|
41
|
+
|
42
|
+
struct async_watcher *async_queue_pop(struct async_queue *queue) {
|
43
|
+
if (queue->count == 0) return 0;
|
44
|
+
|
45
|
+
queue->count--;
|
46
|
+
|
47
|
+
return queue->queue[queue->pop_idx++];
|
48
|
+
}
|
49
|
+
|
50
|
+
void async_queue_remove_at_idx(struct async_queue *queue, unsigned int remove_idx) {
|
51
|
+
queue->count--;
|
52
|
+
queue->push_idx--;
|
53
|
+
if (remove_idx < queue->push_idx)
|
54
|
+
memmove(
|
55
|
+
queue->queue + remove_idx,
|
56
|
+
queue->queue + remove_idx + 1,
|
57
|
+
(queue->push_idx - remove_idx) * sizeof(struct async_watcher *)
|
58
|
+
);
|
59
|
+
}
|
60
|
+
|
61
|
+
void async_queue_remove_by_fiber(struct async_queue *queue, VALUE fiber) {
|
62
|
+
if (queue->count == 0) return;
|
63
|
+
|
64
|
+
for (unsigned idx = queue->pop_idx; idx < queue->push_idx; idx++) {
|
65
|
+
if (queue->queue[idx]->fiber == fiber) {
|
66
|
+
async_queue_remove_at_idx(queue, idx);
|
67
|
+
return;
|
68
|
+
}
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
typedef struct queue {
|
73
|
+
VALUE items;
|
74
|
+
struct async_queue shift_queue;
|
75
|
+
} LibevQueue_t;
|
76
|
+
|
77
|
+
|
78
|
+
VALUE cLibevQueue = Qnil;
|
79
|
+
|
80
|
+
static void LibevQueue_mark(void *ptr) {
|
81
|
+
LibevQueue_t *queue = ptr;
|
82
|
+
rb_gc_mark(queue->items);
|
83
|
+
}
|
84
|
+
|
85
|
+
static void LibevQueue_free(void *ptr) {
|
86
|
+
LibevQueue_t *queue = ptr;
|
87
|
+
async_queue_free(&queue->shift_queue);
|
88
|
+
xfree(ptr);
|
89
|
+
}
|
90
|
+
|
91
|
+
static size_t LibevQueue_size(const void *ptr) {
|
92
|
+
return sizeof(LibevQueue_t);
|
93
|
+
}
|
94
|
+
|
95
|
+
static const rb_data_type_t LibevQueue_type = {
|
96
|
+
"Queue",
|
97
|
+
{LibevQueue_mark, LibevQueue_free, LibevQueue_size,},
|
98
|
+
0, 0, 0
|
99
|
+
};
|
100
|
+
|
101
|
+
static VALUE LibevQueue_allocate(VALUE klass) {
|
102
|
+
LibevQueue_t *queue;
|
103
|
+
|
104
|
+
queue = ALLOC(LibevQueue_t);
|
105
|
+
return TypedData_Wrap_Struct(klass, &LibevQueue_type, queue);
|
106
|
+
}
|
107
|
+
|
108
|
+
#define GetQueue(obj, queue) \
|
109
|
+
TypedData_Get_Struct((obj), LibevQueue_t, &LibevQueue_type, (queue))
|
110
|
+
|
111
|
+
static VALUE LibevQueue_initialize(VALUE self) {
|
112
|
+
LibevQueue_t *queue;
|
113
|
+
GetQueue(self, queue);
|
114
|
+
|
115
|
+
queue->items = rb_ary_new();
|
116
|
+
async_queue_init(&queue->shift_queue);
|
117
|
+
|
118
|
+
return self;
|
119
|
+
}
|
120
|
+
|
121
|
+
VALUE LibevQueue_push(VALUE self, VALUE value) {
|
122
|
+
LibevQueue_t *queue;
|
123
|
+
struct async_watcher *watcher;
|
124
|
+
GetQueue(self, queue);
|
125
|
+
watcher = async_queue_pop(&queue->shift_queue);
|
126
|
+
if (watcher) {
|
127
|
+
ev_async_send(watcher->ev_loop, &watcher->async);
|
128
|
+
}
|
129
|
+
rb_ary_push(queue->items, value);
|
130
|
+
return self;
|
131
|
+
}
|
132
|
+
|
133
|
+
struct ev_loop *LibevAgent_ev_loop(VALUE self);
|
134
|
+
|
135
|
+
void async_queue_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, int revents) {
|
136
|
+
struct async_watcher *watcher = (struct async_watcher *)ev_async;
|
137
|
+
Fiber_make_runnable(watcher->fiber, Qnil);
|
138
|
+
}
|
139
|
+
|
140
|
+
VALUE libev_agent_await(VALUE self);
|
141
|
+
|
142
|
+
VALUE LibevQueue_shift(VALUE self) {
|
143
|
+
LibevQueue_t *queue;
|
144
|
+
GetQueue(self, queue);
|
145
|
+
|
146
|
+
if (RARRAY_LEN(queue->items) == 0) {
|
147
|
+
struct async_watcher watcher;
|
148
|
+
VALUE agent = rb_ivar_get(rb_thread_current(), ID_ivar_agent);
|
149
|
+
VALUE switchpoint_result = Qnil;
|
150
|
+
|
151
|
+
watcher.ev_loop = LibevAgent_ev_loop(agent);
|
152
|
+
watcher.fiber = rb_fiber_current();
|
153
|
+
async_queue_push(&queue->shift_queue, &watcher);
|
154
|
+
ev_async_init(&watcher.async, async_queue_callback);
|
155
|
+
ev_async_start(watcher.ev_loop, &watcher.async);
|
156
|
+
|
157
|
+
switchpoint_result = libev_agent_await(agent);
|
158
|
+
ev_async_stop(watcher.ev_loop, &watcher.async);
|
159
|
+
|
160
|
+
if (RTEST(rb_obj_is_kind_of(switchpoint_result, rb_eException))) {
|
161
|
+
async_queue_remove_by_fiber(&queue->shift_queue, watcher.fiber);
|
162
|
+
return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
|
163
|
+
}
|
164
|
+
RB_GC_GUARD(watcher.fiber);
|
165
|
+
RB_GC_GUARD(agent);
|
166
|
+
RB_GC_GUARD(switchpoint_result);
|
167
|
+
}
|
168
|
+
|
169
|
+
return rb_ary_shift(queue->items);
|
170
|
+
}
|
171
|
+
|
172
|
+
VALUE LibevQueue_shift_each(VALUE self) {
|
173
|
+
LibevQueue_t *queue;
|
174
|
+
VALUE old_queue;
|
175
|
+
GetQueue(self, queue);
|
176
|
+
old_queue = queue->items;
|
177
|
+
queue->items = rb_ary_new();
|
178
|
+
|
179
|
+
if (rb_block_given_p()) {
|
180
|
+
long len = RARRAY_LEN(old_queue);
|
181
|
+
long i;
|
182
|
+
for (i = 0; i < len; i++) {
|
183
|
+
rb_yield(RARRAY_AREF(old_queue, i));
|
184
|
+
}
|
185
|
+
RB_GC_GUARD(old_queue);
|
186
|
+
return self;
|
187
|
+
}
|
188
|
+
else {
|
189
|
+
RB_GC_GUARD(old_queue);
|
190
|
+
return old_queue;
|
191
|
+
}
|
192
|
+
}
|
193
|
+
|
194
|
+
VALUE LibevQueue_empty_p(VALUE self) {
|
195
|
+
LibevQueue_t *queue;
|
196
|
+
GetQueue(self, queue);
|
197
|
+
|
198
|
+
return (RARRAY_LEN(queue->items) == 0) ? Qtrue : Qfalse;
|
199
|
+
}
|
200
|
+
|
201
|
+
void Init_LibevQueue() {
|
202
|
+
cLibevQueue = rb_define_class_under(mPolyphony, "LibevQueue", rb_cData);
|
203
|
+
rb_define_alloc_func(cLibevQueue, LibevQueue_allocate);
|
204
|
+
|
205
|
+
rb_define_method(cLibevQueue, "initialize", LibevQueue_initialize, 0);
|
206
|
+
rb_define_method(cLibevQueue, "push", LibevQueue_push, 1);
|
207
|
+
rb_define_method(cLibevQueue, "<<", LibevQueue_push, 1);
|
208
|
+
|
209
|
+
rb_define_method(cLibevQueue, "pop", LibevQueue_shift, 0);
|
210
|
+
rb_define_method(cLibevQueue, "shift", LibevQueue_shift, 0);
|
211
|
+
|
212
|
+
rb_define_method(cLibevQueue, "shift_each", LibevQueue_shift_each, 0);
|
213
|
+
rb_define_method(cLibevQueue, "empty?", LibevQueue_empty_p, 0);
|
214
|
+
}
|
215
|
+
|
216
|
+
|
@@ -0,0 +1,73 @@
|
|
1
|
+
#include "polyphony.h"
|
2
|
+
|
3
|
+
VALUE mPolyphony;
|
4
|
+
|
5
|
+
ID ID_await_no_raise;
|
6
|
+
ID ID_call;
|
7
|
+
ID ID_caller;
|
8
|
+
ID ID_clear;
|
9
|
+
ID ID_each;
|
10
|
+
ID ID_inspect;
|
11
|
+
ID ID_new;
|
12
|
+
ID ID_raise;
|
13
|
+
ID ID_ivar_running;
|
14
|
+
ID ID_ivar_thread;
|
15
|
+
ID ID_runnable;
|
16
|
+
ID ID_runnable_value;
|
17
|
+
ID ID_size;
|
18
|
+
ID ID_signal;
|
19
|
+
ID ID_switch_fiber;
|
20
|
+
ID ID_transfer;
|
21
|
+
ID ID_R;
|
22
|
+
ID ID_W;
|
23
|
+
ID ID_RW;
|
24
|
+
|
25
|
+
VALUE Polyphony_snooze(VALUE self) {
|
26
|
+
VALUE ret;
|
27
|
+
VALUE fiber = rb_fiber_current();
|
28
|
+
|
29
|
+
Fiber_make_runnable(fiber, Qnil);
|
30
|
+
ret = Thread_switch_fiber(rb_thread_current());
|
31
|
+
TEST_RESUME_EXCEPTION(ret);
|
32
|
+
RB_GC_GUARD(ret);
|
33
|
+
return ret;
|
34
|
+
}
|
35
|
+
|
36
|
+
static VALUE Polyphony_suspend(VALUE self) {
|
37
|
+
VALUE ret = Thread_switch_fiber(rb_thread_current());
|
38
|
+
|
39
|
+
TEST_RESUME_EXCEPTION(ret);
|
40
|
+
RB_GC_GUARD(ret);
|
41
|
+
return ret;
|
42
|
+
}
|
43
|
+
|
44
|
+
VALUE Polyphony_trace(VALUE self, VALUE enabled) {
|
45
|
+
__tracing_enabled__ = RTEST(enabled) ? 1 : 0;
|
46
|
+
return Qnil;
|
47
|
+
}
|
48
|
+
|
49
|
+
void Init_Polyphony() {
|
50
|
+
mPolyphony = rb_define_module("Polyphony");
|
51
|
+
|
52
|
+
rb_define_singleton_method(mPolyphony, "trace", Polyphony_trace, 1);
|
53
|
+
|
54
|
+
rb_define_global_function("snooze", Polyphony_snooze, 0);
|
55
|
+
rb_define_global_function("suspend", Polyphony_suspend, 0);
|
56
|
+
|
57
|
+
ID_await_no_raise = rb_intern("await_no_raise");
|
58
|
+
ID_call = rb_intern("call");
|
59
|
+
ID_caller = rb_intern("caller");
|
60
|
+
ID_clear = rb_intern("clear");
|
61
|
+
ID_each = rb_intern("each");
|
62
|
+
ID_inspect = rb_intern("inspect");
|
63
|
+
ID_ivar_running = rb_intern("@running");
|
64
|
+
ID_ivar_thread = rb_intern("@thread");
|
65
|
+
ID_new = rb_intern("new");
|
66
|
+
ID_raise = rb_intern("raise");
|
67
|
+
ID_runnable = rb_intern("runnable");
|
68
|
+
ID_runnable_value = rb_intern("runnable_value");
|
69
|
+
ID_signal = rb_intern("signal");
|
70
|
+
ID_size = rb_intern("size");
|
71
|
+
ID_switch_fiber = rb_intern("switch_fiber");
|
72
|
+
ID_transfer = rb_intern("transfer");
|
73
|
+
}
|
@@ -7,28 +7,29 @@
|
|
7
7
|
|
8
8
|
// debugging
|
9
9
|
#define OBJ_ID(obj) (NUM2LONG(rb_funcall(obj, rb_intern("object_id"), 0)))
|
10
|
-
#define INSPECT(
|
10
|
+
#define INSPECT(obj) { VALUE s = rb_funcall(obj, rb_intern("inspect"), 0); printf("%s\n", StringValueCStr(s));}
|
11
11
|
#define FIBER_TRACE(...) if (__tracing_enabled__) { \
|
12
12
|
rb_funcall(rb_cObject, ID_fiber_trace, __VA_ARGS__); \
|
13
13
|
}
|
14
14
|
|
15
|
+
#define TEST_EXCEPTION(ret) (RTEST(rb_obj_is_kind_of(ret, rb_eException)))
|
16
|
+
|
15
17
|
#define TEST_RESUME_EXCEPTION(ret) if (RTEST(rb_obj_is_kind_of(ret, rb_eException))) { \
|
16
18
|
return rb_funcall(rb_mKernel, ID_raise, 1, ret); \
|
17
19
|
}
|
18
20
|
|
19
|
-
extern VALUE
|
20
|
-
extern VALUE
|
21
|
-
extern VALUE
|
22
|
-
extern VALUE cGyro_Queue;
|
23
|
-
extern VALUE cGyro_Selector;
|
24
|
-
extern VALUE cGyro_Timer;
|
21
|
+
extern VALUE mPolyphony;
|
22
|
+
extern VALUE cLibevQueue;
|
23
|
+
extern VALUE cEvent;
|
25
24
|
|
25
|
+
extern ID ID_await_no_raise;
|
26
26
|
extern ID ID_call;
|
27
27
|
extern ID ID_caller;
|
28
28
|
extern ID ID_clear;
|
29
29
|
extern ID ID_each;
|
30
30
|
extern ID ID_fiber_trace;
|
31
31
|
extern ID ID_inspect;
|
32
|
+
extern ID ID_ivar_agent;
|
32
33
|
extern ID ID_ivar_running;
|
33
34
|
extern ID ID_ivar_thread;
|
34
35
|
extern ID ID_new;
|
@@ -39,9 +40,6 @@ extern ID ID_signal;
|
|
39
40
|
extern ID ID_size;
|
40
41
|
extern ID ID_switch_fiber;
|
41
42
|
extern ID ID_transfer;
|
42
|
-
extern ID ID_R;
|
43
|
-
extern ID ID_W;
|
44
|
-
extern ID ID_RW;
|
45
43
|
|
46
44
|
extern VALUE SYM_fiber_create;
|
47
45
|
extern VALUE SYM_fiber_ev_loop_enter;
|
@@ -65,43 +63,25 @@ enum {
|
|
65
63
|
GYRO_WATCHER_POST_FORK = 0xFF
|
66
64
|
};
|
67
65
|
|
68
|
-
VALUE
|
69
|
-
VALUE Fiber_auto_io(VALUE self);
|
66
|
+
VALUE Fiber_auto_watcher(VALUE self);
|
70
67
|
void Fiber_make_runnable(VALUE fiber, VALUE value);
|
71
68
|
|
72
|
-
VALUE
|
73
|
-
VALUE
|
74
|
-
|
75
|
-
VALUE
|
76
|
-
VALUE Gyro_IO_await(VALUE self);
|
77
|
-
|
78
|
-
void Gyro_Selector_add_active_watcher(VALUE self, VALUE watcher);
|
79
|
-
VALUE Gyro_Selector_break_out_of_ev_loop(VALUE self);
|
80
|
-
struct ev_loop *Gyro_Selector_current_thread_ev_loop();
|
81
|
-
struct ev_loop *Gyro_Selector_ev_loop(VALUE selector);
|
82
|
-
ev_tstamp Gyro_Selector_now(VALUE selector);
|
83
|
-
long Gyro_Selector_pending_count(VALUE self);
|
84
|
-
VALUE Gyro_Selector_post_fork(VALUE self);
|
85
|
-
void Gyro_Selector_remove_active_watcher(VALUE self, VALUE watcher);
|
86
|
-
VALUE Gyro_Selector_run(VALUE self, VALUE current_fiber);
|
87
|
-
void Gyro_Selector_run_no_wait(VALUE self, VALUE current_fiber, long runnable_count);
|
88
|
-
VALUE Gyro_switchpoint();
|
89
|
-
|
69
|
+
VALUE LibevAgent_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue);
|
70
|
+
VALUE LibevAgent_break(VALUE self);
|
71
|
+
VALUE LibevAgent_pending_count(VALUE self);
|
72
|
+
VALUE LibevAgent_wait_io(VALUE self, VALUE io, VALUE write);
|
90
73
|
|
91
|
-
VALUE
|
92
|
-
VALUE
|
74
|
+
VALUE LibevAgent_ref(VALUE self);
|
75
|
+
VALUE LibevAgent_unref(VALUE self);
|
76
|
+
int LibevAgent_ref_count(VALUE self);
|
77
|
+
void LibevAgent_reset_ref_count(VALUE self);
|
93
78
|
|
94
|
-
VALUE
|
95
|
-
VALUE IO_write_watcher(VALUE io);
|
79
|
+
VALUE Polyphony_snooze(VALUE self);
|
96
80
|
|
97
|
-
VALUE
|
81
|
+
VALUE Polyphony_Queue_push(VALUE self, VALUE value);
|
98
82
|
|
99
|
-
VALUE Thread_current_event_selector();
|
100
|
-
VALUE Thread_post_fork(VALUE thread);
|
101
|
-
VALUE Thread_ref(VALUE thread);
|
102
83
|
VALUE Thread_schedule_fiber(VALUE thread, VALUE fiber, VALUE value);
|
103
84
|
VALUE Thread_switch_fiber(VALUE thread);
|
104
|
-
VALUE Thread_unref(VALUE thread);
|
105
85
|
|
106
86
|
int io_setstrbuf(VALUE *str, long len);
|
107
87
|
void io_set_read_length(VALUE str, long n, int shrinkable);
|
@@ -0,0 +1,21 @@
|
|
1
|
+
#include "polyphony.h"
|
2
|
+
|
3
|
+
void Init_Fiber();
|
4
|
+
void Init_Polyphony();
|
5
|
+
void Init_LibevAgent();
|
6
|
+
void Init_LibevQueue();
|
7
|
+
void Init_Thread();
|
8
|
+
void Init_Tracing();
|
9
|
+
|
10
|
+
void Init_polyphony_ext() {
|
11
|
+
ev_set_allocator(xrealloc);
|
12
|
+
|
13
|
+
Init_Polyphony();
|
14
|
+
Init_LibevAgent();
|
15
|
+
Init_LibevQueue();
|
16
|
+
|
17
|
+
Init_Fiber();
|
18
|
+
Init_Thread();
|
19
|
+
|
20
|
+
Init_Tracing();
|
21
|
+
}
|
@@ -0,0 +1,200 @@
|
|
1
|
+
#include "polyphony.h"
|
2
|
+
|
3
|
+
ID ID_deactivate_all_watchers_post_fork;
|
4
|
+
ID ID_ivar_agent;
|
5
|
+
ID ID_ivar_join_wait_queue;
|
6
|
+
ID ID_ivar_main_fiber;
|
7
|
+
ID ID_ivar_result;
|
8
|
+
ID ID_ivar_terminated;
|
9
|
+
ID ID_run_queue;
|
10
|
+
ID ID_runnable_next;
|
11
|
+
ID ID_stop;
|
12
|
+
|
13
|
+
static VALUE Thread_setup_fiber_scheduling(VALUE self) {
|
14
|
+
VALUE queue;
|
15
|
+
|
16
|
+
rb_ivar_set(self, ID_ivar_main_fiber, rb_fiber_current());
|
17
|
+
queue = rb_ary_new();
|
18
|
+
rb_ivar_set(self, ID_run_queue, queue);
|
19
|
+
|
20
|
+
return self;
|
21
|
+
}
|
22
|
+
|
23
|
+
int Thread_fiber_ref_count(VALUE self) {
|
24
|
+
VALUE agent = rb_ivar_get(self, ID_ivar_agent);
|
25
|
+
return NUM2INT(LibevAgent_ref_count(agent));
|
26
|
+
}
|
27
|
+
|
28
|
+
inline void Thread_fiber_reset_ref_count(VALUE self) {
|
29
|
+
VALUE agent = rb_ivar_get(self, ID_ivar_agent);
|
30
|
+
LibevAgent_reset_ref_count(agent);
|
31
|
+
}
|
32
|
+
|
33
|
+
static VALUE SYM_scheduled_fibers;
|
34
|
+
static VALUE SYM_pending_watchers;
|
35
|
+
|
36
|
+
static VALUE Thread_fiber_scheduling_stats(VALUE self) {
|
37
|
+
VALUE agent = rb_ivar_get(self,ID_ivar_agent);
|
38
|
+
VALUE stats = rb_hash_new();
|
39
|
+
VALUE queue = rb_ivar_get(self, ID_run_queue);
|
40
|
+
long pending_count;
|
41
|
+
|
42
|
+
long scheduled_count = RARRAY_LEN(queue);
|
43
|
+
rb_hash_aset(stats, SYM_scheduled_fibers, INT2NUM(scheduled_count));
|
44
|
+
|
45
|
+
pending_count = LibevAgent_pending_count(agent);
|
46
|
+
rb_hash_aset(stats, SYM_pending_watchers, INT2NUM(pending_count));
|
47
|
+
|
48
|
+
return stats;
|
49
|
+
}
|
50
|
+
|
51
|
+
VALUE Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
|
52
|
+
VALUE queue;
|
53
|
+
|
54
|
+
if (rb_fiber_alive_p(fiber) != Qtrue) return self;
|
55
|
+
|
56
|
+
FIBER_TRACE(3, SYM_fiber_schedule, fiber, value);
|
57
|
+
// if fiber is already scheduled, just set the scheduled value, then return
|
58
|
+
rb_ivar_set(fiber, ID_runnable_value, value);
|
59
|
+
if (rb_ivar_get(fiber, ID_runnable) != Qnil) {
|
60
|
+
return self;
|
61
|
+
}
|
62
|
+
|
63
|
+
queue = rb_ivar_get(self, ID_run_queue);
|
64
|
+
rb_ary_push(queue, fiber);
|
65
|
+
rb_ivar_set(fiber, ID_runnable, Qtrue);
|
66
|
+
|
67
|
+
if (rb_thread_current() != self) {
|
68
|
+
// if the fiber scheduling is done across threads, we need to make sure the
|
69
|
+
// target thread is woken up in case it is in the middle of running its
|
70
|
+
// event selector. Otherwise it's gonna be stuck waiting for an event to
|
71
|
+
// happen, not knowing that it there's already a fiber ready to run in its
|
72
|
+
// run queue.
|
73
|
+
VALUE agent = rb_ivar_get(self,ID_ivar_agent);
|
74
|
+
LibevAgent_break(agent);
|
75
|
+
}
|
76
|
+
return self;
|
77
|
+
}
|
78
|
+
|
79
|
+
VALUE Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value) {
|
80
|
+
VALUE queue;
|
81
|
+
|
82
|
+
if (rb_fiber_alive_p(fiber) != Qtrue) return self;
|
83
|
+
|
84
|
+
FIBER_TRACE(3, SYM_fiber_schedule, fiber, value);
|
85
|
+
rb_ivar_set(fiber, ID_runnable_value, value);
|
86
|
+
|
87
|
+
queue = rb_ivar_get(self, ID_run_queue);
|
88
|
+
|
89
|
+
// if fiber is already scheduled, remove it from the run queue
|
90
|
+
if (rb_ivar_get(fiber, ID_runnable) != Qnil) {
|
91
|
+
rb_ary_delete(queue, fiber);
|
92
|
+
} else {
|
93
|
+
rb_ivar_set(fiber, ID_runnable, Qtrue);
|
94
|
+
}
|
95
|
+
|
96
|
+
// the fiber is given priority by putting it at the front of the run queue
|
97
|
+
rb_ary_unshift(queue, fiber);
|
98
|
+
|
99
|
+
if (rb_thread_current() != self) {
|
100
|
+
// if the fiber scheduling is done across threads, we need to make sure the
|
101
|
+
// target thread is woken up in case it is in the middle of running its
|
102
|
+
// event loop. Otherwise it's gonna be stuck waiting for an event to
|
103
|
+
// happen, not knowing that it there's already a fiber ready to run in its
|
104
|
+
// run queue.
|
105
|
+
VALUE agent = rb_ivar_get(self, ID_ivar_agent);
|
106
|
+
LibevAgent_break(agent);
|
107
|
+
}
|
108
|
+
return self;
|
109
|
+
}
|
110
|
+
|
111
|
+
VALUE Thread_switch_fiber(VALUE self) {
|
112
|
+
VALUE current_fiber = rb_fiber_current();
|
113
|
+
VALUE queue = rb_ivar_get(self, ID_run_queue);
|
114
|
+
VALUE next_fiber;
|
115
|
+
VALUE value;
|
116
|
+
VALUE agent = rb_ivar_get(self, ID_ivar_agent);
|
117
|
+
int ref_count;
|
118
|
+
|
119
|
+
if (__tracing_enabled__) {
|
120
|
+
if (rb_ivar_get(current_fiber, ID_ivar_running) != Qfalse) {
|
121
|
+
rb_funcall(rb_cObject, ID_fiber_trace, 2, SYM_fiber_switchpoint, current_fiber);
|
122
|
+
}
|
123
|
+
}
|
124
|
+
|
125
|
+
ref_count = LibevAgent_ref_count(agent);
|
126
|
+
while (1) {
|
127
|
+
next_fiber = rb_ary_shift(queue);
|
128
|
+
if (next_fiber != Qnil) {
|
129
|
+
if (ref_count > 0) {
|
130
|
+
// this mechanism prevents event starvation in case the run queue never
|
131
|
+
// empties
|
132
|
+
LibevAgent_poll(agent, Qtrue, current_fiber, queue);
|
133
|
+
}
|
134
|
+
break;
|
135
|
+
}
|
136
|
+
if (ref_count == 0) break;
|
137
|
+
|
138
|
+
LibevAgent_poll(agent, Qnil, current_fiber, queue);
|
139
|
+
}
|
140
|
+
|
141
|
+
if (next_fiber == Qnil) return Qnil;
|
142
|
+
|
143
|
+
// run next fiber
|
144
|
+
value = rb_ivar_get(next_fiber, ID_runnable_value);
|
145
|
+
FIBER_TRACE(3, SYM_fiber_run, next_fiber, value);
|
146
|
+
|
147
|
+
rb_ivar_set(next_fiber, ID_runnable, Qnil);
|
148
|
+
RB_GC_GUARD(next_fiber);
|
149
|
+
RB_GC_GUARD(value);
|
150
|
+
return (next_fiber == current_fiber) ?
|
151
|
+
value : rb_funcall(next_fiber, ID_transfer, 1, value);
|
152
|
+
}
|
153
|
+
|
154
|
+
VALUE Thread_reset_fiber_scheduling(VALUE self) {
|
155
|
+
VALUE queue = rb_ivar_get(self, ID_run_queue);
|
156
|
+
rb_ary_clear(queue);
|
157
|
+
Thread_fiber_reset_ref_count(self);
|
158
|
+
return self;
|
159
|
+
}
|
160
|
+
|
161
|
+
VALUE Thread_fiber_break_out_of_ev_loop(VALUE self, VALUE fiber, VALUE resume_obj) {
|
162
|
+
VALUE agent = rb_ivar_get(self, ID_ivar_agent);
|
163
|
+
if (fiber != Qnil) {
|
164
|
+
Thread_schedule_fiber_with_priority(self, fiber, resume_obj);
|
165
|
+
}
|
166
|
+
|
167
|
+
if (LibevAgent_break(agent) == Qnil) {
|
168
|
+
// we're not inside the ev_loop, so we just do a switchpoint
|
169
|
+
Thread_switch_fiber(self);
|
170
|
+
}
|
171
|
+
|
172
|
+
return self;
|
173
|
+
}
|
174
|
+
|
175
|
+
void Init_Thread() {
|
176
|
+
rb_define_method(rb_cThread, "setup_fiber_scheduling", Thread_setup_fiber_scheduling, 0);
|
177
|
+
rb_define_method(rb_cThread, "reset_fiber_scheduling", Thread_reset_fiber_scheduling, 0);
|
178
|
+
rb_define_method(rb_cThread, "fiber_scheduling_stats", Thread_fiber_scheduling_stats, 0);
|
179
|
+
rb_define_method(rb_cThread, "break_out_of_ev_loop", Thread_fiber_break_out_of_ev_loop, 2);
|
180
|
+
|
181
|
+
rb_define_method(rb_cThread, "schedule_fiber", Thread_schedule_fiber, 2);
|
182
|
+
rb_define_method(rb_cThread, "schedule_fiber_with_priority",
|
183
|
+
Thread_schedule_fiber_with_priority, 2);
|
184
|
+
rb_define_method(rb_cThread, "switch_fiber", Thread_switch_fiber, 0);
|
185
|
+
|
186
|
+
ID_deactivate_all_watchers_post_fork = rb_intern("deactivate_all_watchers_post_fork");
|
187
|
+
ID_ivar_agent = rb_intern("@agent");
|
188
|
+
ID_ivar_join_wait_queue = rb_intern("@join_wait_queue");
|
189
|
+
ID_ivar_main_fiber = rb_intern("@main_fiber");
|
190
|
+
ID_ivar_result = rb_intern("@result");
|
191
|
+
ID_ivar_terminated = rb_intern("@terminated");
|
192
|
+
ID_run_queue = rb_intern("run_queue");
|
193
|
+
ID_runnable_next = rb_intern("runnable_next");
|
194
|
+
ID_stop = rb_intern("stop");
|
195
|
+
|
196
|
+
SYM_scheduled_fibers = ID2SYM(rb_intern("scheduled_fibers"));
|
197
|
+
SYM_pending_watchers = ID2SYM(rb_intern("pending_watchers"));
|
198
|
+
rb_global_variable(&SYM_scheduled_fibers);
|
199
|
+
rb_global_variable(&SYM_pending_watchers);
|
200
|
+
}
|