polyphony 0.26 → 0.27

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -3
  3. data/CHANGELOG.md +8 -0
  4. data/Gemfile.lock +1 -1
  5. data/TODO.md +34 -14
  6. data/examples/core/xx-mt-scheduler.rb +349 -0
  7. data/examples/core/xx-queue-async.rb +120 -0
  8. data/examples/core/xx-readpartial.rb +18 -0
  9. data/examples/core/xx-thread-selector-sleep.rb +51 -0
  10. data/examples/core/xx-thread-selector-snooze.rb +46 -0
  11. data/examples/core/xx-thread-sleep.rb +17 -0
  12. data/examples/core/xx-thread-snooze.rb +34 -0
  13. data/examples/performance/snooze.rb +5 -5
  14. data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +73 -0
  15. data/examples/performance/thread-vs-fiber/polyphony_server.rb +12 -4
  16. data/ext/gyro/async.c +58 -61
  17. data/ext/gyro/child.c +50 -59
  18. data/ext/gyro/gyro.c +84 -192
  19. data/ext/gyro/gyro.h +29 -2
  20. data/ext/gyro/gyro_ext.c +6 -0
  21. data/ext/gyro/io.c +87 -96
  22. data/ext/gyro/queue.c +109 -0
  23. data/ext/gyro/selector.c +117 -0
  24. data/ext/gyro/signal.c +44 -54
  25. data/ext/gyro/socket.c +15 -20
  26. data/ext/gyro/thread.c +203 -0
  27. data/ext/gyro/timer.c +79 -50
  28. data/ext/libev/ev.c +3 -0
  29. data/lib/polyphony.rb +7 -12
  30. data/lib/polyphony/core/global_api.rb +5 -1
  31. data/lib/polyphony/core/throttler.rb +6 -11
  32. data/lib/polyphony/extensions/core.rb +2 -0
  33. data/lib/polyphony/extensions/fiber.rb +11 -13
  34. data/lib/polyphony/extensions/thread.rb +52 -0
  35. data/lib/polyphony/version.rb +1 -1
  36. data/test/helper.rb +8 -3
  37. data/test/test_fiber.rb +2 -2
  38. data/test/test_global_api.rb +4 -5
  39. data/test/test_gyro.rb +3 -2
  40. data/test/test_io.rb +1 -0
  41. data/test/test_supervisor.rb +3 -3
  42. data/test/test_thread.rb +44 -0
  43. data/test/test_throttler.rb +41 -0
  44. data/test/test_timer.rb +13 -7
  45. metadata +17 -6
  46. data/examples/core/xx-thread_cancel.rb +0 -28
  47. data/examples/performance/thread.rb +0 -27
  48. data/lib/polyphony/core/thread.rb +0 -23
@@ -2,6 +2,7 @@
2
2
 
3
3
  struct Gyro_Child {
4
4
  struct ev_child ev_child;
5
+ struct ev_loop *ev_loop;
5
6
  int active;
6
7
  int pid;
7
8
  VALUE self;
@@ -10,26 +11,23 @@ struct Gyro_Child {
10
11
 
11
12
  static VALUE cGyro_Child = Qnil;
12
13
 
13
- /* Allocator/deallocator */
14
- static VALUE Gyro_Child_allocate(VALUE klass);
15
- static void Gyro_Child_mark(void *ptr);
16
- static void Gyro_Child_free(void *ptr);
17
- static size_t Gyro_Child_size(const void *ptr);
18
-
19
- /* Methods */
20
- static VALUE Gyro_Child_initialize(VALUE self, VALUE pid);
21
-
22
- static VALUE Gyro_Child_await(VALUE self);
23
-
24
- void Gyro_Child_callback(struct ev_loop *ev_loop, struct ev_child *child, int revents);
14
+ static void Gyro_Child_mark(void *ptr) {
15
+ struct Gyro_Child *child = ptr;
16
+ if (child->fiber != Qnil) {
17
+ rb_gc_mark(child->fiber);
18
+ }
19
+ }
25
20
 
26
- /* Child encapsulates an child watcher */
27
- void Init_Gyro_Child() {
28
- cGyro_Child = rb_define_class_under(mGyro, "Child", rb_cData);
29
- rb_define_alloc_func(cGyro_Child, Gyro_Child_allocate);
21
+ static void Gyro_Child_free(void *ptr) {
22
+ struct Gyro_Child *child = ptr;
23
+ if (child->active) {
24
+ printf("Child watcher garbage collected while still active!\n");
25
+ }
26
+ xfree(child);
27
+ }
30
28
 
31
- rb_define_method(cGyro_Child, "initialize", Gyro_Child_initialize, 1);
32
- rb_define_method(cGyro_Child, "await", Gyro_Child_await, 0);
29
+ static size_t Gyro_Child_size(const void *ptr) {
30
+ return sizeof(struct Gyro_Child);
33
31
  }
34
32
 
35
33
  static const rb_data_type_t Gyro_Child_type = {
@@ -44,23 +42,22 @@ static VALUE Gyro_Child_allocate(VALUE klass) {
44
42
  return TypedData_Wrap_Struct(klass, &Gyro_Child_type, child);
45
43
  }
46
44
 
47
- static void Gyro_Child_mark(void *ptr) {
48
- struct Gyro_Child *child = ptr;
45
+ void Gyro_Child_callback(struct ev_loop *ev_loop, struct ev_child *ev_child, int revents) {
46
+ struct Gyro_Child *child = (struct Gyro_Child*)ev_child;
47
+
48
+ child->active = 0;
49
+ ev_child_stop(child->ev_loop, ev_child);
50
+
49
51
  if (child->fiber != Qnil) {
50
- rb_gc_mark(child->fiber);
51
- }
52
- }
52
+ VALUE fiber = child->fiber;
53
+ int exit_status = ev_child->rstatus >> 8; // weird, why should we do this?
53
54
 
54
- static void Gyro_Child_free(void *ptr) {
55
- struct Gyro_Child *child = ptr;
56
- if (child->active) {
57
- ev_child_stop(EV_DEFAULT, &child->ev_child);
55
+ VALUE resume_value = rb_ary_new_from_args(
56
+ 2, INT2NUM(ev_child->rpid), INT2NUM(exit_status)
57
+ );
58
+ child->fiber = Qnil;
59
+ Gyro_schedule_fiber(fiber, resume_value);
58
60
  }
59
- xfree(child);
60
- }
61
-
62
- static size_t Gyro_Child_size(const void *ptr) {
63
- return sizeof(struct Gyro_Child);
64
61
  }
65
62
 
66
63
  #define GetGyro_Child(obj, child) \
@@ -81,46 +78,40 @@ static VALUE Gyro_Child_initialize(VALUE self, VALUE pid) {
81
78
  return Qnil;
82
79
  }
83
80
 
84
- void Gyro_Child_callback(struct ev_loop *ev_loop, struct ev_child *ev_child, int revents) {
85
- struct Gyro_Child *child = (struct Gyro_Child*)ev_child;
86
-
87
- child->active = 0;
88
- ev_child_stop(EV_DEFAULT, ev_child);
89
-
90
- if (child->fiber != Qnil) {
91
- VALUE fiber = child->fiber;
92
- int exit_status = ev_child->rstatus >> 8; // weird, why should we do this?
93
-
94
- VALUE resume_value = rb_ary_new_from_args(
95
- 2, INT2NUM(ev_child->rpid), INT2NUM(exit_status)
96
- );
97
- child->fiber = Qnil;
98
- Gyro_schedule_fiber(fiber, resume_value);
99
- }
100
- }
101
-
102
81
  static VALUE Gyro_Child_await(VALUE self) {
103
82
  struct Gyro_Child *child;
104
83
  VALUE ret;
105
84
 
106
85
  GetGyro_Child(self, child);
107
86
 
108
- child->fiber = rb_fiber_current();
87
+ if (child->active)
109
88
  child->active = 1;
110
- ev_child_start(EV_DEFAULT, &child->ev_child);
89
+ child->fiber = rb_fiber_current();
90
+ child->ev_loop = Gyro_Selector_current_thread_ev_loop();
91
+ ev_child_start(child->ev_loop, &child->ev_child);
92
+
93
+ ret = Fiber_await();
94
+ RB_GC_GUARD(ret);
111
95
 
112
- ret = Gyro_await();
96
+ if (child->active) {
97
+ child->active = 0;
98
+ child->fiber = Qnil;
99
+ ev_child_stop(child->ev_loop, &child->ev_child);
100
+ }
113
101
 
114
102
  // fiber is resumed, check if resumed value is an exception
115
103
  if (RTEST(rb_obj_is_kind_of(ret, rb_eException))) {
116
- printf("* child error\n");
117
- if (child->active) {
118
- child->active = 0;
119
- ev_child_stop(EV_DEFAULT, &child->ev_child);
120
- }
121
104
  return rb_funcall(rb_mKernel, ID_raise, 1, ret);
122
105
  }
123
106
  else {
124
107
  return ret;
125
108
  }
126
- }
109
+ }
110
+
111
+ void Init_Gyro_Child() {
112
+ cGyro_Child = rb_define_class_under(mGyro, "Child", rb_cData);
113
+ rb_define_alloc_func(cGyro_Child, Gyro_Child_allocate);
114
+
115
+ rb_define_method(cGyro_Child, "initialize", Gyro_Child_initialize, 1);
116
+ rb_define_method(cGyro_Child, "await", Gyro_Child_await, 0);
117
+ }
@@ -1,138 +1,49 @@
1
1
  #include "gyro.h"
2
2
 
3
- static VALUE Gyro_break_set(VALUE self);
4
- static VALUE Gyro_break_get(VALUE self);
5
-
6
- static VALUE Gyro_ref(VALUE self);
7
- static VALUE Gyro_unref(VALUE self);
8
-
9
- static VALUE Gyro_run(VALUE self);
10
- static VALUE Gyro_reset(VALUE self);
11
- static VALUE Gyro_post_fork(VALUE self);
12
- static VALUE Gyro_suspend(VALUE self);
13
-
14
- static VALUE Fiber_safe_transfer(int argc, VALUE *argv, VALUE self);
15
- static VALUE Fiber_schedule(int argc, VALUE *argv, VALUE self);
16
- static VALUE Fiber_state(VALUE self);
17
-
18
- static void Gyro_clear_scheduled_fibers();
19
-
20
3
  VALUE mGyro;
21
4
 
22
- int break_flag = 0;
23
- int ref_count = 0;
24
-
25
- static VALUE scheduled_head;
26
- static VALUE scheduled_tail;
27
-
28
5
  ID ID_call;
29
6
  ID ID_caller;
30
7
  ID ID_clear;
31
8
  ID ID_each;
32
9
  ID ID_inspect;
10
+ ID ID_new;
33
11
  ID ID_raise;
34
- ID ID_running;
12
+ ID ID_ivar_running;
35
13
  ID ID_scheduled;
36
- ID ID_scheduled_next;
37
14
  ID ID_scheduled_value;
15
+ ID ID_size;
16
+ ID ID_signal_bang;
17
+ ID ID_switch_fiber;
38
18
  ID ID_transfer;
39
19
  ID ID_R;
40
20
  ID ID_W;
41
21
  ID ID_RW;
42
22
 
43
- VALUE SYM_DEAD;
44
- VALUE SYM_RUNNING;
45
- VALUE SYM_SCHEDULED;
46
- VALUE SYM_SUSPENDED;
47
-
48
- void Init_Gyro() {
49
- mGyro = rb_define_module("Gyro");
50
-
51
- rb_define_singleton_method(mGyro, "ref", Gyro_ref, 0);
52
- rb_define_singleton_method(mGyro, "unref", Gyro_unref, 0);
53
-
54
- rb_define_singleton_method(mGyro, "run", Gyro_run, 0);
55
- rb_define_singleton_method(mGyro, "reset!", Gyro_reset, 0);
56
- rb_define_singleton_method(mGyro, "post_fork", Gyro_post_fork, 0);
57
- rb_define_singleton_method(mGyro, "snooze", Gyro_snooze, 0);
58
-
59
- rb_define_singleton_method(mGyro, "break!", Gyro_break_set, 0);
60
- rb_define_singleton_method(mGyro, "break?", Gyro_break_get, 0);
61
-
62
- rb_define_global_function("snooze", Gyro_snooze, 0);
63
- rb_define_global_function("suspend", Gyro_suspend, 0);
64
-
65
- VALUE cFiber = rb_const_get(rb_cObject, rb_intern("Fiber"));
66
- rb_define_method(cFiber, "safe_transfer", Fiber_safe_transfer, -1);
67
- rb_define_method(cFiber, "schedule", Fiber_schedule, -1);
68
- rb_define_method(cFiber, "state", Fiber_state, 0);
69
-
70
- ID_call = rb_intern("call");
71
- ID_caller = rb_intern("caller");
72
- ID_clear = rb_intern("clear");
73
- ID_each = rb_intern("each");
74
- ID_inspect = rb_intern("inspect");
75
- ID_raise = rb_intern("raise");
76
- ID_running = rb_intern("@running");
77
- ID_scheduled = rb_intern("scheduled");
78
- ID_scheduled_next = rb_intern("scheduled_next");
79
- ID_scheduled_value = rb_intern("scheduled_value");
80
- ID_transfer = rb_intern("transfer");
81
- ID_R = rb_intern("r");
82
- ID_W = rb_intern("w");
83
- ID_RW = rb_intern("rw");
84
-
85
- SYM_DEAD = ID2SYM(rb_intern("dead"));
86
- SYM_RUNNING = ID2SYM(rb_intern("running"));
87
- SYM_SCHEDULED = ID2SYM(rb_intern("scheduled"));
88
- SYM_SUSPENDED = ID2SYM(rb_intern("suspended"));
89
- rb_global_variable(&SYM_DEAD);
90
- rb_global_variable(&SYM_RUNNING);
91
- rb_global_variable(&SYM_SCHEDULED);
92
- rb_global_variable(&SYM_SUSPENDED);
93
-
94
- scheduled_head = Qnil;
95
- scheduled_tail = Qnil;
96
- rb_global_variable(&scheduled_head);
97
- }
98
-
99
- static VALUE Gyro_ref(VALUE self) {
100
- Gyro_ref_count_incr();
101
- return Qnil;
102
- }
103
-
104
- static VALUE Gyro_unref(VALUE self) {
105
- Gyro_ref_count_decr();
106
- return Qnil;
107
- }
108
-
109
- static VALUE Gyro_run(VALUE self) {
110
- return Gyro_run_next_fiber();
111
- }
23
+ ID ID_empty;
24
+ ID ID_pop;
25
+ ID ID_push;
112
26
 
113
- static VALUE Gyro_reset(VALUE self) {
114
- break_flag = 0;
115
- ref_count = 0;
27
+ VALUE SYM_dead;
28
+ VALUE SYM_running;
29
+ VALUE SYM_scheduled;
30
+ VALUE SYM_suspended;
116
31
 
117
- Gyro_clear_scheduled_fibers();
118
- return Qnil;
119
- }
32
+ // static VALUE Gyro_break_set(VALUE self) {
33
+ // break_flag = 1;
34
+ // ev_break(EV_DEFAULT, EVBREAK_ALL);
35
+ // return Qnil;
36
+ // }
120
37
 
121
- static VALUE Gyro_break_set(VALUE self) {
122
- break_flag = 1;
123
- ev_break(EV_DEFAULT, EVBREAK_ALL);
124
- return Qnil;
125
- }
126
-
127
- static VALUE Gyro_break_get(VALUE self) {
128
- return (break_flag == 0) ? Qfalse : Qtrue;
129
- }
38
+ // static VALUE Gyro_break_get(VALUE self) {
39
+ // return (break_flag == 0) ? Qfalse : Qtrue;
40
+ // }
130
41
 
131
42
  VALUE Gyro_snooze(VALUE self) {
132
43
  VALUE fiber = rb_fiber_current();
133
44
  Gyro_schedule_fiber(fiber, Qnil);
134
45
 
135
- VALUE ret = Gyro_run_next_fiber();
46
+ VALUE ret = Thread_switch_fiber(rb_thread_current());
136
47
  if (RTEST(rb_obj_is_kind_of(ret, rb_eException)))
137
48
  return rb_funcall(rb_mKernel, ID_raise, 1, ret);
138
49
  else
@@ -140,18 +51,22 @@ VALUE Gyro_snooze(VALUE self) {
140
51
  }
141
52
 
142
53
  static VALUE Gyro_post_fork(VALUE self) {
143
- ev_loop_fork(EV_DEFAULT);
144
- break_flag = 0;
145
- ref_count = 0;
146
-
147
- Gyro_clear_scheduled_fibers();
148
-
54
+ Thread_post_fork(rb_thread_current());
149
55
  return Qnil;
150
56
  }
151
57
 
58
+ static VALUE Gyro_ref(VALUE self) {
59
+ return Thread_ref(rb_thread_current());
60
+ }
61
+
62
+ static VALUE Gyro_unref(VALUE self) {
63
+ return Thread_unref(rb_thread_current());
64
+ }
65
+
152
66
  static VALUE Gyro_suspend(VALUE self) {
153
67
  rb_ivar_set(self, ID_scheduled_value, Qnil);
154
- VALUE ret = Gyro_run_next_fiber();
68
+ VALUE ret = Thread_switch_fiber(rb_thread_current());
69
+
155
70
  if (RTEST(rb_obj_is_kind_of(ret, rb_eException))) {
156
71
  return rb_funcall(rb_mKernel, ID_raise, 1, ret);
157
72
  }
@@ -176,93 +91,70 @@ static VALUE Fiber_schedule(int argc, VALUE *argv, VALUE self) {
176
91
  }
177
92
 
178
93
  static VALUE Fiber_state(VALUE self) {
179
- if (!rb_fiber_alive_p(self) || (rb_ivar_get(self, ID_running) == Qfalse))
180
- return SYM_DEAD;
181
- if (rb_fiber_current() == self) return SYM_RUNNING;
182
- if (rb_ivar_get(self, ID_scheduled) != Qnil) return SYM_SCHEDULED;
94
+ if (!rb_fiber_alive_p(self) || (rb_ivar_get(self, ID_ivar_running) == Qfalse))
95
+ return SYM_dead;
96
+ if (rb_fiber_current() == self) return SYM_running;
97
+ if (rb_ivar_get(self, ID_scheduled) != Qnil) return SYM_scheduled;
183
98
 
184
- return SYM_SUSPENDED;
185
- }
186
-
187
- VALUE Gyro_await() {
188
- Gyro_ref_count_incr();
189
- VALUE ret = Gyro_run_next_fiber();
190
- Gyro_ref_count_decr();
191
- return ret;
192
- }
193
-
194
- VALUE Gyro_run_next_fiber() {
195
- while (1) {
196
- if (break_flag != 0) {
197
- return Qnil;
198
- }
199
- if ((scheduled_head != Qnil) || (ref_count == 0)) {
200
- break;
201
- }
202
- ev_run(EV_DEFAULT, EVRUN_ONCE);
203
- }
204
-
205
- // return if no fiber is scheduled
206
- if (scheduled_head == Qnil) {
207
- return Qnil;
208
- }
209
-
210
- // update scheduled linked list refs
211
- VALUE next_fiber = scheduled_head;
212
- VALUE next_next_fiber = rb_ivar_get(next_fiber, ID_scheduled_next);
213
- rb_ivar_set(next_fiber, ID_scheduled_next, Qnil);
214
- scheduled_head = next_next_fiber;
215
- if (scheduled_head == Qnil) {
216
- scheduled_tail = Qnil;
217
- }
218
-
219
- if (rb_fiber_alive_p(next_fiber) != Qtrue) {
220
- return Qnil;
221
- }
222
-
223
- // run next fiber
224
- VALUE value = rb_ivar_get(next_fiber, ID_scheduled_value);
225
- rb_ivar_set(next_fiber, ID_scheduled_value, Qnil);
226
- rb_ivar_set(next_fiber, ID_scheduled, Qnil);
227
- return rb_funcall(next_fiber, ID_transfer, 1, value);
99
+ return SYM_suspended;
228
100
  }
229
101
 
230
- void Gyro_schedule_fiber(VALUE fiber, VALUE value) {
102
+ inline void Gyro_schedule_fiber(VALUE fiber, VALUE value) {
231
103
  rb_ivar_set(fiber, ID_scheduled_value, value);
232
104
  // if fiber is already scheduled, we just set the scheduled value, then return
233
105
  if (rb_ivar_get(fiber, ID_scheduled) != Qnil)
234
106
  return;
235
107
 
236
108
  rb_ivar_set(fiber, ID_scheduled, Qtrue);
237
-
238
- // put fiber on scheduled list
239
- if (scheduled_head != Qnil) {
240
- VALUE last = scheduled_tail;
241
- rb_ivar_set(last, ID_scheduled_next, fiber);
242
- scheduled_tail = fiber;
243
- }
244
- else {
245
- scheduled_tail = scheduled_head = fiber;
246
- }
109
+ Thread_schedule_fiber(rb_thread_current(), fiber);
247
110
  }
248
111
 
249
- int Gyro_ref_count() {
250
- return ref_count;
251
- }
112
+ void Init_Gyro() {
113
+ mGyro = rb_define_module("Gyro");
252
114
 
253
- void Gyro_ref_count_incr() {
254
- ref_count += 1;
255
- }
115
+ rb_define_singleton_method(mGyro, "post_fork", Gyro_post_fork, 0);
116
+ rb_define_singleton_method(mGyro, "ref", Gyro_ref, 0);
117
+ rb_define_singleton_method(mGyro, "unref", Gyro_unref, 0);
256
118
 
257
- void Gyro_ref_count_decr() {
258
- ref_count -= 1;
259
- }
119
+ // rb_define_singleton_method(mGyro, "break!", Gyro_break_set, 0);
120
+ // rb_define_singleton_method(mGyro, "break?", Gyro_break_get, 0);
260
121
 
261
- static void Gyro_clear_scheduled_fibers() {
262
- while (scheduled_head != Qnil) {
263
- VALUE fiber = scheduled_head;
264
- scheduled_head = rb_ivar_get(fiber, ID_scheduled_next);
265
- rb_ivar_set(fiber, ID_scheduled_next, Qnil);
266
- }
267
- scheduled_tail = Qnil;
122
+ rb_define_global_function("snooze", Gyro_snooze, 0);
123
+ rb_define_global_function("suspend", Gyro_suspend, 0);
124
+
125
+ VALUE cFiber = rb_const_get(rb_cObject, rb_intern("Fiber"));
126
+ rb_define_method(cFiber, "safe_transfer", Fiber_safe_transfer, -1);
127
+ rb_define_method(cFiber, "schedule", Fiber_schedule, -1);
128
+ rb_define_method(cFiber, "state", Fiber_state, 0);
129
+
130
+ ID_call = rb_intern("call");
131
+ ID_caller = rb_intern("caller");
132
+ ID_clear = rb_intern("clear");
133
+ ID_each = rb_intern("each");
134
+ ID_inspect = rb_intern("inspect");
135
+ ID_new = rb_intern("new");
136
+ ID_raise = rb_intern("raise");
137
+ ID_ivar_running = rb_intern("@running");
138
+ ID_scheduled = rb_intern("scheduled");
139
+ ID_scheduled_value = rb_intern("scheduled_value");
140
+ ID_size = rb_intern("size");
141
+ ID_signal_bang = rb_intern("signal!");
142
+ ID_switch_fiber = rb_intern("switch_fiber");
143
+ ID_transfer = rb_intern("transfer");
144
+ ID_R = rb_intern("r");
145
+ ID_W = rb_intern("w");
146
+ ID_RW = rb_intern("rw");
147
+
148
+ ID_empty = rb_intern("empty?");
149
+ ID_pop = rb_intern("pop");
150
+ ID_push = rb_intern("push");
151
+
152
+ SYM_dead = ID2SYM(rb_intern("dead"));
153
+ SYM_running = ID2SYM(rb_intern("running"));
154
+ SYM_scheduled = ID2SYM(rb_intern("scheduled"));
155
+ SYM_suspended = ID2SYM(rb_intern("suspended"));
156
+ rb_global_variable(&SYM_dead);
157
+ rb_global_variable(&SYM_running);
158
+ rb_global_variable(&SYM_scheduled);
159
+ rb_global_variable(&SYM_suspended);
268
160
  }