polyphony 0.34 → 0.41

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) 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 +34 -0
  6. data/Gemfile +0 -11
  7. data/Gemfile.lock +11 -10
  8. data/README.md +2 -1
  9. data/Rakefile +6 -2
  10. data/TODO.md +18 -95
  11. data/docs/_includes/head.html +40 -0
  12. data/docs/_includes/nav.html +5 -5
  13. data/docs/api-reference.md +1 -1
  14. data/docs/api-reference/fiber.md +18 -0
  15. data/docs/api-reference/gyro-async.md +57 -0
  16. data/docs/api-reference/gyro-child.md +29 -0
  17. data/docs/api-reference/gyro-queue.md +44 -0
  18. data/docs/api-reference/gyro-timer.md +51 -0
  19. data/docs/api-reference/gyro.md +25 -0
  20. data/docs/index.md +10 -7
  21. data/docs/main-concepts/design-principles.md +67 -9
  22. data/docs/main-concepts/extending.md +1 -1
  23. data/docs/main-concepts/fiber-scheduling.md +55 -72
  24. data/examples/core/xx-agent.rb +102 -0
  25. data/examples/core/xx-fork-cleanup.rb +22 -0
  26. data/examples/core/xx-sleeping.rb +14 -6
  27. data/examples/core/xx-timer-gc.rb +17 -0
  28. data/examples/io/tunnel.rb +48 -0
  29. data/examples/io/xx-irb.rb +1 -1
  30. data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +7 -6
  31. data/examples/performance/thread-vs-fiber/polyphony_server.rb +14 -25
  32. data/ext/{gyro → polyphony}/extconf.rb +2 -2
  33. data/ext/polyphony/fiber.c +112 -0
  34. data/ext/{gyro → polyphony}/libev.c +0 -0
  35. data/ext/{gyro → polyphony}/libev.h +0 -0
  36. data/ext/polyphony/libev_agent.c +503 -0
  37. data/ext/polyphony/libev_queue.c +214 -0
  38. data/ext/polyphony/polyphony.c +89 -0
  39. data/ext/{gyro/gyro.h → polyphony/polyphony.h} +49 -59
  40. data/ext/polyphony/polyphony_ext.c +23 -0
  41. data/ext/{gyro → polyphony}/socket.c +21 -19
  42. data/ext/{gyro → polyphony}/thread.c +55 -119
  43. data/ext/{gyro → polyphony}/tracing.c +1 -1
  44. data/lib/polyphony.rb +37 -44
  45. data/lib/polyphony/adapters/fs.rb +1 -4
  46. data/lib/polyphony/adapters/irb.rb +2 -2
  47. data/lib/polyphony/adapters/postgres.rb +6 -5
  48. data/lib/polyphony/adapters/process.rb +27 -23
  49. data/lib/polyphony/adapters/trace.rb +110 -105
  50. data/lib/polyphony/core/channel.rb +35 -35
  51. data/lib/polyphony/core/exceptions.rb +29 -29
  52. data/lib/polyphony/core/global_api.rb +94 -91
  53. data/lib/polyphony/core/resource_pool.rb +83 -83
  54. data/lib/polyphony/core/sync.rb +16 -16
  55. data/lib/polyphony/core/thread_pool.rb +49 -37
  56. data/lib/polyphony/core/throttler.rb +30 -23
  57. data/lib/polyphony/event.rb +27 -0
  58. data/lib/polyphony/extensions/core.rb +23 -14
  59. data/lib/polyphony/extensions/fiber.rb +269 -267
  60. data/lib/polyphony/extensions/io.rb +56 -26
  61. data/lib/polyphony/extensions/openssl.rb +5 -9
  62. data/lib/polyphony/extensions/socket.rb +29 -10
  63. data/lib/polyphony/extensions/thread.rb +19 -12
  64. data/lib/polyphony/net.rb +64 -60
  65. data/lib/polyphony/version.rb +1 -1
  66. data/polyphony.gemspec +3 -6
  67. data/test/helper.rb +14 -1
  68. data/test/stress.rb +17 -12
  69. data/test/test_agent.rb +77 -0
  70. data/test/{test_async.rb → test_event.rb} +17 -9
  71. data/test/test_ext.rb +25 -4
  72. data/test/test_fiber.rb +23 -14
  73. data/test/test_global_api.rb +5 -5
  74. data/test/test_io.rb +46 -24
  75. data/test/test_queue.rb +74 -0
  76. data/test/test_signal.rb +3 -40
  77. data/test/test_socket.rb +33 -0
  78. data/test/test_thread.rb +38 -16
  79. data/test/test_thread_pool.rb +3 -3
  80. data/test/test_throttler.rb +0 -1
  81. data/test/test_trace.rb +6 -5
  82. metadata +34 -39
  83. data/ext/gyro/async.c +0 -158
  84. data/ext/gyro/child.c +0 -117
  85. data/ext/gyro/gyro.c +0 -203
  86. data/ext/gyro/gyro_ext.c +0 -31
  87. data/ext/gyro/io.c +0 -447
  88. data/ext/gyro/queue.c +0 -142
  89. data/ext/gyro/selector.c +0 -183
  90. data/ext/gyro/signal.c +0 -108
  91. data/ext/gyro/timer.c +0 -154
  92. data/test/test_timer.rb +0 -56
@@ -1,142 +0,0 @@
1
- #include "gyro.h"
2
-
3
- struct Gyro_Queue {
4
- VALUE queue;
5
- VALUE wait_queue;
6
- };
7
-
8
- VALUE cGyro_Queue = Qnil;
9
-
10
- static void Gyro_Queue_mark(void *ptr) {
11
- struct Gyro_Queue *queue = ptr;
12
- if (queue->queue != Qnil) {
13
- rb_gc_mark(queue->queue);
14
- }
15
- if (queue->wait_queue != Qnil) {
16
- rb_gc_mark(queue->wait_queue);
17
- }
18
- }
19
-
20
- static void Gyro_Queue_free(void *ptr) {
21
- struct Gyro_Queue *queue = ptr;
22
- xfree(queue);
23
- }
24
-
25
- static size_t Gyro_Queue_size(const void *ptr) {
26
- return sizeof(struct Gyro_Queue);
27
- }
28
-
29
- static const rb_data_type_t Gyro_Queue_type = {
30
- "Gyro_Queue",
31
- {Gyro_Queue_mark, Gyro_Queue_free, Gyro_Queue_size,},
32
- 0, 0,
33
- RUBY_TYPED_FREE_IMMEDIATELY,
34
- };
35
-
36
- static VALUE Gyro_Queue_allocate(VALUE klass) {
37
- struct Gyro_Queue *queue = (struct Gyro_Queue *)xmalloc(sizeof(struct Gyro_Queue));
38
- return TypedData_Wrap_Struct(klass, &Gyro_Queue_type, queue);
39
- }
40
- #define GetGyro_Queue(obj, queue) \
41
- TypedData_Get_Struct((obj), struct Gyro_Queue, &Gyro_Queue_type, (queue))
42
-
43
- static VALUE Gyro_Queue_initialize(VALUE self) {
44
- struct Gyro_Queue *queue;
45
- GetGyro_Queue(self, queue);
46
-
47
- queue->queue = rb_ary_new();
48
- queue->wait_queue = rb_ary_new();
49
-
50
- return Qnil;
51
- }
52
-
53
- VALUE Gyro_Queue_push(VALUE self, VALUE value) {
54
- struct Gyro_Queue *queue;
55
- GetGyro_Queue(self, queue);
56
-
57
- if (RARRAY_LEN(queue->wait_queue) > 0) {
58
- VALUE async = rb_ary_shift(queue->wait_queue);
59
- rb_funcall(async, ID_signal_bang, 1, Qnil);
60
- }
61
-
62
- rb_ary_push(queue->queue, value);
63
- return self;
64
- }
65
-
66
- VALUE Gyro_Queue_shift(VALUE self) {
67
- struct Gyro_Queue *queue;
68
- GetGyro_Queue(self, queue);
69
-
70
- if (RARRAY_LEN(queue->queue) == 0) {
71
- VALUE async = rb_funcall(cGyro_Async, ID_new, 0);
72
- rb_ary_push(queue->wait_queue, async);
73
- VALUE ret = Gyro_Async_await_no_raise(async);
74
- if (RTEST(rb_obj_is_kind_of(ret, rb_eException))) {
75
- rb_ary_delete(queue->wait_queue, async);
76
- return rb_funcall(rb_mKernel, ID_raise, 1, ret);
77
- }
78
- }
79
-
80
- return rb_ary_shift(queue->queue);
81
- }
82
-
83
- VALUE Gyro_Queue_shift_no_wait(VALUE self) {
84
- struct Gyro_Queue *queue;
85
- GetGyro_Queue(self, queue);
86
-
87
- return rb_ary_shift(queue->queue);
88
- }
89
-
90
- VALUE Gyro_Queue_shift_each(VALUE self) {
91
- struct Gyro_Queue *queue;
92
- GetGyro_Queue(self, queue);
93
-
94
- VALUE old_queue = queue->queue;
95
- queue->queue = rb_ary_new();
96
-
97
- if (rb_block_given_p()) {
98
- long len = RARRAY_LEN(old_queue);
99
- long i;
100
- for (i = 0; i < len; i++) {
101
- rb_yield(RARRAY_AREF(old_queue, i));
102
- }
103
- RB_GC_GUARD(old_queue);
104
- return self;
105
- }
106
- else {
107
- return old_queue;
108
- }
109
- }
110
-
111
- VALUE Gyro_Queue_clear(VALUE self) {
112
- struct Gyro_Queue *queue;
113
- GetGyro_Queue(self, queue);
114
-
115
- rb_ary_clear(queue->queue);
116
- return self;
117
- }
118
-
119
- VALUE Gyro_Queue_empty_p(VALUE self) {
120
- struct Gyro_Queue *queue;
121
- GetGyro_Queue(self, queue);
122
-
123
- return (RARRAY_LEN(queue->queue) == 0) ? Qtrue : Qfalse;
124
- }
125
-
126
- void Init_Gyro_Queue() {
127
- cGyro_Queue = rb_define_class_under(mGyro, "Queue", rb_cData);
128
- rb_define_alloc_func(cGyro_Queue, Gyro_Queue_allocate);
129
-
130
- rb_define_method(cGyro_Queue, "initialize", Gyro_Queue_initialize, 0);
131
- rb_define_method(cGyro_Queue, "push", Gyro_Queue_push, 1);
132
- rb_define_method(cGyro_Queue, "<<", Gyro_Queue_push, 1);
133
-
134
- rb_define_method(cGyro_Queue, "pop", Gyro_Queue_shift, 0);
135
- rb_define_method(cGyro_Queue, "shift", Gyro_Queue_shift, 0);
136
-
137
- rb_define_method(cGyro_Queue, "shift_no_wait", Gyro_Queue_shift_no_wait, 0);
138
-
139
- rb_define_method(cGyro_Queue, "shift_each", Gyro_Queue_shift_each, 0);
140
- rb_define_method(cGyro_Queue, "clear", Gyro_Queue_clear, 0);
141
- rb_define_method(cGyro_Queue, "empty?", Gyro_Queue_empty_p, 0);
142
- }
@@ -1,183 +0,0 @@
1
- #include "gyro.h"
2
-
3
- struct Gyro_Selector {
4
- struct ev_loop *ev_loop;
5
- long run_no_wait_count;
6
- int ev_loop_running;
7
- struct ev_async async;
8
- };
9
-
10
- VALUE cGyro_Selector = Qnil;
11
-
12
- static void Gyro_Selector_mark(void *ptr) {
13
- // struct Gyro_Selector *selector = ptr;
14
- }
15
-
16
- static void Gyro_Selector_free(void *ptr) {
17
- struct Gyro_Selector *selector = ptr;
18
- ev_async_stop(selector->ev_loop, &selector->async);
19
- if (selector->ev_loop && !ev_is_default_loop(selector->ev_loop)) {
20
- // printf("Selector garbage collected before being stopped!\n");
21
- ev_loop_destroy(selector->ev_loop);
22
- }
23
- xfree(selector);
24
- }
25
-
26
- static size_t Gyro_Selector_size(const void *ptr) {
27
- return sizeof(struct Gyro_Selector);
28
- }
29
-
30
- static const rb_data_type_t Gyro_Selector_type = {
31
- "Gyro_Selector",
32
- {Gyro_Selector_mark, Gyro_Selector_free, Gyro_Selector_size,},
33
- 0, 0,
34
- RUBY_TYPED_FREE_IMMEDIATELY,
35
- };
36
-
37
- static VALUE Gyro_Selector_allocate(VALUE klass) {
38
- struct Gyro_Selector *selector = (struct Gyro_Selector *)xmalloc(sizeof(struct Gyro_Selector));
39
- return TypedData_Wrap_Struct(klass, &Gyro_Selector_type, selector);
40
- }
41
-
42
- #define GetGyro_Selector(obj, selector) \
43
- TypedData_Get_Struct((obj), struct Gyro_Selector, &Gyro_Selector_type, (selector))
44
-
45
- inline struct ev_loop *Gyro_Selector_ev_loop(VALUE self) {
46
- struct Gyro_Selector *selector;
47
- GetGyro_Selector(self, selector);
48
-
49
- return selector->ev_loop;
50
- }
51
-
52
- inline struct ev_loop *Gyro_Selector_current_thread_ev_loop() {
53
- struct Gyro_Selector *selector;
54
- GetGyro_Selector(Thread_current_event_selector(), selector);
55
-
56
- return selector->ev_loop;
57
- }
58
-
59
- inline ev_tstamp Gyro_Selector_now(VALUE self) {
60
- struct Gyro_Selector *selector;
61
- GetGyro_Selector(self, selector);
62
-
63
- return ev_now(selector->ev_loop);
64
- }
65
-
66
- long Gyro_Selector_pending_count(VALUE self) {
67
- struct Gyro_Selector *selector;
68
- GetGyro_Selector(self, selector);
69
-
70
- return ev_pending_count(selector->ev_loop);
71
- }
72
-
73
- void dummy_async_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, int revents) {
74
- // This callback does nothing, the selector's async is used solely for waking
75
- // up the event loop.
76
- }
77
-
78
- static VALUE Gyro_Selector_initialize(VALUE self, VALUE thread) {
79
- struct Gyro_Selector *selector;
80
- GetGyro_Selector(self, selector);
81
-
82
- int use_default_loop = (rb_thread_current() == rb_thread_main());
83
- selector->ev_loop = use_default_loop ? EV_DEFAULT : ev_loop_new(EVFLAG_NOSIGMASK);
84
- selector->run_no_wait_count = 0;
85
-
86
- ev_async_init(&selector->async, dummy_async_callback);
87
- ev_async_start(selector->ev_loop, &selector->async);
88
- ev_run(selector->ev_loop, EVRUN_NOWAIT);
89
- return Qnil;
90
- }
91
-
92
- inline VALUE Gyro_Selector_run(VALUE self, VALUE current_fiber) {
93
- struct Gyro_Selector *selector;
94
- GetGyro_Selector(self, selector);
95
- if (selector->ev_loop) {
96
- selector->run_no_wait_count = 0;
97
- FIBER_TRACE(2, SYM_fiber_ev_loop_enter, current_fiber);
98
- selector->ev_loop_running = 1;
99
- ev_run(selector->ev_loop, EVRUN_ONCE);
100
- selector->ev_loop_running = 0;
101
- FIBER_TRACE(2, SYM_fiber_ev_loop_leave, current_fiber);
102
- }
103
- return Qnil;
104
- }
105
-
106
- inline void Gyro_Selector_run_no_wait(VALUE self, VALUE current_fiber, long runnable_count) {
107
- struct Gyro_Selector *selector;
108
- GetGyro_Selector(self, selector);
109
-
110
- selector->run_no_wait_count++;
111
- if (selector->run_no_wait_count < runnable_count || selector->run_no_wait_count < 10) {
112
- return;
113
- }
114
-
115
- selector->run_no_wait_count = 0;
116
- FIBER_TRACE(2, SYM_fiber_ev_loop_enter, current_fiber);
117
- ev_run(selector->ev_loop, EVRUN_NOWAIT);
118
- FIBER_TRACE(2, SYM_fiber_ev_loop_leave, current_fiber);
119
- }
120
-
121
- VALUE Gyro_Selector_stop(VALUE self) {
122
- struct Gyro_Selector *selector;
123
- GetGyro_Selector(self, selector);
124
-
125
- if (selector->ev_loop && !ev_is_default_loop(selector->ev_loop)) {
126
- // ev_loop_destroy(selector->ev_loop);
127
- // selector->ev_loop = 0;
128
- }
129
- return Qnil;
130
- }
131
-
132
- VALUE Gyro_Selector_post_fork(VALUE self) {
133
- struct Gyro_Selector *selector;
134
- GetGyro_Selector(self, selector);
135
-
136
- ev_loop_fork(selector->ev_loop);
137
- return self;
138
- }
139
-
140
- VALUE Gyro_Selector_break_out_of_ev_loop(VALUE self) {
141
- struct Gyro_Selector *selector;
142
- GetGyro_Selector(self, selector);
143
-
144
- if (selector->ev_loop_running) {
145
- // Since the loop will run until at least one event has occurred, we signal
146
- // the selector's associated async watcher, which will cause the ev loop to
147
- // return. In contrast to using `ev_break` to break out of the loop, which
148
- // should be called from the same thread (from within the ev_loop), using an
149
- // `ev_async` allows us to interrupt the event loop across threads.
150
- ev_async_send(selector->ev_loop, &selector->async);
151
- return Qtrue;
152
- }
153
-
154
- return Qnil;
155
- }
156
-
157
- inline static VALUE Gyro_Selector_wait_readable(VALUE self, VALUE io) {
158
- VALUE watcher = IO_read_watcher(io);
159
- return Gyro_IO_await(watcher);
160
- }
161
-
162
- inline static VALUE Gyro_Selector_wait_writable(VALUE self, VALUE io) {
163
- VALUE watcher = IO_write_watcher(io);
164
- return Gyro_IO_await(watcher);
165
- }
166
-
167
- inline static VALUE Gyro_Selector_wait_timeout(VALUE self, VALUE duration) {
168
- VALUE watcher = rb_funcall(cGyro_Timer, ID_new, 2, duration, Qnil);
169
- return Gyro_Timer_await(watcher);
170
- }
171
-
172
- void Init_Gyro_Selector() {
173
- cGyro_Selector = rb_define_class_under(mGyro, "Selector", rb_cData);
174
- rb_define_alloc_func(cGyro_Selector, Gyro_Selector_allocate);
175
-
176
- rb_define_method(cGyro_Selector, "initialize", Gyro_Selector_initialize, 1);
177
- rb_define_method(cGyro_Selector, "run", Gyro_Selector_run, 1);
178
- rb_define_method(cGyro_Selector, "stop", Gyro_Selector_stop, 0);
179
- rb_define_method(cGyro_Selector, "wait_readable", Gyro_Selector_wait_readable, 1);
180
- rb_define_method(cGyro_Selector, "wait_writable", Gyro_Selector_wait_writable, 1);
181
- rb_define_method(cGyro_Selector, "wait_timeout", Gyro_Selector_wait_timeout, 1);
182
- rb_define_method(cGyro_Selector, "break_out_of_ev_loop", Gyro_Selector_break_out_of_ev_loop, 0);
183
- }
@@ -1,108 +0,0 @@
1
- #include "gyro.h"
2
-
3
- struct Gyro_Signal {
4
- struct ev_signal ev_signal;
5
- struct ev_loop *ev_loop;
6
- int active;
7
- int signum;
8
- VALUE fiber;
9
- };
10
-
11
- static VALUE cGyro_Signal = Qnil;
12
-
13
- static void Gyro_Signal_mark(void *ptr) {
14
- struct Gyro_Signal *signal = ptr;
15
- if (signal->fiber != Qnil) {
16
- rb_gc_mark(signal->fiber);
17
- }
18
- }
19
-
20
- static void Gyro_Signal_free(void *ptr) {
21
- struct Gyro_Signal *signal = ptr;
22
- if (signal->active) {
23
- printf("Signal watcher garbage collected while still active!\n");
24
- // ev_signal_stop(signal->ev_loop, &signal->ev_signal);
25
- }
26
- xfree(signal);
27
- }
28
-
29
- static size_t Gyro_Signal_size(const void *ptr) {
30
- return sizeof(struct Gyro_Signal);
31
- }
32
-
33
- static const rb_data_type_t Gyro_Signal_type = {
34
- "Gyro_Signal",
35
- {Gyro_Signal_mark, Gyro_Signal_free, Gyro_Signal_size,},
36
- 0, 0,
37
- RUBY_TYPED_FREE_IMMEDIATELY,
38
- };
39
-
40
- static VALUE Gyro_Signal_allocate(VALUE klass) {
41
- struct Gyro_Signal *signal = (struct Gyro_Signal *)xmalloc(sizeof(struct Gyro_Signal));
42
- return TypedData_Wrap_Struct(klass, &Gyro_Signal_type, signal);
43
- }
44
-
45
- void Gyro_Signal_callback(struct ev_loop *ev_loop, struct ev_signal *ev_signal, int revents) {
46
- struct Gyro_Signal *signal = (struct Gyro_Signal*)ev_signal;
47
-
48
- if (signal->fiber != Qnil) {
49
- VALUE fiber = signal->fiber;
50
-
51
- ev_signal_stop(signal->ev_loop, ev_signal);
52
- signal->active = 0;
53
- signal->fiber = Qnil;
54
- Gyro_schedule_fiber(fiber, INT2NUM(signal->signum));
55
- }
56
- }
57
-
58
- #define GetGyro_Signal(obj, signal) \
59
- TypedData_Get_Struct((obj), struct Gyro_Signal, &Gyro_Signal_type, (signal))
60
-
61
- static VALUE Gyro_Signal_initialize(VALUE self, VALUE sig) {
62
- struct Gyro_Signal *signal;
63
- VALUE signum = sig;
64
-
65
- GetGyro_Signal(self, signal);
66
- signal->signum = NUM2INT(signum);
67
-
68
- ev_signal_init(&signal->ev_signal, Gyro_Signal_callback, signal->signum);
69
-
70
- return Qnil;
71
- }
72
-
73
- static VALUE Gyro_Signal_await(VALUE self) {
74
- struct Gyro_Signal *signal;
75
- VALUE ret;
76
-
77
- GetGyro_Signal(self, signal);
78
-
79
- signal->fiber = rb_fiber_current();
80
- signal->active = 1;
81
- signal->ev_loop = Gyro_Selector_current_thread_ev_loop();
82
- ev_signal_start(signal->ev_loop, &signal->ev_signal);
83
-
84
- ret = Fiber_await();
85
- RB_GC_GUARD(ret);
86
-
87
- if (signal->active) {
88
- signal->active = 0;
89
- signal->fiber = Qnil;
90
- ev_signal_stop(signal->ev_loop, &signal->ev_signal);
91
- }
92
-
93
- // fiber is resumed, check if resumed value is an exception
94
- signal->fiber = Qnil;
95
- if (RTEST(rb_obj_is_kind_of(ret, rb_eException))) {
96
- return rb_funcall(rb_mKernel, ID_raise, 1, ret);
97
- }
98
- else
99
- return ret;
100
- }
101
-
102
- void Init_Gyro_Signal() {
103
- cGyro_Signal = rb_define_class_under(mGyro, "Signal", rb_cData);
104
- rb_define_alloc_func(cGyro_Signal, Gyro_Signal_allocate);
105
-
106
- rb_define_method(cGyro_Signal, "initialize", Gyro_Signal_initialize, 1);
107
- rb_define_method(cGyro_Signal, "await", Gyro_Signal_await, 0);
108
- }
@@ -1,154 +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
- int should_free;
10
- VALUE self;
11
- VALUE fiber;
12
- VALUE selector;
13
- };
14
-
15
- VALUE cGyro_Timer = Qnil;
16
-
17
- static void Gyro_Timer_mark(void *ptr) {
18
- struct Gyro_Timer *timer = ptr;
19
- if (timer->fiber != Qnil) {
20
- rb_gc_mark(timer->fiber);
21
- }
22
- if (timer->selector != Qnil) {
23
- rb_gc_mark(timer->selector);
24
- }
25
- }
26
-
27
- static void Gyro_Timer_free(void *ptr) {
28
- struct Gyro_Timer *timer = ptr;
29
- if (timer->active) {
30
- printf("Timer watcher garbage collected while still active (%g, %g)!\n", timer->after, timer->repeat);
31
- timer->should_free = 1;
32
- } else {
33
- xfree(timer);
34
- }
35
- }
36
-
37
- static size_t Gyro_Timer_size(const void *ptr) {
38
- return sizeof(struct Gyro_Timer);
39
- }
40
-
41
- static const rb_data_type_t Gyro_Timer_type = {
42
- "Gyro_Timer",
43
- {Gyro_Timer_mark, Gyro_Timer_free, Gyro_Timer_size,},
44
- 0, 0,
45
- RUBY_TYPED_FREE_IMMEDIATELY,
46
- };
47
-
48
- static VALUE Gyro_Timer_allocate(VALUE klass) {
49
- struct Gyro_Timer *timer = (struct Gyro_Timer *)xmalloc(sizeof(struct Gyro_Timer));
50
- return TypedData_Wrap_Struct(klass, &Gyro_Timer_type, timer);
51
- }
52
- #define GetGyro_Timer(obj, timer) \
53
- TypedData_Get_Struct((obj), struct Gyro_Timer, &Gyro_Timer_type, (timer))
54
-
55
- void Gyro_Timer_callback(struct ev_loop *ev_loop, struct ev_timer *ev_timer, int revents) {
56
- struct Gyro_Timer *timer = (struct Gyro_Timer*)ev_timer;
57
-
58
- if (timer->should_free) {
59
- ev_timer_stop(timer->ev_loop, ev_timer);
60
- xfree(timer);
61
- return;
62
- }
63
-
64
- if (!timer->repeat) {
65
- timer->active = 0;
66
- timer->selector = Qnil;
67
- }
68
-
69
- if (timer->fiber != Qnil) {
70
- Gyro_schedule_fiber(timer->fiber, DBL2NUM(timer->after));
71
- }
72
- }
73
-
74
- static VALUE Gyro_Timer_initialize(VALUE self, VALUE after, VALUE repeat) {
75
- struct Gyro_Timer *timer;
76
-
77
- GetGyro_Timer(self, timer);
78
-
79
- timer->self = self;
80
- timer->fiber = Qnil;
81
- timer->after = NUM2DBL(after);
82
- timer->repeat = NUM2DBL(repeat);
83
- timer->active = 0;
84
-
85
- timer->should_free = 0;
86
-
87
- ev_timer_init(&timer->ev_timer, Gyro_Timer_callback, timer->after, timer->repeat);
88
- timer->ev_loop = 0;
89
- timer->selector = Qnil;
90
-
91
- return Qnil;
92
- }
93
-
94
- VALUE Gyro_Timer_stop(VALUE self) {
95
- struct Gyro_Timer *timer;
96
- GetGyro_Timer(self, timer);
97
-
98
- if (timer->active) {
99
- timer->active = 0;
100
- timer->fiber = Qnil;
101
- timer->selector = Qnil;
102
- ev_timer_stop(timer->ev_loop, &timer->ev_timer);
103
- timer->ev_loop = 0;
104
- }
105
-
106
- return self;
107
- }
108
-
109
- VALUE Gyro_Timer_await(VALUE self) {
110
- struct Gyro_Timer *timer;
111
- VALUE ret;
112
-
113
- GetGyro_Timer(self, timer);
114
-
115
- timer->fiber = rb_fiber_current();
116
- timer->selector = Thread_current_event_selector();
117
- timer->ev_loop = Gyro_Selector_ev_loop(timer->selector);
118
-
119
- if (timer->active != 1) {
120
- timer->active = 1;
121
- ev_timer_start(timer->ev_loop, &timer->ev_timer);
122
- }
123
-
124
- ret = Fiber_await();
125
- RB_GC_GUARD(ret);
126
-
127
- if (timer->active && (timer->repeat == .0)) {
128
- timer->active = 0;
129
- timer->fiber = Qnil;
130
- timer->selector = Qnil;
131
- ev_timer_stop(timer->ev_loop, &timer->ev_timer);
132
- timer->ev_loop = 0;
133
- }
134
-
135
- // fiber is resumed, check if resumed value is an exception
136
- timer->fiber = Qnil;
137
- timer->selector = Qnil;
138
-
139
- RB_GC_GUARD(self);
140
- if (RTEST(rb_obj_is_kind_of(ret, rb_eException))) {
141
- return rb_funcall(rb_mKernel, ID_raise, 1, ret);
142
- }
143
- else
144
- return ret;
145
- }
146
-
147
- void Init_Gyro_Timer() {
148
- cGyro_Timer = rb_define_class_under(mGyro, "Timer", rb_cData);
149
- rb_define_alloc_func(cGyro_Timer, Gyro_Timer_allocate);
150
-
151
- rb_define_method(cGyro_Timer, "initialize", Gyro_Timer_initialize, 2);
152
- rb_define_method(cGyro_Timer, "stop", Gyro_Timer_stop, 0);
153
- rb_define_method(cGyro_Timer, "await", Gyro_Timer_await, 0);
154
- }