polyphony 0.36 → 0.42
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 +28 -2
- data/Gemfile +0 -11
- data/Gemfile.lock +15 -14
- data/README.md +2 -1
- data/Rakefile +7 -3
- data/TODO.md +28 -95
- data/docs/_config.yml +56 -7
- data/docs/_sass/custom/custom.scss +0 -30
- 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/getting-started/index.md +10 -0
- data/docs/getting-started/installing.md +2 -6
- data/docs/getting-started/overview.md +507 -0
- data/docs/getting-started/tutorial.md +27 -19
- data/docs/index.md +3 -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/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-fork-cleanup.rb +22 -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 +18 -22
- 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/{gyro/gyro.c → polyphony/polyphony.c} +16 -46
- data/ext/{gyro/gyro.h → polyphony/polyphony.h} +25 -39
- data/ext/polyphony/polyphony_ext.c +23 -0
- data/ext/{gyro → polyphony}/socket.c +21 -18
- data/ext/polyphony/thread.c +206 -0
- data/ext/{gyro → polyphony}/tracing.c +1 -1
- data/lib/polyphony.rb +40 -44
- data/lib/polyphony/adapters/fs.rb +1 -4
- data/lib/polyphony/adapters/irb.rb +1 -1
- data/lib/polyphony/adapters/postgres.rb +6 -5
- data/lib/polyphony/adapters/process.rb +27 -23
- data/lib/polyphony/adapters/trace.rb +110 -105
- data/lib/polyphony/core/channel.rb +35 -35
- data/lib/polyphony/core/exceptions.rb +29 -29
- data/lib/polyphony/core/global_api.rb +94 -91
- data/lib/polyphony/core/resource_pool.rb +83 -83
- data/lib/polyphony/core/sync.rb +16 -16
- data/lib/polyphony/core/thread_pool.rb +49 -37
- data/lib/polyphony/core/throttler.rb +30 -23
- data/lib/polyphony/event.rb +27 -0
- data/lib/polyphony/extensions/core.rb +25 -17
- data/lib/polyphony/extensions/fiber.rb +269 -267
- data/lib/polyphony/extensions/io.rb +56 -26
- data/lib/polyphony/extensions/openssl.rb +5 -9
- data/lib/polyphony/extensions/socket.rb +29 -10
- data/lib/polyphony/extensions/thread.rb +19 -12
- data/lib/polyphony/net.rb +64 -60
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +4 -7
- data/test/helper.rb +14 -1
- data/test/stress.rb +17 -12
- 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 +4 -4
- 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 +33 -0
- data/test/test_thread.rb +38 -16
- data/test/test_thread_pool.rb +2 -2
- data/test/test_throttler.rb +0 -1
- data/test/test_trace.rb +6 -5
- metadata +41 -57
- 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 -148
- data/ext/gyro/child.c +0 -127
- data/ext/gyro/gyro_ext.c +0 -33
- data/ext/gyro/io.c +0 -474
- data/ext/gyro/queue.c +0 -142
- data/ext/gyro/selector.c +0 -205
- data/ext/gyro/signal.c +0 -118
- data/ext/gyro/thread.c +0 -298
- data/ext/gyro/timer.c +0 -134
- data/test/test_timer.rb +0 -56
data/ext/gyro/thread.c
DELETED
@@ -1,298 +0,0 @@
|
|
1
|
-
#include "gyro.h"
|
2
|
-
|
3
|
-
static VALUE cQueue;
|
4
|
-
|
5
|
-
static ID ID_create_event_selector;
|
6
|
-
static ID ID_empty;
|
7
|
-
static ID ID_fiber_ref_count;
|
8
|
-
static ID ID_ivar_event_selector_proc;
|
9
|
-
static ID ID_ivar_event_selector;
|
10
|
-
static ID ID_ivar_join_wait_queue;
|
11
|
-
static ID ID_ivar_main_fiber;
|
12
|
-
static ID ID_ivar_result;
|
13
|
-
static ID ID_ivar_terminated;
|
14
|
-
static ID ID_pop;
|
15
|
-
static ID ID_push;
|
16
|
-
static ID ID_run_queue;
|
17
|
-
// static ID ID_run_queue_head;
|
18
|
-
// static ID ID_run_queue_tail;
|
19
|
-
static ID ID_runnable_next;
|
20
|
-
static ID ID_stop;
|
21
|
-
|
22
|
-
VALUE event_selector_factory_proc(RB_BLOCK_CALL_FUNC_ARGLIST(args, klass)) {
|
23
|
-
return rb_funcall(klass, ID_new, 1, rb_ary_entry(args, 0));
|
24
|
-
}
|
25
|
-
|
26
|
-
static VALUE Thread_event_selector_set_proc(VALUE self, VALUE proc) {
|
27
|
-
if (!rb_obj_is_proc(proc)) {
|
28
|
-
proc = rb_proc_new(event_selector_factory_proc, proc);
|
29
|
-
}
|
30
|
-
rb_ivar_set(self, ID_ivar_event_selector_proc, proc);
|
31
|
-
return self;
|
32
|
-
}
|
33
|
-
|
34
|
-
static VALUE Thread_create_event_selector(VALUE self, VALUE thread) {
|
35
|
-
VALUE selector_proc = rb_ivar_get(self, ID_ivar_event_selector_proc);
|
36
|
-
if (selector_proc == Qnil) {
|
37
|
-
rb_raise(rb_eRuntimeError, "No event_selector_proc defined");
|
38
|
-
}
|
39
|
-
|
40
|
-
return rb_funcall(selector_proc, ID_call, 1, thread);
|
41
|
-
}
|
42
|
-
|
43
|
-
static VALUE Thread_setup_fiber_scheduling(VALUE self) {
|
44
|
-
rb_ivar_set(self, ID_ivar_main_fiber, rb_fiber_current());
|
45
|
-
rb_ivar_set(self, ID_fiber_ref_count, INT2NUM(0));
|
46
|
-
VALUE queue = rb_ary_new();
|
47
|
-
rb_ivar_set(self, ID_run_queue, queue);
|
48
|
-
VALUE selector = rb_funcall(rb_cThread, ID_create_event_selector, 1, self);
|
49
|
-
rb_ivar_set(self, ID_ivar_event_selector, selector);
|
50
|
-
|
51
|
-
return self;
|
52
|
-
}
|
53
|
-
|
54
|
-
static VALUE Thread_stop_event_selector(VALUE self) {
|
55
|
-
VALUE selector = rb_ivar_get(self, ID_ivar_event_selector);
|
56
|
-
if (selector != Qnil) {
|
57
|
-
rb_funcall(selector, ID_stop, 0);
|
58
|
-
}
|
59
|
-
// Nullify the selector in order to prevent running the
|
60
|
-
// selector after the thread is done running.
|
61
|
-
rb_ivar_set(self, ID_ivar_event_selector, Qnil);
|
62
|
-
|
63
|
-
return self;
|
64
|
-
}
|
65
|
-
|
66
|
-
VALUE Thread_ref(VALUE self) {
|
67
|
-
VALUE count = rb_ivar_get(self, ID_fiber_ref_count);
|
68
|
-
int new_count = NUM2INT(count) + 1;
|
69
|
-
rb_ivar_set(self, ID_fiber_ref_count, INT2NUM(new_count));
|
70
|
-
return self;
|
71
|
-
}
|
72
|
-
|
73
|
-
VALUE Thread_unref(VALUE self) {
|
74
|
-
VALUE count = rb_ivar_get(self, ID_fiber_ref_count);
|
75
|
-
int new_count = NUM2INT(count) - 1;
|
76
|
-
rb_ivar_set(self, ID_fiber_ref_count, INT2NUM(new_count));
|
77
|
-
return self;
|
78
|
-
}
|
79
|
-
|
80
|
-
int Thread_fiber_ref_count(VALUE self) {
|
81
|
-
VALUE count = rb_ivar_get(self, ID_fiber_ref_count);
|
82
|
-
return NUM2INT(count);
|
83
|
-
}
|
84
|
-
|
85
|
-
void Thread_fiber_reset_ref_count(VALUE self) {
|
86
|
-
rb_ivar_set(self, ID_fiber_ref_count, INT2NUM(0));
|
87
|
-
}
|
88
|
-
|
89
|
-
static VALUE SYM_scheduled_fibers;
|
90
|
-
static VALUE SYM_pending_watchers;
|
91
|
-
|
92
|
-
static VALUE Thread_fiber_scheduling_stats(VALUE self) {
|
93
|
-
VALUE stats = rb_hash_new();
|
94
|
-
VALUE queue = rb_ivar_get(self, ID_run_queue);
|
95
|
-
VALUE selector = rb_ivar_get(self, ID_ivar_event_selector);
|
96
|
-
|
97
|
-
long scheduled_count = RARRAY_LEN(queue);
|
98
|
-
rb_hash_aset(stats, SYM_scheduled_fibers, INT2NUM(scheduled_count));
|
99
|
-
|
100
|
-
long pending_count = Gyro_Selector_pending_count(selector);
|
101
|
-
rb_hash_aset(stats, SYM_pending_watchers, INT2NUM(pending_count));
|
102
|
-
|
103
|
-
return stats;
|
104
|
-
}
|
105
|
-
|
106
|
-
VALUE Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
|
107
|
-
if (rb_fiber_alive_p(fiber) != Qtrue) {
|
108
|
-
return self;
|
109
|
-
}
|
110
|
-
|
111
|
-
FIBER_TRACE(3, SYM_fiber_schedule, fiber, value);
|
112
|
-
// if fiber is already scheduled, just set the scheduled value, then return
|
113
|
-
rb_ivar_set(fiber, ID_runnable_value, value);
|
114
|
-
if (rb_ivar_get(fiber, ID_runnable) != Qnil) {
|
115
|
-
return self;
|
116
|
-
}
|
117
|
-
|
118
|
-
VALUE queue = rb_ivar_get(self, ID_run_queue);
|
119
|
-
rb_ary_push(queue, fiber);
|
120
|
-
rb_ivar_set(fiber, ID_runnable, Qtrue);
|
121
|
-
|
122
|
-
if (rb_thread_current() != self) {
|
123
|
-
// if the fiber scheduling is done across threads, we need to make sure the
|
124
|
-
// target thread is woken up in case it is in the middle of running its
|
125
|
-
// event selector. Otherwise it's gonna be stuck waiting for an event to
|
126
|
-
// happen, not knowing that it there's already a fiber ready to run in its
|
127
|
-
// run queue.
|
128
|
-
VALUE selector = rb_ivar_get(self, ID_ivar_event_selector);
|
129
|
-
if (selector != Qnil) {
|
130
|
-
Gyro_Selector_break_out_of_ev_loop(selector);
|
131
|
-
}
|
132
|
-
}
|
133
|
-
return self;
|
134
|
-
}
|
135
|
-
|
136
|
-
VALUE Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value) {
|
137
|
-
if (rb_fiber_alive_p(fiber) != Qtrue) {
|
138
|
-
return self;
|
139
|
-
}
|
140
|
-
FIBER_TRACE(3, SYM_fiber_schedule, fiber, value);
|
141
|
-
rb_ivar_set(fiber, ID_runnable_value, value);
|
142
|
-
|
143
|
-
VALUE queue = rb_ivar_get(self, ID_run_queue);
|
144
|
-
|
145
|
-
// if fiber is already scheduled, remove it from the run queue
|
146
|
-
if (rb_ivar_get(fiber, ID_runnable) != Qnil) {
|
147
|
-
rb_ary_delete(queue, fiber);
|
148
|
-
} else {
|
149
|
-
rb_ivar_set(fiber, ID_runnable, Qtrue);
|
150
|
-
}
|
151
|
-
|
152
|
-
// the fiber is given priority by putting it at the front of the run queue
|
153
|
-
rb_ary_unshift(queue, fiber);
|
154
|
-
|
155
|
-
if (rb_thread_current() != self) {
|
156
|
-
// if the fiber scheduling is done across threads, we need to make sure the
|
157
|
-
// target thread is woken up in case it is in the middle of running its
|
158
|
-
// event selector. Otherwise it's gonna be stuck waiting for an event to
|
159
|
-
// happen, not knowing that it there's already a fiber ready to run in its
|
160
|
-
// run queue.
|
161
|
-
VALUE selector = rb_ivar_get(self, ID_ivar_event_selector);
|
162
|
-
if (selector != Qnil) {
|
163
|
-
Gyro_Selector_break_out_of_ev_loop(selector);
|
164
|
-
}
|
165
|
-
}
|
166
|
-
return self;
|
167
|
-
}
|
168
|
-
|
169
|
-
VALUE Thread_switch_fiber(VALUE self) {
|
170
|
-
VALUE current_fiber = rb_fiber_current();
|
171
|
-
if (__tracing_enabled__) {
|
172
|
-
if (rb_ivar_get(current_fiber, ID_ivar_running) != Qfalse) {
|
173
|
-
rb_funcall(rb_cObject, ID_fiber_trace, 2, SYM_fiber_switchpoint, current_fiber);
|
174
|
-
}
|
175
|
-
}
|
176
|
-
VALUE queue = rb_ivar_get(self, ID_run_queue);
|
177
|
-
VALUE selector = rb_ivar_get(self, ID_ivar_event_selector);
|
178
|
-
|
179
|
-
VALUE next_fiber;
|
180
|
-
|
181
|
-
while (1) {
|
182
|
-
next_fiber = rb_ary_shift(queue);
|
183
|
-
// if (break_flag != 0) {
|
184
|
-
// return Qnil;
|
185
|
-
// }
|
186
|
-
int ref_count = Thread_fiber_ref_count(self);
|
187
|
-
if (next_fiber != Qnil) {
|
188
|
-
if (ref_count > 0) {
|
189
|
-
// this mechanism prevents event starvation in case the run queue never
|
190
|
-
// empties
|
191
|
-
Gyro_Selector_run_no_wait(selector, current_fiber, RARRAY_LEN(queue));
|
192
|
-
}
|
193
|
-
break;
|
194
|
-
}
|
195
|
-
if (ref_count == 0) {
|
196
|
-
break;
|
197
|
-
}
|
198
|
-
|
199
|
-
Gyro_Selector_run(selector, current_fiber);
|
200
|
-
}
|
201
|
-
|
202
|
-
if (next_fiber == Qnil) {
|
203
|
-
return Qnil;
|
204
|
-
}
|
205
|
-
|
206
|
-
// run next fiber
|
207
|
-
VALUE value = rb_ivar_get(next_fiber, ID_runnable_value);
|
208
|
-
FIBER_TRACE(3, SYM_fiber_run, next_fiber, value);
|
209
|
-
|
210
|
-
rb_ivar_set(next_fiber, ID_runnable, Qnil);
|
211
|
-
RB_GC_GUARD(next_fiber);
|
212
|
-
RB_GC_GUARD(value);
|
213
|
-
return rb_funcall(next_fiber, ID_transfer, 1, value);
|
214
|
-
}
|
215
|
-
|
216
|
-
VALUE Thread_reset_fiber_scheduling(VALUE self) {
|
217
|
-
VALUE queue = rb_ivar_get(self, ID_run_queue);
|
218
|
-
rb_ary_clear(queue);
|
219
|
-
Thread_fiber_reset_ref_count(self);
|
220
|
-
return self;
|
221
|
-
}
|
222
|
-
|
223
|
-
VALUE Thread_post_fork(VALUE self) {
|
224
|
-
VALUE selector = rb_ivar_get(self, ID_ivar_event_selector);
|
225
|
-
Gyro_Selector_post_fork(selector);
|
226
|
-
|
227
|
-
return self;
|
228
|
-
}
|
229
|
-
|
230
|
-
VALUE Gyro_switchpoint() {
|
231
|
-
VALUE thread = rb_thread_current();
|
232
|
-
Thread_ref(thread);
|
233
|
-
VALUE ret = Thread_switch_fiber(thread);
|
234
|
-
Thread_unref(thread);
|
235
|
-
RB_GC_GUARD(ret);
|
236
|
-
return ret;
|
237
|
-
}
|
238
|
-
|
239
|
-
VALUE Thread_current_event_selector() {
|
240
|
-
return rb_ivar_get(rb_thread_current(), ID_ivar_event_selector);
|
241
|
-
}
|
242
|
-
|
243
|
-
VALUE Thread_fiber_break_out_of_ev_loop(VALUE self, VALUE fiber, VALUE resume_obj) {
|
244
|
-
VALUE selector = rb_ivar_get(self, ID_ivar_event_selector);
|
245
|
-
if (fiber != Qnil) {
|
246
|
-
Thread_schedule_fiber_with_priority(self, fiber, resume_obj);
|
247
|
-
}
|
248
|
-
|
249
|
-
if (Gyro_Selector_break_out_of_ev_loop(selector) == Qnil) {
|
250
|
-
// we're not inside the ev_loop, so we just do a switchpoint
|
251
|
-
Thread_switch_fiber(self);
|
252
|
-
}
|
253
|
-
|
254
|
-
return self;
|
255
|
-
}
|
256
|
-
|
257
|
-
void Init_Thread() {
|
258
|
-
cQueue = rb_const_get(rb_cObject, rb_intern("Queue"));
|
259
|
-
|
260
|
-
rb_define_singleton_method(rb_cThread, "event_selector=", Thread_event_selector_set_proc, 1);
|
261
|
-
rb_define_singleton_method(rb_cThread, "create_event_selector", Thread_create_event_selector, 1);
|
262
|
-
|
263
|
-
rb_define_method(rb_cThread, "fiber_ref", Thread_ref, 0);
|
264
|
-
rb_define_method(rb_cThread, "fiber_unref", Thread_unref, 0);
|
265
|
-
|
266
|
-
rb_define_method(rb_cThread, "post_fork", Thread_post_fork, 0);
|
267
|
-
|
268
|
-
rb_define_method(rb_cThread, "setup_fiber_scheduling", Thread_setup_fiber_scheduling, 0);
|
269
|
-
rb_define_method(rb_cThread, "stop_event_selector", Thread_stop_event_selector, 0);
|
270
|
-
rb_define_method(rb_cThread, "reset_fiber_scheduling", Thread_reset_fiber_scheduling, 0);
|
271
|
-
rb_define_method(rb_cThread, "fiber_scheduling_stats", Thread_fiber_scheduling_stats, 0);
|
272
|
-
rb_define_method(rb_cThread, "break_out_of_ev_loop", Thread_fiber_break_out_of_ev_loop, 2);
|
273
|
-
|
274
|
-
rb_define_method(rb_cThread, "schedule_fiber", Thread_schedule_fiber, 2);
|
275
|
-
rb_define_method(rb_cThread, "schedule_fiber_with_priority",
|
276
|
-
Thread_schedule_fiber_with_priority, 2);
|
277
|
-
rb_define_method(rb_cThread, "switch_fiber", Thread_switch_fiber, 0);
|
278
|
-
|
279
|
-
ID_create_event_selector = rb_intern("create_event_selector");
|
280
|
-
ID_empty = rb_intern("empty?");
|
281
|
-
ID_fiber_ref_count = rb_intern("fiber_ref_count");
|
282
|
-
ID_ivar_event_selector = rb_intern("@event_selector");
|
283
|
-
ID_ivar_event_selector_proc = rb_intern("@event_selector_proc");
|
284
|
-
ID_ivar_join_wait_queue = rb_intern("@join_wait_queue");
|
285
|
-
ID_ivar_main_fiber = rb_intern("@main_fiber");
|
286
|
-
ID_ivar_result = rb_intern("@result");
|
287
|
-
ID_ivar_terminated = rb_intern("@terminated");
|
288
|
-
ID_pop = rb_intern("pop");
|
289
|
-
ID_push = rb_intern("push");
|
290
|
-
ID_run_queue = rb_intern("run_queue");
|
291
|
-
ID_runnable_next = rb_intern("runnable_next");
|
292
|
-
ID_stop = rb_intern("stop");
|
293
|
-
|
294
|
-
SYM_scheduled_fibers = ID2SYM(rb_intern("scheduled_fibers"));
|
295
|
-
SYM_pending_watchers = ID2SYM(rb_intern("pending_watchers"));
|
296
|
-
rb_global_variable(&SYM_scheduled_fibers);
|
297
|
-
rb_global_variable(&SYM_pending_watchers);
|
298
|
-
}
|
data/ext/gyro/timer.c
DELETED
@@ -1,134 +0,0 @@
|
|
1
|
-
#include "gyro.h"
|
2
|
-
|
3
|
-
struct Gyro_Timer {
|
4
|
-
struct ev_timer ev_timer;
|
5
|
-
struct ev_loop *ev_loop;
|
6
|
-
int active;
|
7
|
-
double after;
|
8
|
-
double repeat;
|
9
|
-
VALUE self;
|
10
|
-
VALUE fiber;
|
11
|
-
VALUE selector;
|
12
|
-
};
|
13
|
-
|
14
|
-
VALUE cGyro_Timer = Qnil;
|
15
|
-
|
16
|
-
static void Gyro_Timer_mark(void *ptr) {
|
17
|
-
struct Gyro_Timer *timer = ptr;
|
18
|
-
if (timer->fiber != Qnil) {
|
19
|
-
rb_gc_mark(timer->fiber);
|
20
|
-
}
|
21
|
-
if (timer->selector != Qnil) {
|
22
|
-
rb_gc_mark(timer->selector);
|
23
|
-
}
|
24
|
-
}
|
25
|
-
|
26
|
-
static void Gyro_Timer_free(void *ptr) {
|
27
|
-
struct Gyro_Timer *timer = ptr;
|
28
|
-
if (timer->active) {
|
29
|
-
ev_clear_pending(timer->ev_loop, &timer->ev_timer);
|
30
|
-
ev_timer_stop(timer->ev_loop, &timer->ev_timer);
|
31
|
-
}
|
32
|
-
xfree(timer);
|
33
|
-
}
|
34
|
-
|
35
|
-
static size_t Gyro_Timer_size(const void *ptr) {
|
36
|
-
return sizeof(struct Gyro_Timer);
|
37
|
-
}
|
38
|
-
|
39
|
-
static const rb_data_type_t Gyro_Timer_type = {
|
40
|
-
"Gyro_Timer",
|
41
|
-
{Gyro_Timer_mark, Gyro_Timer_free, Gyro_Timer_size,},
|
42
|
-
0, 0, 0
|
43
|
-
};
|
44
|
-
|
45
|
-
static VALUE Gyro_Timer_allocate(VALUE klass) {
|
46
|
-
struct Gyro_Timer *timer = ALLOC(struct Gyro_Timer);
|
47
|
-
return TypedData_Wrap_Struct(klass, &Gyro_Timer_type, timer);
|
48
|
-
}
|
49
|
-
|
50
|
-
#define GetGyro_Timer(obj, timer) \
|
51
|
-
TypedData_Get_Struct((obj), struct Gyro_Timer, &Gyro_Timer_type, (timer))
|
52
|
-
|
53
|
-
inline void Gyro_Timer_activate(struct Gyro_Timer *timer) {
|
54
|
-
timer->fiber = rb_fiber_current();
|
55
|
-
timer->selector = Thread_current_event_selector();
|
56
|
-
timer->ev_loop = Gyro_Selector_ev_loop(timer->selector);
|
57
|
-
|
58
|
-
if (timer->active) return;
|
59
|
-
|
60
|
-
timer->active = 1;
|
61
|
-
Gyro_Selector_add_active_watcher(timer->selector, timer->self);
|
62
|
-
ev_timer_start(timer->ev_loop, &timer->ev_timer);
|
63
|
-
}
|
64
|
-
|
65
|
-
inline void Gyro_Timer_deactivate(struct Gyro_Timer *timer, int non_recurring_only) {
|
66
|
-
if (!timer->active) return;
|
67
|
-
|
68
|
-
if (!timer->repeat || !non_recurring_only) {
|
69
|
-
ev_timer_stop(timer->ev_loop, &timer->ev_timer);
|
70
|
-
if (RTEST(timer->selector)) {
|
71
|
-
Gyro_Selector_remove_active_watcher(timer->selector, timer->self);
|
72
|
-
timer->selector = Qnil;
|
73
|
-
}
|
74
|
-
timer->ev_loop = 0;
|
75
|
-
timer->active = 0;
|
76
|
-
}
|
77
|
-
|
78
|
-
timer->fiber = Qnil;
|
79
|
-
}
|
80
|
-
|
81
|
-
void Gyro_Timer_callback(struct ev_loop *ev_loop, struct ev_timer *ev_timer, int revents) {
|
82
|
-
struct Gyro_Timer *timer = (struct Gyro_Timer*)ev_timer;
|
83
|
-
|
84
|
-
Fiber_make_runnable(timer->fiber, DBL2NUM(timer->after));
|
85
|
-
Gyro_Timer_deactivate(timer, 1);
|
86
|
-
}
|
87
|
-
|
88
|
-
static VALUE Gyro_Timer_initialize(VALUE self, VALUE after, VALUE repeat) {
|
89
|
-
struct Gyro_Timer *timer;
|
90
|
-
|
91
|
-
GetGyro_Timer(self, timer);
|
92
|
-
|
93
|
-
timer->self = self;
|
94
|
-
timer->fiber = Qnil;
|
95
|
-
timer->selector = Qnil;
|
96
|
-
timer->after = NUM2DBL(after);
|
97
|
-
timer->repeat = NUM2DBL(repeat);
|
98
|
-
timer->active = 0;
|
99
|
-
timer->ev_loop = 0;
|
100
|
-
|
101
|
-
ev_timer_init(&timer->ev_timer, Gyro_Timer_callback, timer->after, timer->repeat);
|
102
|
-
|
103
|
-
return Qnil;
|
104
|
-
}
|
105
|
-
|
106
|
-
VALUE Gyro_Timer_stop(VALUE self) {
|
107
|
-
struct Gyro_Timer *timer;
|
108
|
-
GetGyro_Timer(self, timer);
|
109
|
-
|
110
|
-
Gyro_Timer_deactivate(timer, 0);
|
111
|
-
return self;
|
112
|
-
}
|
113
|
-
|
114
|
-
VALUE Gyro_Timer_await(VALUE self) {
|
115
|
-
struct Gyro_Timer *timer;
|
116
|
-
GetGyro_Timer(self, timer);
|
117
|
-
|
118
|
-
Gyro_Timer_activate(timer);
|
119
|
-
VALUE ret = Gyro_switchpoint();
|
120
|
-
Gyro_Timer_deactivate(timer, 1);
|
121
|
-
|
122
|
-
TEST_RESUME_EXCEPTION(ret);
|
123
|
-
RB_GC_GUARD(ret);
|
124
|
-
return ret;
|
125
|
-
}
|
126
|
-
|
127
|
-
void Init_Gyro_Timer() {
|
128
|
-
cGyro_Timer = rb_define_class_under(mGyro, "Timer", rb_cData);
|
129
|
-
rb_define_alloc_func(cGyro_Timer, Gyro_Timer_allocate);
|
130
|
-
|
131
|
-
rb_define_method(cGyro_Timer, "initialize", Gyro_Timer_initialize, 2);
|
132
|
-
rb_define_method(cGyro_Timer, "stop", Gyro_Timer_stop, 0);
|
133
|
-
rb_define_method(cGyro_Timer, "await", Gyro_Timer_await, 0);
|
134
|
-
}
|
data/test/test_timer.rb
DELETED
@@ -1,56 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'helper'
|
4
|
-
|
5
|
-
class TimerTest < MiniTest::Test
|
6
|
-
def test_that_one_shot_timer_works
|
7
|
-
count = 0
|
8
|
-
t = Gyro::Timer.new(0.01, 0)
|
9
|
-
spin {
|
10
|
-
t.await
|
11
|
-
count += 1
|
12
|
-
}
|
13
|
-
suspend
|
14
|
-
assert_equal 1, count
|
15
|
-
end
|
16
|
-
|
17
|
-
def test_that_repeating_timer_works
|
18
|
-
count = 0
|
19
|
-
t = Gyro::Timer.new(0.001, 0.001)
|
20
|
-
spin {
|
21
|
-
loop {
|
22
|
-
t.await
|
23
|
-
count += 1
|
24
|
-
break if count >= 3
|
25
|
-
}
|
26
|
-
}
|
27
|
-
suspend
|
28
|
-
assert_equal 3, count
|
29
|
-
ensure
|
30
|
-
t.stop
|
31
|
-
end
|
32
|
-
|
33
|
-
def test_that_repeating_timer_compensates_for_drift
|
34
|
-
count = 0
|
35
|
-
t = Gyro::Timer.new(0.1, 0.1)
|
36
|
-
deltas = []
|
37
|
-
last = nil
|
38
|
-
spin {
|
39
|
-
last = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
40
|
-
loop {
|
41
|
-
t.await
|
42
|
-
now = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
43
|
-
elapsed = (now - last)
|
44
|
-
deltas << elapsed
|
45
|
-
last = now
|
46
|
-
count += 1
|
47
|
-
sleep 0.05
|
48
|
-
break if count >= 3
|
49
|
-
}
|
50
|
-
}
|
51
|
-
suspend
|
52
|
-
assert_equal 0, deltas[1..-1].filter { |d| (d - 0.1).abs >= 0.05 }.size
|
53
|
-
ensure
|
54
|
-
t.stop
|
55
|
-
end
|
56
|
-
end
|