polyphony 0.40 → 0.43.2
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 +29 -2
- data/Gemfile.lock +13 -10
- data/README.md +0 -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/adapters/redis_blpop.rb +12 -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/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 +15 -22
- data/ext/{gyro → polyphony}/libev.c +0 -0
- data/ext/{gyro → polyphony}/libev.h +0 -0
- data/ext/polyphony/libev_agent.c +725 -0
- data/ext/polyphony/libev_queue.c +217 -0
- data/ext/{gyro/gyro.c → polyphony/polyphony.c} +12 -37
- data/ext/polyphony/polyphony.h +90 -0
- data/ext/polyphony/polyphony_ext.c +21 -0
- data/ext/{gyro → polyphony}/thread.c +34 -151
- data/ext/{gyro → polyphony}/tracing.c +1 -1
- data/lib/polyphony.rb +19 -12
- 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/redis.rb +3 -2
- 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 +3 -3
- data/test/helper.rb +12 -1
- data/test/test_agent.rb +130 -0
- data/test/{test_async.rb → test_event.rb} +13 -7
- data/test/test_ext.rb +25 -4
- data/test/test_fiber.rb +19 -10
- data/test/test_global_api.rb +6 -6
- data/test/test_io.rb +46 -24
- data/test/test_queue.rb +74 -0
- data/test/test_signal.rb +3 -40
- data/test/test_socket.rb +34 -0
- data/test/test_thread.rb +37 -16
- data/test/test_trace.rb +6 -5
- metadata +39 -41
- 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 -132
- data/ext/gyro/child.c +0 -108
- data/ext/gyro/gyro.h +0 -158
- data/ext/gyro/gyro_ext.c +0 -33
- data/ext/gyro/io.c +0 -457
- data/ext/gyro/queue.c +0 -146
- data/ext/gyro/selector.c +0 -205
- data/ext/gyro/signal.c +0 -99
- data/ext/gyro/socket.c +0 -213
- data/ext/gyro/timer.c +0 -115
- data/test/test_timer.rb +0 -56
@@ -0,0 +1,217 @@
|
|
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
|
+
GetQueue(self, queue);
|
124
|
+
if (queue->shift_queue.count > 0) {
|
125
|
+
struct async_watcher *watcher = async_queue_pop(&queue->shift_queue);
|
126
|
+
if (watcher) {
|
127
|
+
ev_async_send(watcher->ev_loop, &watcher->async);
|
128
|
+
}
|
129
|
+
}
|
130
|
+
rb_ary_push(queue->items, value);
|
131
|
+
return self;
|
132
|
+
}
|
133
|
+
|
134
|
+
struct ev_loop *LibevAgent_ev_loop(VALUE self);
|
135
|
+
|
136
|
+
void async_queue_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, int revents) {
|
137
|
+
struct async_watcher *watcher = (struct async_watcher *)ev_async;
|
138
|
+
Fiber_make_runnable(watcher->fiber, Qnil);
|
139
|
+
}
|
140
|
+
|
141
|
+
VALUE libev_agent_await(VALUE self);
|
142
|
+
|
143
|
+
VALUE LibevQueue_shift(VALUE self) {
|
144
|
+
LibevQueue_t *queue;
|
145
|
+
GetQueue(self, queue);
|
146
|
+
|
147
|
+
if (RARRAY_LEN(queue->items) == 0) {
|
148
|
+
struct async_watcher watcher;
|
149
|
+
VALUE agent = rb_ivar_get(rb_thread_current(), ID_ivar_agent);
|
150
|
+
VALUE switchpoint_result = Qnil;
|
151
|
+
|
152
|
+
watcher.ev_loop = LibevAgent_ev_loop(agent);
|
153
|
+
watcher.fiber = rb_fiber_current();
|
154
|
+
async_queue_push(&queue->shift_queue, &watcher);
|
155
|
+
ev_async_init(&watcher.async, async_queue_callback);
|
156
|
+
ev_async_start(watcher.ev_loop, &watcher.async);
|
157
|
+
|
158
|
+
switchpoint_result = libev_agent_await(agent);
|
159
|
+
ev_async_stop(watcher.ev_loop, &watcher.async);
|
160
|
+
|
161
|
+
if (RTEST(rb_obj_is_kind_of(switchpoint_result, rb_eException))) {
|
162
|
+
async_queue_remove_by_fiber(&queue->shift_queue, watcher.fiber);
|
163
|
+
return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
|
164
|
+
}
|
165
|
+
RB_GC_GUARD(watcher.fiber);
|
166
|
+
RB_GC_GUARD(agent);
|
167
|
+
RB_GC_GUARD(switchpoint_result);
|
168
|
+
}
|
169
|
+
|
170
|
+
return rb_ary_shift(queue->items);
|
171
|
+
}
|
172
|
+
|
173
|
+
VALUE LibevQueue_shift_each(VALUE self) {
|
174
|
+
LibevQueue_t *queue;
|
175
|
+
VALUE old_queue;
|
176
|
+
GetQueue(self, queue);
|
177
|
+
old_queue = queue->items;
|
178
|
+
queue->items = rb_ary_new();
|
179
|
+
|
180
|
+
if (rb_block_given_p()) {
|
181
|
+
long len = RARRAY_LEN(old_queue);
|
182
|
+
long i;
|
183
|
+
for (i = 0; i < len; i++) {
|
184
|
+
rb_yield(RARRAY_AREF(old_queue, i));
|
185
|
+
}
|
186
|
+
RB_GC_GUARD(old_queue);
|
187
|
+
return self;
|
188
|
+
}
|
189
|
+
else {
|
190
|
+
RB_GC_GUARD(old_queue);
|
191
|
+
return old_queue;
|
192
|
+
}
|
193
|
+
}
|
194
|
+
|
195
|
+
VALUE LibevQueue_empty_p(VALUE self) {
|
196
|
+
LibevQueue_t *queue;
|
197
|
+
GetQueue(self, queue);
|
198
|
+
|
199
|
+
return (RARRAY_LEN(queue->items) == 0) ? Qtrue : Qfalse;
|
200
|
+
}
|
201
|
+
|
202
|
+
void Init_LibevQueue() {
|
203
|
+
cLibevQueue = rb_define_class_under(mPolyphony, "LibevQueue", rb_cData);
|
204
|
+
rb_define_alloc_func(cLibevQueue, LibevQueue_allocate);
|
205
|
+
|
206
|
+
rb_define_method(cLibevQueue, "initialize", LibevQueue_initialize, 0);
|
207
|
+
rb_define_method(cLibevQueue, "push", LibevQueue_push, 1);
|
208
|
+
rb_define_method(cLibevQueue, "<<", LibevQueue_push, 1);
|
209
|
+
|
210
|
+
rb_define_method(cLibevQueue, "pop", LibevQueue_shift, 0);
|
211
|
+
rb_define_method(cLibevQueue, "shift", LibevQueue_shift, 0);
|
212
|
+
|
213
|
+
rb_define_method(cLibevQueue, "shift_each", LibevQueue_shift_each, 0);
|
214
|
+
rb_define_method(cLibevQueue, "empty?", LibevQueue_empty_p, 0);
|
215
|
+
}
|
216
|
+
|
217
|
+
|
@@ -1,17 +1,14 @@
|
|
1
|
-
#include "
|
1
|
+
#include "polyphony.h"
|
2
2
|
|
3
|
-
VALUE
|
4
|
-
int __gyro_current_generation__ = 0;
|
3
|
+
VALUE mPolyphony;
|
5
4
|
|
5
|
+
ID ID_await_no_raise;
|
6
6
|
ID ID_call;
|
7
7
|
ID ID_caller;
|
8
8
|
ID ID_clear;
|
9
9
|
ID ID_each;
|
10
|
-
ID ID_empty;
|
11
10
|
ID ID_inspect;
|
12
11
|
ID ID_new;
|
13
|
-
ID ID_pop;
|
14
|
-
ID ID_push;
|
15
12
|
ID ID_raise;
|
16
13
|
ID ID_ivar_running;
|
17
14
|
ID ID_ivar_thread;
|
@@ -25,7 +22,7 @@ ID ID_R;
|
|
25
22
|
ID ID_W;
|
26
23
|
ID ID_RW;
|
27
24
|
|
28
|
-
VALUE
|
25
|
+
VALUE Polyphony_snooze(VALUE self) {
|
29
26
|
VALUE ret;
|
30
27
|
VALUE fiber = rb_fiber_current();
|
31
28
|
|
@@ -36,15 +33,7 @@ VALUE Gyro_snooze(VALUE self) {
|
|
36
33
|
return ret;
|
37
34
|
}
|
38
35
|
|
39
|
-
static VALUE
|
40
|
-
return Thread_ref(rb_thread_current());
|
41
|
-
}
|
42
|
-
|
43
|
-
static VALUE Gyro_unref(VALUE self) {
|
44
|
-
return Thread_unref(rb_thread_current());
|
45
|
-
}
|
46
|
-
|
47
|
-
static VALUE Gyro_suspend(VALUE self) {
|
36
|
+
static VALUE Polyphony_suspend(VALUE self) {
|
48
37
|
VALUE ret = Thread_switch_fiber(rb_thread_current());
|
49
38
|
|
50
39
|
TEST_RESUME_EXCEPTION(ret);
|
@@ -52,38 +41,28 @@ static VALUE Gyro_suspend(VALUE self) {
|
|
52
41
|
return ret;
|
53
42
|
}
|
54
43
|
|
55
|
-
VALUE
|
44
|
+
VALUE Polyphony_trace(VALUE self, VALUE enabled) {
|
56
45
|
__tracing_enabled__ = RTEST(enabled) ? 1 : 0;
|
57
46
|
return Qnil;
|
58
47
|
}
|
59
48
|
|
60
|
-
|
61
|
-
|
62
|
-
return Qnil;
|
63
|
-
}
|
49
|
+
void Init_Polyphony() {
|
50
|
+
mPolyphony = rb_define_module("Polyphony");
|
64
51
|
|
65
|
-
|
66
|
-
mGyro = rb_define_module("Gyro");
|
52
|
+
rb_define_singleton_method(mPolyphony, "trace", Polyphony_trace, 1);
|
67
53
|
|
68
|
-
|
69
|
-
|
70
|
-
rb_define_singleton_method(mGyro, "trace", Gyro_trace, 1);
|
71
|
-
rb_define_singleton_method(mGyro, "unref", Gyro_unref, 0);
|
72
|
-
|
73
|
-
rb_define_global_function("snooze", Gyro_snooze, 0);
|
74
|
-
rb_define_global_function("suspend", Gyro_suspend, 0);
|
54
|
+
rb_define_global_function("snooze", Polyphony_snooze, 0);
|
55
|
+
rb_define_global_function("suspend", Polyphony_suspend, 0);
|
75
56
|
|
57
|
+
ID_await_no_raise = rb_intern("await_no_raise");
|
76
58
|
ID_call = rb_intern("call");
|
77
59
|
ID_caller = rb_intern("caller");
|
78
60
|
ID_clear = rb_intern("clear");
|
79
61
|
ID_each = rb_intern("each");
|
80
|
-
ID_empty = rb_intern("empty?");
|
81
62
|
ID_inspect = rb_intern("inspect");
|
82
63
|
ID_ivar_running = rb_intern("@running");
|
83
64
|
ID_ivar_thread = rb_intern("@thread");
|
84
65
|
ID_new = rb_intern("new");
|
85
|
-
ID_pop = rb_intern("pop");
|
86
|
-
ID_push = rb_intern("push");
|
87
66
|
ID_raise = rb_intern("raise");
|
88
67
|
ID_runnable = rb_intern("runnable");
|
89
68
|
ID_runnable_value = rb_intern("runnable_value");
|
@@ -91,8 +70,4 @@ void Init_Gyro() {
|
|
91
70
|
ID_size = rb_intern("size");
|
92
71
|
ID_switch_fiber = rb_intern("switch_fiber");
|
93
72
|
ID_transfer = rb_intern("transfer");
|
94
|
-
|
95
|
-
ID_R = rb_intern("r");
|
96
|
-
ID_RW = rb_intern("rw");
|
97
|
-
ID_W = rb_intern("w");
|
98
73
|
}
|
@@ -0,0 +1,90 @@
|
|
1
|
+
#ifndef RUBY_EV_H
|
2
|
+
#define RUBY_EV_H
|
3
|
+
|
4
|
+
#include "ruby.h"
|
5
|
+
#include "ruby/io.h"
|
6
|
+
#include "libev.h"
|
7
|
+
|
8
|
+
// debugging
|
9
|
+
#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 FIBER_TRACE(...) if (__tracing_enabled__) { \
|
12
|
+
rb_funcall(rb_cObject, ID_fiber_trace, __VA_ARGS__); \
|
13
|
+
}
|
14
|
+
|
15
|
+
#define TEST_EXCEPTION(ret) (RTEST(rb_obj_is_kind_of(ret, rb_eException)))
|
16
|
+
|
17
|
+
#define TEST_RESUME_EXCEPTION(ret) if (RTEST(rb_obj_is_kind_of(ret, rb_eException))) { \
|
18
|
+
return rb_funcall(rb_mKernel, ID_raise, 1, ret); \
|
19
|
+
}
|
20
|
+
|
21
|
+
extern VALUE mPolyphony;
|
22
|
+
extern VALUE cLibevQueue;
|
23
|
+
extern VALUE cEvent;
|
24
|
+
|
25
|
+
extern ID ID_await_no_raise;
|
26
|
+
extern ID ID_call;
|
27
|
+
extern ID ID_caller;
|
28
|
+
extern ID ID_clear;
|
29
|
+
extern ID ID_each;
|
30
|
+
extern ID ID_fiber_trace;
|
31
|
+
extern ID ID_inspect;
|
32
|
+
extern ID ID_ivar_agent;
|
33
|
+
extern ID ID_ivar_running;
|
34
|
+
extern ID ID_ivar_thread;
|
35
|
+
extern ID ID_new;
|
36
|
+
extern ID ID_raise;
|
37
|
+
extern ID ID_runnable;
|
38
|
+
extern ID ID_runnable_value;
|
39
|
+
extern ID ID_signal;
|
40
|
+
extern ID ID_size;
|
41
|
+
extern ID ID_switch_fiber;
|
42
|
+
extern ID ID_transfer;
|
43
|
+
|
44
|
+
extern VALUE SYM_fiber_create;
|
45
|
+
extern VALUE SYM_fiber_ev_loop_enter;
|
46
|
+
extern VALUE SYM_fiber_ev_loop_leave;
|
47
|
+
extern VALUE SYM_fiber_run;
|
48
|
+
extern VALUE SYM_fiber_schedule;
|
49
|
+
extern VALUE SYM_fiber_switchpoint;
|
50
|
+
extern VALUE SYM_fiber_terminate;
|
51
|
+
|
52
|
+
extern int __tracing_enabled__;
|
53
|
+
|
54
|
+
enum {
|
55
|
+
FIBER_STATE_NOT_SCHEDULED = 0,
|
56
|
+
FIBER_STATE_WAITING = 1,
|
57
|
+
FIBER_STATE_SCHEDULED = 2
|
58
|
+
};
|
59
|
+
|
60
|
+
// watcher flags
|
61
|
+
enum {
|
62
|
+
// a watcher's active field will be set to this after fork
|
63
|
+
GYRO_WATCHER_POST_FORK = 0xFF
|
64
|
+
};
|
65
|
+
|
66
|
+
VALUE Fiber_auto_watcher(VALUE self);
|
67
|
+
void Fiber_make_runnable(VALUE fiber, VALUE value);
|
68
|
+
|
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);
|
73
|
+
|
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);
|
78
|
+
|
79
|
+
VALUE Polyphony_snooze(VALUE self);
|
80
|
+
|
81
|
+
VALUE Polyphony_Queue_push(VALUE self, VALUE value);
|
82
|
+
|
83
|
+
VALUE Thread_schedule_fiber(VALUE thread, VALUE fiber, VALUE value);
|
84
|
+
VALUE Thread_switch_fiber(VALUE thread);
|
85
|
+
|
86
|
+
int io_setstrbuf(VALUE *str, long len);
|
87
|
+
void io_set_read_length(VALUE str, long n, int shrinkable);
|
88
|
+
VALUE io_enc_str(VALUE str, rb_io_t *fptr);
|
89
|
+
|
90
|
+
#endif /* RUBY_EV_H */
|
@@ -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
|
+
}
|
@@ -1,115 +1,48 @@
|
|
1
|
-
#include "
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
static ID ID_ivar_main_fiber;
|
13
|
-
static ID ID_ivar_result;
|
14
|
-
static ID ID_ivar_terminated;
|
15
|
-
static ID ID_pop;
|
16
|
-
static ID ID_push;
|
17
|
-
static ID ID_run_queue;
|
18
|
-
static ID ID_runnable_next;
|
19
|
-
static ID ID_stop;
|
20
|
-
|
21
|
-
VALUE event_selector_factory_proc(RB_BLOCK_CALL_FUNC_ARGLIST(args, klass)) {
|
22
|
-
return rb_funcall(klass, ID_new, 1, rb_ary_entry(args, 0));
|
23
|
-
}
|
24
|
-
|
25
|
-
static VALUE Thread_event_selector_set_proc(VALUE self, VALUE proc) {
|
26
|
-
if (!rb_obj_is_proc(proc)) {
|
27
|
-
proc = rb_proc_new(event_selector_factory_proc, proc);
|
28
|
-
}
|
29
|
-
rb_ivar_set(self, ID_ivar_event_selector_proc, proc);
|
30
|
-
return self;
|
31
|
-
}
|
32
|
-
|
33
|
-
static VALUE Thread_create_event_selector(VALUE self, VALUE thread) {
|
34
|
-
VALUE selector_proc = rb_ivar_get(self, ID_ivar_event_selector_proc);
|
35
|
-
if (selector_proc == Qnil) {
|
36
|
-
rb_raise(rb_eRuntimeError, "No event_selector_proc defined");
|
37
|
-
}
|
38
|
-
|
39
|
-
return rb_funcall(selector_proc, ID_call, 1, thread);
|
40
|
-
}
|
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;
|
41
12
|
|
42
13
|
static VALUE Thread_setup_fiber_scheduling(VALUE self) {
|
43
14
|
VALUE queue;
|
44
|
-
VALUE selector;
|
45
15
|
|
46
16
|
rb_ivar_set(self, ID_ivar_main_fiber, rb_fiber_current());
|
47
|
-
rb_ivar_set(self, ID_fiber_ref_count, INT2NUM(0));
|
48
17
|
queue = rb_ary_new();
|
49
18
|
rb_ivar_set(self, ID_run_queue, queue);
|
50
|
-
selector = rb_funcall(rb_cThread, ID_create_event_selector, 1, self);
|
51
|
-
rb_ivar_set(self, ID_ivar_event_selector, selector);
|
52
|
-
|
53
|
-
return self;
|
54
|
-
}
|
55
|
-
|
56
|
-
static VALUE Thread_stop_event_selector(VALUE self) {
|
57
|
-
VALUE selector = rb_ivar_get(self, ID_ivar_event_selector);
|
58
|
-
if (selector != Qnil) {
|
59
|
-
rb_funcall(selector, ID_stop, 0);
|
60
|
-
}
|
61
|
-
// Nullify the selector in order to prevent running the
|
62
|
-
// selector after the thread is done running.
|
63
|
-
rb_ivar_set(self, ID_ivar_event_selector, Qnil);
|
64
|
-
|
65
|
-
return self;
|
66
|
-
}
|
67
|
-
|
68
|
-
static VALUE Thread_deactivate_all_watchers_post_fork(VALUE self) {
|
69
|
-
VALUE selector = rb_ivar_get(self, ID_ivar_event_selector);
|
70
|
-
if (selector != Qnil) {
|
71
|
-
rb_funcall(selector, ID_deactivate_all_watchers_post_fork, 0);
|
72
|
-
}
|
73
|
-
|
74
|
-
return self;
|
75
|
-
}
|
76
19
|
|
77
|
-
VALUE Thread_ref(VALUE self) {
|
78
|
-
VALUE count = rb_ivar_get(self, ID_fiber_ref_count);
|
79
|
-
int new_count = NUM2INT(count) + 1;
|
80
|
-
rb_ivar_set(self, ID_fiber_ref_count, INT2NUM(new_count));
|
81
|
-
return self;
|
82
|
-
}
|
83
|
-
|
84
|
-
VALUE Thread_unref(VALUE self) {
|
85
|
-
VALUE count = rb_ivar_get(self, ID_fiber_ref_count);
|
86
|
-
int new_count = NUM2INT(count) - 1;
|
87
|
-
rb_ivar_set(self, ID_fiber_ref_count, INT2NUM(new_count));
|
88
20
|
return self;
|
89
21
|
}
|
90
22
|
|
91
23
|
int Thread_fiber_ref_count(VALUE self) {
|
92
|
-
VALUE
|
93
|
-
return NUM2INT(
|
24
|
+
VALUE agent = rb_ivar_get(self, ID_ivar_agent);
|
25
|
+
return NUM2INT(LibevAgent_ref_count(agent));
|
94
26
|
}
|
95
27
|
|
96
|
-
void Thread_fiber_reset_ref_count(VALUE self) {
|
97
|
-
|
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);
|
98
31
|
}
|
99
32
|
|
100
33
|
static VALUE SYM_scheduled_fibers;
|
101
34
|
static VALUE SYM_pending_watchers;
|
102
35
|
|
103
36
|
static VALUE Thread_fiber_scheduling_stats(VALUE self) {
|
37
|
+
VALUE agent = rb_ivar_get(self,ID_ivar_agent);
|
104
38
|
VALUE stats = rb_hash_new();
|
105
39
|
VALUE queue = rb_ivar_get(self, ID_run_queue);
|
106
|
-
VALUE selector = rb_ivar_get(self, ID_ivar_event_selector);
|
107
40
|
long pending_count;
|
108
41
|
|
109
42
|
long scheduled_count = RARRAY_LEN(queue);
|
110
43
|
rb_hash_aset(stats, SYM_scheduled_fibers, INT2NUM(scheduled_count));
|
111
44
|
|
112
|
-
pending_count =
|
45
|
+
pending_count = LibevAgent_pending_count(agent);
|
113
46
|
rb_hash_aset(stats, SYM_pending_watchers, INT2NUM(pending_count));
|
114
47
|
|
115
48
|
return stats;
|
@@ -137,10 +70,8 @@ VALUE Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
|
|
137
70
|
// event selector. Otherwise it's gonna be stuck waiting for an event to
|
138
71
|
// happen, not knowing that it there's already a fiber ready to run in its
|
139
72
|
// run queue.
|
140
|
-
VALUE
|
141
|
-
|
142
|
-
Gyro_Selector_break_out_of_ev_loop(selector);
|
143
|
-
}
|
73
|
+
VALUE agent = rb_ivar_get(self,ID_ivar_agent);
|
74
|
+
LibevAgent_break(agent);
|
144
75
|
}
|
145
76
|
return self;
|
146
77
|
}
|
@@ -168,13 +99,11 @@ VALUE Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value)
|
|
168
99
|
if (rb_thread_current() != self) {
|
169
100
|
// if the fiber scheduling is done across threads, we need to make sure the
|
170
101
|
// target thread is woken up in case it is in the middle of running its
|
171
|
-
// event
|
102
|
+
// event loop. Otherwise it's gonna be stuck waiting for an event to
|
172
103
|
// happen, not knowing that it there's already a fiber ready to run in its
|
173
104
|
// run queue.
|
174
|
-
VALUE
|
175
|
-
|
176
|
-
Gyro_Selector_break_out_of_ev_loop(selector);
|
177
|
-
}
|
105
|
+
VALUE agent = rb_ivar_get(self, ID_ivar_agent);
|
106
|
+
LibevAgent_break(agent);
|
178
107
|
}
|
179
108
|
return self;
|
180
109
|
}
|
@@ -182,9 +111,9 @@ VALUE Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value)
|
|
182
111
|
VALUE Thread_switch_fiber(VALUE self) {
|
183
112
|
VALUE current_fiber = rb_fiber_current();
|
184
113
|
VALUE queue = rb_ivar_get(self, ID_run_queue);
|
185
|
-
VALUE selector = rb_ivar_get(self, ID_ivar_event_selector);
|
186
114
|
VALUE next_fiber;
|
187
115
|
VALUE value;
|
116
|
+
VALUE agent = rb_ivar_get(self, ID_ivar_agent);
|
188
117
|
int ref_count;
|
189
118
|
|
190
119
|
if (__tracing_enabled__) {
|
@@ -193,31 +122,23 @@ VALUE Thread_switch_fiber(VALUE self) {
|
|
193
122
|
}
|
194
123
|
}
|
195
124
|
|
196
|
-
|
125
|
+
ref_count = LibevAgent_ref_count(agent);
|
197
126
|
while (1) {
|
198
127
|
next_fiber = rb_ary_shift(queue);
|
199
|
-
// if (break_flag != 0) {
|
200
|
-
// return Qnil;
|
201
|
-
// }
|
202
|
-
ref_count = Thread_fiber_ref_count(self);
|
203
128
|
if (next_fiber != Qnil) {
|
204
129
|
if (ref_count > 0) {
|
205
130
|
// this mechanism prevents event starvation in case the run queue never
|
206
131
|
// empties
|
207
|
-
|
132
|
+
LibevAgent_poll(agent, Qtrue, current_fiber, queue);
|
208
133
|
}
|
209
134
|
break;
|
210
135
|
}
|
211
|
-
if (ref_count == 0)
|
212
|
-
break;
|
213
|
-
}
|
136
|
+
if (ref_count == 0) break;
|
214
137
|
|
215
|
-
|
138
|
+
LibevAgent_poll(agent, Qnil, current_fiber, queue);
|
216
139
|
}
|
217
140
|
|
218
|
-
if (next_fiber == Qnil)
|
219
|
-
return Qnil;
|
220
|
-
}
|
141
|
+
if (next_fiber == Qnil) return Qnil;
|
221
142
|
|
222
143
|
// run next fiber
|
223
144
|
value = rb_ivar_get(next_fiber, ID_runnable_value);
|
@@ -226,7 +147,8 @@ VALUE Thread_switch_fiber(VALUE self) {
|
|
226
147
|
rb_ivar_set(next_fiber, ID_runnable, Qnil);
|
227
148
|
RB_GC_GUARD(next_fiber);
|
228
149
|
RB_GC_GUARD(value);
|
229
|
-
return
|
150
|
+
return (next_fiber == current_fiber) ?
|
151
|
+
value : rb_funcall(next_fiber, ID_transfer, 1, value);
|
230
152
|
}
|
231
153
|
|
232
154
|
VALUE Thread_reset_fiber_scheduling(VALUE self) {
|
@@ -236,34 +158,13 @@ VALUE Thread_reset_fiber_scheduling(VALUE self) {
|
|
236
158
|
return self;
|
237
159
|
}
|
238
160
|
|
239
|
-
VALUE Thread_post_fork(VALUE self) {
|
240
|
-
VALUE selector = rb_ivar_get(self, ID_ivar_event_selector);
|
241
|
-
Gyro_Selector_post_fork(selector);
|
242
|
-
|
243
|
-
return self;
|
244
|
-
}
|
245
|
-
|
246
|
-
VALUE Gyro_switchpoint() {
|
247
|
-
VALUE ret;
|
248
|
-
VALUE thread = rb_thread_current();
|
249
|
-
Thread_ref(thread);
|
250
|
-
ret = Thread_switch_fiber(thread);
|
251
|
-
Thread_unref(thread);
|
252
|
-
RB_GC_GUARD(ret);
|
253
|
-
return ret;
|
254
|
-
}
|
255
|
-
|
256
|
-
VALUE Thread_current_event_selector() {
|
257
|
-
return rb_ivar_get(rb_thread_current(), ID_ivar_event_selector);
|
258
|
-
}
|
259
|
-
|
260
161
|
VALUE Thread_fiber_break_out_of_ev_loop(VALUE self, VALUE fiber, VALUE resume_obj) {
|
261
|
-
VALUE
|
162
|
+
VALUE agent = rb_ivar_get(self, ID_ivar_agent);
|
262
163
|
if (fiber != Qnil) {
|
263
164
|
Thread_schedule_fiber_with_priority(self, fiber, resume_obj);
|
264
165
|
}
|
265
166
|
|
266
|
-
if (
|
167
|
+
if (LibevAgent_break(agent) == Qnil) {
|
267
168
|
// we're not inside the ev_loop, so we just do a switchpoint
|
268
169
|
Thread_switch_fiber(self);
|
269
170
|
}
|
@@ -272,19 +173,7 @@ VALUE Thread_fiber_break_out_of_ev_loop(VALUE self, VALUE fiber, VALUE resume_ob
|
|
272
173
|
}
|
273
174
|
|
274
175
|
void Init_Thread() {
|
275
|
-
cQueue = rb_const_get(rb_cObject, rb_intern("Queue"));
|
276
|
-
|
277
|
-
rb_define_singleton_method(rb_cThread, "event_selector=", Thread_event_selector_set_proc, 1);
|
278
|
-
rb_define_singleton_method(rb_cThread, "create_event_selector", Thread_create_event_selector, 1);
|
279
|
-
|
280
|
-
rb_define_method(rb_cThread, "fiber_ref", Thread_ref, 0);
|
281
|
-
rb_define_method(rb_cThread, "fiber_unref", Thread_unref, 0);
|
282
|
-
|
283
|
-
rb_define_method(rb_cThread, "post_fork", Thread_post_fork, 0);
|
284
|
-
|
285
176
|
rb_define_method(rb_cThread, "setup_fiber_scheduling", Thread_setup_fiber_scheduling, 0);
|
286
|
-
rb_define_method(rb_cThread, "stop_event_selector", Thread_stop_event_selector, 0);
|
287
|
-
rb_define_method(rb_cThread, "deactivate_all_watchers_post_fork", Thread_deactivate_all_watchers_post_fork, 0);
|
288
177
|
rb_define_method(rb_cThread, "reset_fiber_scheduling", Thread_reset_fiber_scheduling, 0);
|
289
178
|
rb_define_method(rb_cThread, "fiber_scheduling_stats", Thread_fiber_scheduling_stats, 0);
|
290
179
|
rb_define_method(rb_cThread, "break_out_of_ev_loop", Thread_fiber_break_out_of_ev_loop, 2);
|
@@ -294,18 +183,12 @@ void Init_Thread() {
|
|
294
183
|
Thread_schedule_fiber_with_priority, 2);
|
295
184
|
rb_define_method(rb_cThread, "switch_fiber", Thread_switch_fiber, 0);
|
296
185
|
|
297
|
-
ID_create_event_selector = rb_intern("create_event_selector");
|
298
186
|
ID_deactivate_all_watchers_post_fork = rb_intern("deactivate_all_watchers_post_fork");
|
299
|
-
|
300
|
-
ID_fiber_ref_count = rb_intern("fiber_ref_count");
|
301
|
-
ID_ivar_event_selector = rb_intern("@event_selector");
|
302
|
-
ID_ivar_event_selector_proc = rb_intern("@event_selector_proc");
|
187
|
+
ID_ivar_agent = rb_intern("@agent");
|
303
188
|
ID_ivar_join_wait_queue = rb_intern("@join_wait_queue");
|
304
189
|
ID_ivar_main_fiber = rb_intern("@main_fiber");
|
305
190
|
ID_ivar_result = rb_intern("@result");
|
306
191
|
ID_ivar_terminated = rb_intern("@terminated");
|
307
|
-
ID_pop = rb_intern("pop");
|
308
|
-
ID_push = rb_intern("push");
|
309
192
|
ID_run_queue = rb_intern("run_queue");
|
310
193
|
ID_runnable_next = rb_intern("runnable_next");
|
311
194
|
ID_stop = rb_intern("stop");
|