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.
Files changed (118) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +11 -2
  3. data/.gitignore +2 -2
  4. data/.rubocop.yml +30 -0
  5. data/CHANGELOG.md +28 -2
  6. data/Gemfile +0 -11
  7. data/Gemfile.lock +15 -14
  8. data/README.md +2 -1
  9. data/Rakefile +7 -3
  10. data/TODO.md +28 -95
  11. data/docs/_config.yml +56 -7
  12. data/docs/_sass/custom/custom.scss +0 -30
  13. data/docs/_sass/overrides.scss +0 -46
  14. data/docs/{user-guide → _user-guide}/all-about-timers.md +0 -0
  15. data/docs/_user-guide/index.md +9 -0
  16. data/docs/{user-guide → _user-guide}/web-server.md +0 -0
  17. data/docs/api-reference/fiber.md +2 -2
  18. data/docs/api-reference/index.md +9 -0
  19. data/docs/api-reference/polyphony-process.md +1 -1
  20. data/docs/api-reference/thread.md +1 -1
  21. data/docs/faq.md +21 -11
  22. data/docs/getting-started/index.md +10 -0
  23. data/docs/getting-started/installing.md +2 -6
  24. data/docs/getting-started/overview.md +507 -0
  25. data/docs/getting-started/tutorial.md +27 -19
  26. data/docs/index.md +3 -2
  27. data/docs/main-concepts/concurrency.md +0 -5
  28. data/docs/main-concepts/design-principles.md +69 -21
  29. data/docs/main-concepts/extending.md +1 -1
  30. data/docs/main-concepts/index.md +9 -0
  31. data/examples/core/01-spinning-up-fibers.rb +1 -0
  32. data/examples/core/03-interrupting.rb +4 -1
  33. data/examples/core/04-handling-signals.rb +19 -0
  34. data/examples/core/xx-agent.rb +102 -0
  35. data/examples/core/xx-fork-cleanup.rb +22 -0
  36. data/examples/core/xx-sleeping.rb +14 -6
  37. data/examples/io/tunnel.rb +48 -0
  38. data/examples/io/xx-irb.rb +1 -1
  39. data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +7 -6
  40. data/examples/performance/thread-vs-fiber/polyphony_server.rb +13 -36
  41. data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +58 -0
  42. data/examples/performance/xx-array.rb +11 -0
  43. data/examples/performance/xx-fiber-switch.rb +9 -0
  44. data/examples/performance/xx-snooze.rb +15 -0
  45. data/ext/{gyro → polyphony}/extconf.rb +2 -2
  46. data/ext/{gyro → polyphony}/fiber.c +18 -22
  47. data/ext/{gyro → polyphony}/libev.c +0 -0
  48. data/ext/{gyro → polyphony}/libev.h +0 -0
  49. data/ext/polyphony/libev_agent.c +718 -0
  50. data/ext/polyphony/libev_queue.c +216 -0
  51. data/ext/{gyro/gyro.c → polyphony/polyphony.c} +16 -46
  52. data/ext/{gyro/gyro.h → polyphony/polyphony.h} +25 -39
  53. data/ext/polyphony/polyphony_ext.c +23 -0
  54. data/ext/{gyro → polyphony}/socket.c +21 -18
  55. data/ext/polyphony/thread.c +206 -0
  56. data/ext/{gyro → polyphony}/tracing.c +1 -1
  57. data/lib/polyphony.rb +40 -44
  58. data/lib/polyphony/adapters/fs.rb +1 -4
  59. data/lib/polyphony/adapters/irb.rb +1 -1
  60. data/lib/polyphony/adapters/postgres.rb +6 -5
  61. data/lib/polyphony/adapters/process.rb +27 -23
  62. data/lib/polyphony/adapters/trace.rb +110 -105
  63. data/lib/polyphony/core/channel.rb +35 -35
  64. data/lib/polyphony/core/exceptions.rb +29 -29
  65. data/lib/polyphony/core/global_api.rb +94 -91
  66. data/lib/polyphony/core/resource_pool.rb +83 -83
  67. data/lib/polyphony/core/sync.rb +16 -16
  68. data/lib/polyphony/core/thread_pool.rb +49 -37
  69. data/lib/polyphony/core/throttler.rb +30 -23
  70. data/lib/polyphony/event.rb +27 -0
  71. data/lib/polyphony/extensions/core.rb +25 -17
  72. data/lib/polyphony/extensions/fiber.rb +269 -267
  73. data/lib/polyphony/extensions/io.rb +56 -26
  74. data/lib/polyphony/extensions/openssl.rb +5 -9
  75. data/lib/polyphony/extensions/socket.rb +29 -10
  76. data/lib/polyphony/extensions/thread.rb +19 -12
  77. data/lib/polyphony/net.rb +64 -60
  78. data/lib/polyphony/version.rb +1 -1
  79. data/polyphony.gemspec +4 -7
  80. data/test/helper.rb +14 -1
  81. data/test/stress.rb +17 -12
  82. data/test/test_agent.rb +124 -0
  83. data/test/{test_async.rb → test_event.rb} +15 -7
  84. data/test/test_ext.rb +25 -4
  85. data/test/test_fiber.rb +19 -10
  86. data/test/test_global_api.rb +4 -4
  87. data/test/test_io.rb +46 -24
  88. data/test/test_queue.rb +74 -0
  89. data/test/test_signal.rb +3 -40
  90. data/test/test_socket.rb +33 -0
  91. data/test/test_thread.rb +38 -16
  92. data/test/test_thread_pool.rb +2 -2
  93. data/test/test_throttler.rb +0 -1
  94. data/test/test_trace.rb +6 -5
  95. metadata +41 -57
  96. data/docs/_includes/nav.html +0 -51
  97. data/docs/_includes/prevnext.html +0 -17
  98. data/docs/_layouts/default.html +0 -106
  99. data/docs/api-reference.md +0 -11
  100. data/docs/api-reference/gyro-async.md +0 -57
  101. data/docs/api-reference/gyro-child.md +0 -29
  102. data/docs/api-reference/gyro-queue.md +0 -44
  103. data/docs/api-reference/gyro-timer.md +0 -51
  104. data/docs/api-reference/gyro.md +0 -25
  105. data/docs/getting-started.md +0 -10
  106. data/docs/main-concepts.md +0 -10
  107. data/docs/user-guide.md +0 -10
  108. data/examples/core/forever_sleep.rb +0 -19
  109. data/ext/gyro/async.c +0 -148
  110. data/ext/gyro/child.c +0 -127
  111. data/ext/gyro/gyro_ext.c +0 -33
  112. data/ext/gyro/io.c +0 -474
  113. data/ext/gyro/queue.c +0 -142
  114. data/ext/gyro/selector.c +0 -205
  115. data/ext/gyro/signal.c +0 -118
  116. data/ext/gyro/thread.c +0 -298
  117. data/ext/gyro/timer.c +0 -134
  118. data/test/test_timer.rb +0 -56
@@ -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
- }
@@ -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
- }
@@ -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