polyphony 0.43.8 → 0.45.0

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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +7 -1
  3. data/CHANGELOG.md +38 -0
  4. data/Gemfile.lock +13 -11
  5. data/README.md +20 -5
  6. data/Rakefile +1 -1
  7. data/TODO.md +16 -14
  8. data/bin/stress.rb +28 -0
  9. data/docs/_posts/2020-07-26-polyphony-0.44.md +77 -0
  10. data/docs/api-reference/thread.md +1 -1
  11. data/docs/getting-started/overview.md +14 -14
  12. data/docs/getting-started/tutorial.md +1 -1
  13. data/examples/adapters/sequel_mysql.rb +23 -0
  14. data/examples/adapters/sequel_mysql_pool.rb +33 -0
  15. data/examples/core/{xx-agent.rb → xx-backend.rb} +5 -5
  16. data/examples/core/xx-channels.rb +4 -2
  17. data/examples/core/xx-using-a-mutex.rb +2 -1
  18. data/examples/io/xx-pry.rb +18 -0
  19. data/examples/io/xx-rack_server.rb +71 -0
  20. data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +1 -1
  21. data/ext/polyphony/backend.h +41 -0
  22. data/ext/polyphony/event.c +86 -0
  23. data/ext/polyphony/extconf.rb +1 -1
  24. data/ext/polyphony/fiber.c +0 -5
  25. data/ext/polyphony/{libev_agent.c → libev_backend.c} +234 -228
  26. data/ext/polyphony/polyphony.c +4 -0
  27. data/ext/polyphony/polyphony.h +16 -16
  28. data/ext/polyphony/polyphony_ext.c +4 -2
  29. data/ext/polyphony/queue.c +52 -12
  30. data/ext/polyphony/thread.c +55 -42
  31. data/lib/polyphony.rb +25 -39
  32. data/lib/polyphony/adapters/irb.rb +2 -17
  33. data/lib/polyphony/adapters/mysql2.rb +19 -0
  34. data/lib/polyphony/adapters/postgres.rb +5 -5
  35. data/lib/polyphony/adapters/process.rb +2 -2
  36. data/lib/polyphony/adapters/readline.rb +17 -0
  37. data/lib/polyphony/adapters/sequel.rb +45 -0
  38. data/lib/polyphony/core/channel.rb +3 -34
  39. data/lib/polyphony/core/exceptions.rb +11 -0
  40. data/lib/polyphony/core/global_api.rb +11 -6
  41. data/lib/polyphony/core/resource_pool.rb +22 -71
  42. data/lib/polyphony/core/sync.rb +48 -9
  43. data/lib/polyphony/core/throttler.rb +1 -1
  44. data/lib/polyphony/extensions/core.rb +37 -19
  45. data/lib/polyphony/extensions/fiber.rb +5 -1
  46. data/lib/polyphony/extensions/io.rb +7 -8
  47. data/lib/polyphony/extensions/openssl.rb +6 -6
  48. data/lib/polyphony/extensions/socket.rb +12 -22
  49. data/lib/polyphony/extensions/thread.rb +6 -5
  50. data/lib/polyphony/net.rb +2 -1
  51. data/lib/polyphony/version.rb +1 -1
  52. data/polyphony.gemspec +6 -3
  53. data/test/helper.rb +1 -1
  54. data/test/{test_agent.rb → test_backend.rb} +22 -22
  55. data/test/test_event.rb +1 -0
  56. data/test/test_fiber.rb +21 -5
  57. data/test/test_io.rb +1 -1
  58. data/test/test_kernel.rb +5 -0
  59. data/test/test_queue.rb +20 -0
  60. data/test/test_resource_pool.rb +34 -43
  61. data/test/test_signal.rb +5 -29
  62. data/test/test_sync.rb +52 -0
  63. metadata +74 -30
  64. data/.gitbook.yaml +0 -4
  65. data/lib/polyphony/event.rb +0 -17
@@ -7,6 +7,7 @@ ID ID_caller;
7
7
  ID ID_clear;
8
8
  ID ID_each;
9
9
  ID ID_inspect;
10
+ ID ID_invoke;
10
11
  ID ID_new;
11
12
  ID ID_raise;
12
13
  ID ID_ivar_running;
@@ -21,6 +22,8 @@ ID ID_R;
21
22
  ID ID_W;
22
23
  ID ID_RW;
23
24
 
25
+ backend_interface_t backend_interface;
26
+
24
27
  VALUE Polyphony_snooze(VALUE self) {
25
28
  VALUE ret;
26
29
  VALUE fiber = rb_fiber_current();
@@ -58,6 +61,7 @@ void Init_Polyphony() {
58
61
  ID_clear = rb_intern("clear");
59
62
  ID_each = rb_intern("each");
60
63
  ID_inspect = rb_intern("inspect");
64
+ ID_invoke = rb_intern("invoke");
61
65
  ID_ivar_running = rb_intern("@running");
62
66
  ID_ivar_thread = rb_intern("@thread");
63
67
  ID_new = rb_intern("new");
@@ -4,20 +4,30 @@
4
4
  #include "ruby.h"
5
5
  #include "ruby/io.h"
6
6
  #include "libev.h"
7
+ #include "backend.h"
7
8
 
8
9
  // debugging
9
10
  #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__); \
11
+ #define INSPECT(str, obj) { printf(str); VALUE s = rb_funcall(obj, rb_intern("inspect"), 0); printf("%s\n", StringValueCStr(s)); }
12
+ #define TRACE_CALLER() { VALUE c = rb_funcall(rb_mKernel, rb_intern("caller"), 0); INSPECT("caller: ", c); }
13
+
14
+ // tracing
15
+ #define TRACE(...) rb_funcall(rb_cObject, ID_fiber_trace, __VA_ARGS__)
16
+ #define COND_TRACE(...) if (__tracing_enabled__) { \
17
+ TRACE(__VA_ARGS__); \
13
18
  }
14
19
 
15
20
  #define TEST_EXCEPTION(ret) (RTEST(rb_obj_is_kind_of(ret, rb_eException)))
16
21
 
22
+ #define RAISE_EXCEPTION(e) rb_funcall(e, ID_invoke, 0);
17
23
  #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); \
24
+ return RAISE_EXCEPTION(ret); \
19
25
  }
20
26
 
27
+
28
+ extern backend_interface_t backend_interface;
29
+ #define __BACKEND__ (backend_interface)
30
+
21
31
  extern VALUE mPolyphony;
22
32
  extern VALUE cQueue;
23
33
  extern VALUE cEvent;
@@ -28,7 +38,8 @@ extern ID ID_clear;
28
38
  extern ID ID_each;
29
39
  extern ID ID_fiber_trace;
30
40
  extern ID ID_inspect;
31
- extern ID ID_ivar_agent;
41
+ extern ID ID_invoke;
42
+ extern ID ID_ivar_backend;
32
43
  extern ID ID_ivar_running;
33
44
  extern ID ID_ivar_thread;
34
45
  extern ID ID_new;
@@ -65,17 +76,6 @@ enum {
65
76
  VALUE Fiber_auto_watcher(VALUE self);
66
77
  void Fiber_make_runnable(VALUE fiber, VALUE value);
67
78
 
68
- VALUE LibevAgent_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue);
69
- VALUE LibevAgent_break(VALUE self);
70
- VALUE LibevAgent_pending_count(VALUE self);
71
- VALUE LibevAgent_wait_io(VALUE self, VALUE io, VALUE write);
72
-
73
- VALUE LibevAgent_ref(VALUE self);
74
- VALUE LibevAgent_unref(VALUE self);
75
- int LibevAgent_ref_count(VALUE self);
76
- void LibevAgent_reset_ref_count(VALUE self);
77
- VALUE LibevAgent_wait_event(VALUE self, VALUE raise);
78
-
79
79
  VALUE Queue_push(VALUE self, VALUE value);
80
80
  VALUE Queue_unshift(VALUE self, VALUE value);
81
81
  VALUE Queue_shift(VALUE self);
@@ -2,8 +2,9 @@
2
2
 
3
3
  void Init_Fiber();
4
4
  void Init_Polyphony();
5
- void Init_LibevAgent();
5
+ void Init_LibevBackend();
6
6
  void Init_Queue();
7
+ void Init_Event();
7
8
  void Init_Thread();
8
9
  void Init_Tracing();
9
10
 
@@ -11,8 +12,9 @@ void Init_polyphony_ext() {
11
12
  ev_set_allocator(xrealloc);
12
13
 
13
14
  Init_Polyphony();
14
- Init_LibevAgent();
15
+ Init_LibevBackend();
15
16
  Init_Queue();
17
+ Init_Event();
16
18
 
17
19
  Init_Fiber();
18
20
  Init_Thread();
@@ -54,6 +54,7 @@ static VALUE Queue_initialize(VALUE self) {
54
54
  VALUE Queue_push(VALUE self, VALUE value) {
55
55
  Queue_t *queue;
56
56
  GetQueue(self, queue);
57
+
57
58
  if (queue->shift_queue.count > 0) {
58
59
  VALUE fiber = ring_buffer_shift(&queue->shift_queue);
59
60
  if (fiber != Qnil) Fiber_make_runnable(fiber, Qnil);
@@ -77,21 +78,26 @@ VALUE Queue_shift(VALUE self) {
77
78
  Queue_t *queue;
78
79
  GetQueue(self, queue);
79
80
 
80
- if (queue->values.count == 0) {
81
- VALUE agent = rb_ivar_get(rb_thread_current(), ID_ivar_agent);
82
- VALUE fiber = rb_fiber_current();
83
- VALUE switchpoint_result = Qnil;
81
+ VALUE fiber = rb_fiber_current();
82
+ VALUE thread = rb_thread_current();
83
+ VALUE backend = rb_ivar_get(thread, ID_ivar_backend);
84
+
85
+ while (1) {
84
86
  ring_buffer_push(&queue->shift_queue, fiber);
85
- switchpoint_result = LibevAgent_wait_event(agent, Qnil);
86
- if (RTEST(rb_obj_is_kind_of(switchpoint_result, rb_eException))) {
87
- ring_buffer_delete(&queue->shift_queue, fiber);
87
+ if (queue->values.count > 0) Fiber_make_runnable(fiber, Qnil);
88
+
89
+ VALUE switchpoint_result = __BACKEND__.wait_event(backend, Qnil);
90
+ ring_buffer_delete(&queue->shift_queue, fiber);
91
+
92
+ if (RTEST(rb_obj_is_kind_of(switchpoint_result, rb_eException)))
88
93
  return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
89
- }
90
- RB_GC_GUARD(agent);
91
94
  RB_GC_GUARD(switchpoint_result);
95
+
96
+ if (queue->values.count > 0)
97
+ return ring_buffer_shift(&queue->values);
92
98
  }
93
99
 
94
- return ring_buffer_shift(&queue->values);
100
+ return Qnil;
95
101
  }
96
102
 
97
103
  VALUE Queue_shift_no_wait(VALUE self) {
@@ -139,6 +145,18 @@ VALUE Queue_shift_all(VALUE self) {
139
145
  return ring_buffer_shift_all(&queue->values);
140
146
  }
141
147
 
148
+ VALUE Queue_flush_waiters(VALUE self, VALUE value) {
149
+ Queue_t *queue;
150
+ GetQueue(self, queue);
151
+
152
+ while(1) {
153
+ VALUE fiber = ring_buffer_shift(&queue->shift_queue);
154
+ if (fiber == Qnil) return self;
155
+
156
+ Fiber_make_runnable(fiber, value);
157
+ }
158
+ }
159
+
142
160
  VALUE Queue_empty_p(VALUE self) {
143
161
  Queue_t *queue;
144
162
  GetQueue(self, queue);
@@ -146,6 +164,27 @@ VALUE Queue_empty_p(VALUE self) {
146
164
  return (queue->values.count == 0) ? Qtrue : Qfalse;
147
165
  }
148
166
 
167
+ VALUE Queue_pending_p(VALUE self) {
168
+ Queue_t *queue;
169
+ GetQueue(self, queue);
170
+
171
+ return (queue->shift_queue.count > 0) ? Qtrue : Qfalse;
172
+ }
173
+
174
+ VALUE Queue_size_m(VALUE self) {
175
+ Queue_t *queue;
176
+ GetQueue(self, queue);
177
+
178
+ return INT2NUM(queue->values.count);
179
+ }
180
+
181
+ void Queue_trace(VALUE self) {
182
+ Queue_t *queue;
183
+ GetQueue(self, queue);
184
+
185
+ printf("run queue size: %d count: %d\n", queue->values.size, queue->values.count);
186
+ }
187
+
149
188
  void Init_Queue() {
150
189
  cQueue = rb_define_class_under(mPolyphony, "Queue", rb_cData);
151
190
  rb_define_alloc_func(cQueue, Queue_allocate);
@@ -162,7 +201,8 @@ void Init_Queue() {
162
201
 
163
202
  rb_define_method(cQueue, "shift_each", Queue_shift_each, 0);
164
203
  rb_define_method(cQueue, "shift_all", Queue_shift_all, 0);
204
+ rb_define_method(cQueue, "flush_waiters", Queue_flush_waiters, 1);
165
205
  rb_define_method(cQueue, "empty?", Queue_empty_p, 0);
206
+ rb_define_method(cQueue, "pending?", Queue_pending_p, 0);
207
+ rb_define_method(cQueue, "size", Queue_size_m, 0);
166
208
  }
167
-
168
-
@@ -1,7 +1,7 @@
1
1
  #include "polyphony.h"
2
2
 
3
3
  ID ID_deactivate_all_watchers_post_fork;
4
- ID ID_ivar_agent;
4
+ ID ID_ivar_backend;
5
5
  ID ID_ivar_join_wait_queue;
6
6
  ID ID_ivar_main_fiber;
7
7
  ID ID_ivar_result;
@@ -20,20 +20,20 @@ static VALUE Thread_setup_fiber_scheduling(VALUE self) {
20
20
  }
21
21
 
22
22
  int Thread_fiber_ref_count(VALUE self) {
23
- VALUE agent = rb_ivar_get(self, ID_ivar_agent);
24
- return NUM2INT(LibevAgent_ref_count(agent));
23
+ VALUE backend = rb_ivar_get(self, ID_ivar_backend);
24
+ return NUM2INT(__BACKEND__.ref_count(backend));
25
25
  }
26
26
 
27
27
  inline void Thread_fiber_reset_ref_count(VALUE self) {
28
- VALUE agent = rb_ivar_get(self, ID_ivar_agent);
29
- LibevAgent_reset_ref_count(agent);
28
+ VALUE backend = rb_ivar_get(self, ID_ivar_backend);
29
+ __BACKEND__.reset_ref_count(backend);
30
30
  }
31
31
 
32
32
  static VALUE SYM_scheduled_fibers;
33
33
  static VALUE SYM_pending_watchers;
34
34
 
35
35
  static VALUE Thread_fiber_scheduling_stats(VALUE self) {
36
- VALUE agent = rb_ivar_get(self,ID_ivar_agent);
36
+ VALUE backend = rb_ivar_get(self,ID_ivar_backend);
37
37
  VALUE stats = rb_hash_new();
38
38
  VALUE queue = rb_ivar_get(self, ID_run_queue);
39
39
  long pending_count;
@@ -41,7 +41,7 @@ static VALUE Thread_fiber_scheduling_stats(VALUE self) {
41
41
  long scheduled_count = RARRAY_LEN(queue);
42
42
  rb_hash_aset(stats, SYM_scheduled_fibers, INT2NUM(scheduled_count));
43
43
 
44
- pending_count = LibevAgent_pending_count(agent);
44
+ pending_count = __BACKEND__.pending_count(backend);
45
45
  rb_hash_aset(stats, SYM_pending_watchers, INT2NUM(pending_count));
46
46
 
47
47
  return stats;
@@ -52,25 +52,34 @@ VALUE Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
52
52
 
53
53
  if (rb_fiber_alive_p(fiber) != Qtrue) return self;
54
54
 
55
- FIBER_TRACE(3, SYM_fiber_schedule, fiber, value);
56
- // if fiber is already scheduled, just set the scheduled value, then return
57
- rb_ivar_set(fiber, ID_runnable_value, value);
58
- if (rb_ivar_get(fiber, ID_runnable) != Qnil) {
59
- return self;
55
+ int already_runnable = rb_ivar_get(fiber, ID_runnable) != Qnil;
56
+
57
+ if (already_runnable) {
58
+ VALUE current_runnable_value = rb_ivar_get(fiber, ID_runnable_value);
59
+
60
+ // If the fiber is already runnable and the runnable value is an exception,
61
+ // we don't update the value, in order to prevent a race condition where
62
+ // exceptions will be lost (see issue #33)
63
+ if (TEST_EXCEPTION(current_runnable_value)) return self;
60
64
  }
61
65
 
62
- queue = rb_ivar_get(self, ID_run_queue);
63
- Queue_push(queue, fiber);
64
- rb_ivar_set(fiber, ID_runnable, Qtrue);
66
+ rb_ivar_set(fiber, ID_runnable_value, value);
67
+ COND_TRACE(3, SYM_fiber_schedule, fiber, value);
65
68
 
66
- if (rb_thread_current() != self) {
67
- // if the fiber scheduling is done across threads, we need to make sure the
68
- // target thread is woken up in case it is in the middle of running its
69
- // event selector. Otherwise it's gonna be stuck waiting for an event to
70
- // happen, not knowing that it there's already a fiber ready to run in its
71
- // run queue.
72
- VALUE agent = rb_ivar_get(self,ID_ivar_agent);
73
- LibevAgent_break(agent);
69
+ if (!already_runnable) {
70
+ queue = rb_ivar_get(self, ID_run_queue);
71
+ Queue_push(queue, fiber);
72
+ rb_ivar_set(fiber, ID_runnable, Qtrue);
73
+
74
+ if (rb_thread_current() != self) {
75
+ // If the fiber scheduling is done across threads, we need to make sure the
76
+ // target thread is woken up in case it is in the middle of running its
77
+ // event selector. Otherwise it's gonna be stuck waiting for an event to
78
+ // happen, not knowing that it there's already a fiber ready to run in its
79
+ // run queue.
80
+ VALUE backend = rb_ivar_get(self,ID_ivar_backend);
81
+ __BACKEND__.wakeup(backend);
82
+ }
74
83
  }
75
84
  return self;
76
85
  }
@@ -80,7 +89,7 @@ VALUE Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value)
80
89
 
81
90
  if (rb_fiber_alive_p(fiber) != Qtrue) return self;
82
91
 
83
- FIBER_TRACE(3, SYM_fiber_schedule, fiber, value);
92
+ COND_TRACE(3, SYM_fiber_schedule, fiber, value);
84
93
  rb_ivar_set(fiber, ID_runnable_value, value);
85
94
 
86
95
  queue = rb_ivar_get(self, ID_run_queue);
@@ -101,8 +110,8 @@ VALUE Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value)
101
110
  // event loop. Otherwise it's gonna be stuck waiting for an event to
102
111
  // happen, not knowing that it there's already a fiber ready to run in its
103
112
  // run queue.
104
- VALUE agent = rb_ivar_get(self, ID_ivar_agent);
105
- LibevAgent_break(agent);
113
+ VALUE backend = rb_ivar_get(self, ID_ivar_backend);
114
+ __BACKEND__.wakeup(backend);
106
115
  }
107
116
  return self;
108
117
  }
@@ -112,38 +121,35 @@ VALUE Thread_switch_fiber(VALUE self) {
112
121
  VALUE queue = rb_ivar_get(self, ID_run_queue);
113
122
  VALUE next_fiber;
114
123
  VALUE value;
115
- VALUE agent = rb_ivar_get(self, ID_ivar_agent);
124
+ VALUE backend = rb_ivar_get(self, ID_ivar_backend);
116
125
  int ref_count;
117
- int agent_was_polled = 0;1;
126
+ int backend_was_polled = 0;
118
127
 
119
- if (__tracing_enabled__) {
120
- if (rb_ivar_get(current_fiber, ID_ivar_running) != Qfalse) {
121
- rb_funcall(rb_cObject, ID_fiber_trace, 2, SYM_fiber_switchpoint, current_fiber);
122
- }
123
- }
128
+ if (__tracing_enabled__ && (rb_ivar_get(current_fiber, ID_ivar_running) != Qfalse))
129
+ TRACE(2, SYM_fiber_switchpoint, current_fiber);
124
130
 
125
- ref_count = LibevAgent_ref_count(agent);
131
+ ref_count = __BACKEND__.ref_count(backend);
126
132
  while (1) {
127
133
  next_fiber = Queue_shift_no_wait(queue);
128
134
  if (next_fiber != Qnil) {
129
- if (agent_was_polled == 0 && ref_count > 0) {
135
+ if (backend_was_polled == 0 && ref_count > 0) {
130
136
  // this mechanism prevents event starvation in case the run queue never
131
137
  // empties
132
- LibevAgent_poll(agent, Qtrue, current_fiber, queue);
138
+ __BACKEND__.poll(backend, Qtrue, current_fiber, queue);
133
139
  }
134
140
  break;
135
141
  }
136
142
  if (ref_count == 0) break;
137
143
 
138
- LibevAgent_poll(agent, Qnil, current_fiber, queue);
139
- agent_was_polled = 1;
144
+ __BACKEND__.poll(backend, Qnil, current_fiber, queue);
145
+ backend_was_polled = 1;
140
146
  }
141
147
 
142
148
  if (next_fiber == Qnil) return Qnil;
143
149
 
144
150
  // run next fiber
145
151
  value = rb_ivar_get(next_fiber, ID_runnable_value);
146
- FIBER_TRACE(3, SYM_fiber_run, next_fiber, value);
152
+ COND_TRACE(3, SYM_fiber_run, next_fiber, value);
147
153
 
148
154
  rb_ivar_set(next_fiber, ID_runnable, Qnil);
149
155
  RB_GC_GUARD(next_fiber);
@@ -166,12 +172,12 @@ VALUE Thread_reset_fiber_scheduling(VALUE self) {
166
172
  }
167
173
 
168
174
  VALUE Thread_fiber_break_out_of_ev_loop(VALUE self, VALUE fiber, VALUE resume_obj) {
169
- VALUE agent = rb_ivar_get(self, ID_ivar_agent);
175
+ VALUE backend = rb_ivar_get(self, ID_ivar_backend);
170
176
  if (fiber != Qnil) {
171
177
  Thread_schedule_fiber_with_priority(self, fiber, resume_obj);
172
178
  }
173
179
 
174
- if (LibevAgent_break(agent) == Qnil) {
180
+ if (__BACKEND__.wakeup(backend) == Qnil) {
175
181
  // we're not inside the ev_loop, so we just do a switchpoint
176
182
  Thread_switch_fiber(self);
177
183
  }
@@ -179,6 +185,11 @@ VALUE Thread_fiber_break_out_of_ev_loop(VALUE self, VALUE fiber, VALUE resume_ob
179
185
  return self;
180
186
  }
181
187
 
188
+ VALUE Thread_debug(VALUE self) {
189
+ rb_ivar_set(self, rb_intern("@__debug__"), Qtrue);
190
+ return self;
191
+ }
192
+
182
193
  void Init_Thread() {
183
194
  rb_define_method(rb_cThread, "setup_fiber_scheduling", Thread_setup_fiber_scheduling, 0);
184
195
  rb_define_method(rb_cThread, "reset_fiber_scheduling", Thread_reset_fiber_scheduling, 0);
@@ -191,8 +202,10 @@ void Init_Thread() {
191
202
  rb_define_method(rb_cThread, "switch_fiber", Thread_switch_fiber, 0);
192
203
  rb_define_method(rb_cThread, "run_queue_trace", Thread_run_queue_trace, 0);
193
204
 
205
+ rb_define_method(rb_cThread, "debug!", Thread_debug, 0);
206
+
194
207
  ID_deactivate_all_watchers_post_fork = rb_intern("deactivate_all_watchers_post_fork");
195
- ID_ivar_agent = rb_intern("@agent");
208
+ ID_ivar_backend = rb_intern("@backend");
196
209
  ID_ivar_join_wait_queue = rb_intern("@join_wait_queue");
197
210
  ID_ivar_main_fiber = rb_intern("@main_fiber");
198
211
  ID_ivar_result = rb_intern("@result");
@@ -3,51 +3,36 @@
3
3
  require 'fiber'
4
4
  require_relative './polyphony_ext'
5
5
 
6
- module Polyphony
7
- # replace core Queue class with our own
8
- verbose = $VERBOSE
9
- $VERBOSE = nil
10
- Object.const_set(:Queue, Polyphony::Queue)
11
- $VERBOSE = verbose
12
- end
13
-
14
6
  require_relative './polyphony/extensions/core'
15
7
  require_relative './polyphony/extensions/thread'
16
8
  require_relative './polyphony/extensions/fiber'
17
9
  require_relative './polyphony/extensions/io'
18
10
 
19
11
  Thread.current.setup_fiber_scheduling
20
- Thread.current.agent = Polyphony::LibevAgent.new
12
+ Thread.current.backend = Polyphony::Backend.new
21
13
 
22
14
  require_relative './polyphony/core/global_api'
23
15
  require_relative './polyphony/core/resource_pool'
16
+ require_relative './polyphony/core/sync'
24
17
  require_relative './polyphony/net'
25
18
  require_relative './polyphony/adapters/process'
26
- require_relative './polyphony/event'
27
19
 
28
- # Main Polyphony API
20
+ # Polyphony API
29
21
  module Polyphony
30
22
  class << self
31
- def wait_for_signal(sig)
32
- raise "should be reimplemented"
33
-
34
- fiber = Fiber.current
35
- # Polyphony.ref
36
- old_trap = trap(sig) do
37
- # Polyphony.unref
38
- fiber.schedule(sig)
39
- trap(sig, old_trap)
40
- end
41
- suspend
42
-
43
- end
44
-
45
23
  def fork(&block)
46
24
  Kernel.fork do
47
- # # Since the fiber doing the fork will become the main fiber of the
48
- # # forked process, we leave it behind by transferring to a new fiber
49
- # # created in the context of the forked process, which rescues *all*
50
- # # exceptions, including Interrupt and SystemExit.
25
+ # A race condition can arise if a TERM or INT signal is received before
26
+ # the forked process has finished initializing. To prevent this we restore
27
+ # the default signal handlers, and then reinstall the custom Polyphony
28
+ # handlers just before running the given block.
29
+ trap('SIGTERM', 'DEFAULT')
30
+ trap('SIGINT', 'DEFAULT')
31
+
32
+ # Since the fiber doing the fork will become the main fiber of the
33
+ # forked process, we leave it behind by transferring to a new fiber
34
+ # created in the context of the forked process, which rescues *all*
35
+ # exceptions, including Interrupt and SystemExit.
51
36
  spin_forked_block(&block).transfer
52
37
  end
53
38
  end
@@ -58,7 +43,7 @@ module Polyphony
58
43
  rescue SystemExit
59
44
  # fall through to ensure
60
45
  rescue Exception => e
61
- e.full_message
46
+ warn e.full_message
62
47
  exit!
63
48
  ensure
64
49
  exit_forked_process
@@ -66,16 +51,9 @@ module Polyphony
66
51
  end
67
52
 
68
53
  def run_forked_block(&block)
69
- # A race condition can arise if a TERM or INT signal is received before
70
- # the forked process has finished initializing. To prevent this we restore
71
- # the default signal handlers, and then reinstall the custom Polyphony
72
- # handlers just before running the given block.
73
- trap('SIGTERM', 'DEFAULT')
74
- trap('SIGINT', 'DEFAULT')
75
-
76
54
  Thread.current.setup
77
55
  Fiber.current.setup_main_fiber
78
- Thread.current.agent.post_fork
56
+ Thread.current.backend.post_fork
79
57
 
80
58
  install_terminating_signal_handlers
81
59
 
@@ -124,12 +102,20 @@ module Polyphony
124
102
  # processes (see Polyphony.fork).
125
103
  at_exit do
126
104
  next unless @original_pid == ::Process.pid
127
-
105
+
128
106
  Polyphony.terminate_threads
129
107
  Fiber.current.shutdown_all_children
130
108
  end
131
109
  end
132
110
  end
111
+
112
+ # replace core Queue class with our own
113
+ verbose = $VERBOSE
114
+ $VERBOSE = nil
115
+ Object.const_set(:Queue, Polyphony::Queue)
116
+ Object.const_set(:Mutex, Polyphony::Mutex)
117
+ Object.const_set(:ConditionVariable, Polyphony::ConditionVariable)
118
+ $VERBOSE = verbose
133
119
  end
134
120
 
135
121
  Polyphony.install_terminating_signal_handlers