polyphony 0.43.9 → 0.45.1

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 (141) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +8 -1
  3. data/CHANGELOG.md +40 -0
  4. data/Gemfile.lock +16 -6
  5. data/Rakefile +1 -1
  6. data/TODO.md +14 -13
  7. data/docs/_posts/2020-07-26-polyphony-0.44.md +77 -0
  8. data/docs/api-reference/thread.md +1 -1
  9. data/docs/getting-started/overview.md +14 -14
  10. data/docs/getting-started/tutorial.md +1 -1
  11. data/examples/adapters/redis_client.rb +3 -1
  12. data/examples/adapters/redis_pubsub_perf.rb +11 -8
  13. data/examples/adapters/sequel_mysql.rb +23 -0
  14. data/examples/adapters/sequel_mysql_pool.rb +33 -0
  15. data/examples/adapters/sequel_pg.rb +24 -0
  16. data/examples/core/{02-awaiting-fibers.rb → await.rb} +0 -0
  17. data/examples/core/{xx-channels.rb → channels.rb} +0 -0
  18. data/examples/core/deferring-an-operation.rb +16 -0
  19. data/examples/core/{xx-erlang-style-genserver.rb → erlang-style-genserver.rb} +16 -9
  20. data/examples/core/{xx-forking.rb → forking.rb} +1 -1
  21. data/examples/core/handling-signals.rb +11 -0
  22. data/examples/core/{03-interrupting.rb → interrupt.rb} +0 -0
  23. data/examples/core/{xx-pingpong.rb → pingpong.rb} +7 -5
  24. data/examples/core/{xx-recurrent-timer.rb → recurrent-timer.rb} +1 -1
  25. data/examples/core/{xx-resource_delegate.rb → resource_delegate.rb} +3 -4
  26. data/examples/core/{01-spinning-up-fibers.rb → spin.rb} +1 -1
  27. data/examples/core/{xx-spin_error_backtrace.rb → spin_error_backtrace.rb} +1 -1
  28. data/examples/core/{xx-supervise-process.rb → supervise-process.rb} +8 -5
  29. data/examples/core/supervisor.rb +20 -0
  30. data/examples/core/{xx-thread-sleep.rb → thread-sleep.rb} +0 -0
  31. data/examples/core/{xx-thread_pool.rb → thread_pool.rb} +0 -0
  32. data/examples/core/{xx-throttling.rb → throttling.rb} +0 -0
  33. data/examples/core/{xx-timeout.rb → timeout.rb} +0 -0
  34. data/examples/core/{xx-using-a-mutex.rb → using-a-mutex.rb} +0 -0
  35. data/examples/core/{xx-worker-thread.rb → worker-thread.rb} +2 -2
  36. data/examples/io/{xx-backticks.rb → backticks.rb} +0 -0
  37. data/examples/io/{xx-echo_client.rb → echo_client.rb} +1 -1
  38. data/examples/io/{xx-echo_client_from_stdin.rb → echo_client_from_stdin.rb} +2 -2
  39. data/examples/io/{xx-echo_pipe.rb → echo_pipe.rb} +1 -1
  40. data/examples/io/{xx-echo_server.rb → echo_server.rb} +0 -0
  41. data/examples/io/{xx-echo_server_with_timeout.rb → echo_server_with_timeout.rb} +1 -1
  42. data/examples/io/{xx-echo_stdin.rb → echo_stdin.rb} +0 -0
  43. data/examples/io/{xx-happy-eyeballs.rb → happy-eyeballs.rb} +0 -0
  44. data/examples/io/{xx-httparty.rb → httparty.rb} +4 -13
  45. data/examples/io/{xx-irb.rb → irb.rb} +0 -0
  46. data/examples/io/{xx-net-http.rb → net-http.rb} +0 -0
  47. data/examples/io/{xx-open.rb → open.rb} +0 -0
  48. data/examples/io/pry.rb +18 -0
  49. data/examples/io/rack_server.rb +71 -0
  50. data/examples/io/{xx-system.rb → system.rb} +1 -1
  51. data/examples/io/{xx-tcpserver.rb → tcpserver.rb} +0 -0
  52. data/examples/io/{xx-tcpsocket.rb → tcpsocket.rb} +0 -0
  53. data/examples/io/tunnel.rb +6 -1
  54. data/examples/io/{xx-zip.rb → zip.rb} +0 -0
  55. data/examples/performance/fiber_transfer.rb +2 -1
  56. data/examples/performance/fs_read.rb +5 -6
  57. data/examples/{io/xx-switch.rb → performance/switch.rb} +2 -1
  58. data/examples/performance/thread-vs-fiber/{xx-httparty_multi.rb → httparty_multi.rb} +3 -4
  59. data/examples/performance/thread-vs-fiber/{xx-httparty_threaded.rb → httparty_threaded.rb} +0 -0
  60. data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +1 -1
  61. data/examples/performance/thread-vs-fiber/polyphony_server.rb +1 -1
  62. data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +1 -1
  63. data/examples/performance/thread-vs-fiber/threaded_server.rb +1 -5
  64. data/examples/performance/thread_pool_perf.rb +6 -7
  65. data/ext/polyphony/backend.h +40 -0
  66. data/ext/polyphony/event.c +3 -3
  67. data/ext/polyphony/extconf.rb +1 -1
  68. data/ext/polyphony/{libev_agent.c → libev_backend.c} +272 -265
  69. data/ext/polyphony/polyphony.c +4 -2
  70. data/ext/polyphony/polyphony.h +15 -28
  71. data/ext/polyphony/polyphony_ext.c +3 -4
  72. data/ext/polyphony/queue.c +32 -12
  73. data/ext/polyphony/ring_buffer.c +0 -1
  74. data/ext/polyphony/thread.c +58 -44
  75. data/lib/polyphony.rb +25 -38
  76. data/lib/polyphony/adapters/fs.rb +1 -1
  77. data/lib/polyphony/adapters/irb.rb +2 -17
  78. data/lib/polyphony/adapters/mysql2.rb +19 -0
  79. data/lib/polyphony/adapters/postgres.rb +5 -5
  80. data/lib/polyphony/adapters/process.rb +2 -2
  81. data/lib/polyphony/adapters/readline.rb +17 -0
  82. data/lib/polyphony/adapters/redis.rb +1 -1
  83. data/lib/polyphony/adapters/sequel.rb +45 -0
  84. data/lib/polyphony/core/exceptions.rb +11 -0
  85. data/lib/polyphony/core/global_api.rb +15 -10
  86. data/lib/polyphony/core/resource_pool.rb +20 -7
  87. data/lib/polyphony/core/sync.rb +46 -8
  88. data/lib/polyphony/core/throttler.rb +1 -1
  89. data/lib/polyphony/extensions/core.rb +38 -25
  90. data/lib/polyphony/extensions/fiber.rb +5 -1
  91. data/lib/polyphony/extensions/io.rb +45 -12
  92. data/lib/polyphony/extensions/openssl.rb +6 -6
  93. data/lib/polyphony/extensions/socket.rb +22 -23
  94. data/lib/polyphony/extensions/thread.rb +6 -5
  95. data/lib/polyphony/net.rb +2 -1
  96. data/lib/polyphony/version.rb +1 -1
  97. data/polyphony.gemspec +7 -3
  98. data/test/helper.rb +1 -1
  99. data/test/{test_agent.rb → test_backend.rb} +22 -22
  100. data/test/test_fiber.rb +21 -5
  101. data/test/test_io.rb +17 -1
  102. data/test/test_kernel.rb +5 -0
  103. data/test/test_resource_pool.rb +50 -16
  104. data/test/test_signal.rb +5 -29
  105. data/test/test_socket.rb +17 -0
  106. data/test/test_sync.rb +52 -0
  107. data/test/test_throttler.rb +1 -0
  108. metadata +125 -96
  109. data/.gitbook.yaml +0 -4
  110. data/examples/adapters/concurrent-ruby.rb +0 -9
  111. data/examples/core/04-handling-signals.rb +0 -19
  112. data/examples/core/xx-agent.rb +0 -102
  113. data/examples/core/xx-at_exit.rb +0 -29
  114. data/examples/core/xx-caller.rb +0 -12
  115. data/examples/core/xx-daemon.rb +0 -14
  116. data/examples/core/xx-deadlock.rb +0 -8
  117. data/examples/core/xx-deferring-an-operation.rb +0 -14
  118. data/examples/core/xx-exception-backtrace.rb +0 -40
  119. data/examples/core/xx-fork-cleanup.rb +0 -22
  120. data/examples/core/xx-fork-spin.rb +0 -42
  121. data/examples/core/xx-fork-terminate.rb +0 -27
  122. data/examples/core/xx-move_on.rb +0 -23
  123. data/examples/core/xx-queue-async.rb +0 -120
  124. data/examples/core/xx-readpartial.rb +0 -18
  125. data/examples/core/xx-signals.rb +0 -16
  126. data/examples/core/xx-sleep-forever.rb +0 -9
  127. data/examples/core/xx-sleeping.rb +0 -25
  128. data/examples/core/xx-snooze-starve.rb +0 -16
  129. data/examples/core/xx-spin-fork.rb +0 -49
  130. data/examples/core/xx-state-machine.rb +0 -51
  131. data/examples/core/xx-stop.rb +0 -20
  132. data/examples/core/xx-supervisors.rb +0 -21
  133. data/examples/core/xx-thread-selector-sleep.rb +0 -51
  134. data/examples/core/xx-thread-selector-snooze.rb +0 -46
  135. data/examples/core/xx-thread-snooze.rb +0 -34
  136. data/examples/core/xx-timer-gc.rb +0 -17
  137. data/examples/core/xx-trace.rb +0 -79
  138. data/examples/performance/xx-array.rb +0 -11
  139. data/examples/performance/xx-fiber-switch.rb +0 -9
  140. data/examples/performance/xx-snooze.rb +0 -15
  141. data/examples/xx-spin.rb +0 -32
@@ -7,8 +7,8 @@ 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
- ID ID_raise;
12
12
  ID ID_ivar_running;
13
13
  ID ID_ivar_thread;
14
14
  ID ID_runnable;
@@ -21,6 +21,8 @@ ID ID_R;
21
21
  ID ID_W;
22
22
  ID ID_RW;
23
23
 
24
+ backend_interface_t backend_interface;
25
+
24
26
  VALUE Polyphony_snooze(VALUE self) {
25
27
  VALUE ret;
26
28
  VALUE fiber = rb_fiber_current();
@@ -58,10 +60,10 @@ void Init_Polyphony() {
58
60
  ID_clear = rb_intern("clear");
59
61
  ID_each = rb_intern("each");
60
62
  ID_inspect = rb_intern("inspect");
63
+ ID_invoke = rb_intern("invoke");
61
64
  ID_ivar_running = rb_intern("@running");
62
65
  ID_ivar_thread = rb_intern("@thread");
63
66
  ID_new = rb_intern("new");
64
- ID_raise = rb_intern("raise");
65
67
  ID_runnable = rb_intern("runnable");
66
68
  ID_runnable_value = rb_intern("runnable_value");
67
69
  ID_signal = rb_intern("signal");
@@ -4,20 +4,29 @@
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
+ extern backend_interface_t backend_interface;
28
+ #define __BACKEND__ (backend_interface)
29
+
21
30
  extern VALUE mPolyphony;
22
31
  extern VALUE cQueue;
23
32
  extern VALUE cEvent;
@@ -28,7 +37,8 @@ extern ID ID_clear;
28
37
  extern ID ID_each;
29
38
  extern ID ID_fiber_trace;
30
39
  extern ID ID_inspect;
31
- extern ID ID_ivar_agent;
40
+ extern ID ID_invoke;
41
+ extern ID ID_ivar_backend;
32
42
  extern ID ID_ivar_running;
33
43
  extern ID ID_ivar_thread;
34
44
  extern ID ID_new;
@@ -56,26 +66,9 @@ enum {
56
66
  FIBER_STATE_SCHEDULED = 2
57
67
  };
58
68
 
59
- // watcher flags
60
- enum {
61
- // a watcher's active field will be set to this after fork
62
- GYRO_WATCHER_POST_FORK = 0xFF
63
- };
64
-
65
69
  VALUE Fiber_auto_watcher(VALUE self);
66
70
  void Fiber_make_runnable(VALUE fiber, VALUE value);
67
71
 
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
72
  VALUE Queue_push(VALUE self, VALUE value);
80
73
  VALUE Queue_unshift(VALUE self, VALUE value);
81
74
  VALUE Queue_shift(VALUE self);
@@ -85,13 +78,7 @@ VALUE Queue_delete(VALUE self, VALUE value);
85
78
  long Queue_len(VALUE self);
86
79
  void Queue_trace(VALUE self);
87
80
 
88
- VALUE Polyphony_snooze(VALUE self);
89
-
90
81
  VALUE Thread_schedule_fiber(VALUE thread, VALUE fiber, VALUE value);
91
82
  VALUE Thread_switch_fiber(VALUE thread);
92
83
 
93
- int io_setstrbuf(VALUE *str, long len);
94
- void io_set_read_length(VALUE str, long n, int shrinkable);
95
- VALUE io_enc_str(VALUE str, rb_io_t *fptr);
96
-
97
84
  #endif /* POLYPHONY_H */
@@ -2,7 +2,7 @@
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
7
  void Init_Event();
8
8
  void Init_Thread();
@@ -12,12 +12,11 @@ void Init_polyphony_ext() {
12
12
  ev_set_allocator(xrealloc);
13
13
 
14
14
  Init_Polyphony();
15
- Init_LibevAgent();
15
+
16
+ Init_LibevBackend();
16
17
  Init_Queue();
17
18
  Init_Event();
18
-
19
19
  Init_Fiber();
20
20
  Init_Thread();
21
-
22
21
  Init_Tracing();
23
22
  }
@@ -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,25 @@ 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);
88
- return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
89
- }
90
- RB_GC_GUARD(agent);
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
+ TEST_RESUME_EXCEPTION(switchpoint_result);
91
93
  RB_GC_GUARD(switchpoint_result);
94
+
95
+ if (queue->values.count > 0)
96
+ return ring_buffer_shift(&queue->values);
92
97
  }
93
98
 
94
- return ring_buffer_shift(&queue->values);
99
+ return Qnil;
95
100
  }
96
101
 
97
102
  VALUE Queue_shift_no_wait(VALUE self) {
@@ -146,7 +151,7 @@ VALUE Queue_flush_waiters(VALUE self, VALUE value) {
146
151
  while(1) {
147
152
  VALUE fiber = ring_buffer_shift(&queue->shift_queue);
148
153
  if (fiber == Qnil) return self;
149
-
154
+
150
155
  Fiber_make_runnable(fiber, value);
151
156
  }
152
157
  }
@@ -158,6 +163,13 @@ VALUE Queue_empty_p(VALUE self) {
158
163
  return (queue->values.count == 0) ? Qtrue : Qfalse;
159
164
  }
160
165
 
166
+ VALUE Queue_pending_p(VALUE self) {
167
+ Queue_t *queue;
168
+ GetQueue(self, queue);
169
+
170
+ return (queue->shift_queue.count > 0) ? Qtrue : Qfalse;
171
+ }
172
+
161
173
  VALUE Queue_size_m(VALUE self) {
162
174
  Queue_t *queue;
163
175
  GetQueue(self, queue);
@@ -165,6 +177,13 @@ VALUE Queue_size_m(VALUE self) {
165
177
  return INT2NUM(queue->values.count);
166
178
  }
167
179
 
180
+ void Queue_trace(VALUE self) {
181
+ Queue_t *queue;
182
+ GetQueue(self, queue);
183
+
184
+ printf("run queue size: %d count: %d\n", queue->values.size, queue->values.count);
185
+ }
186
+
168
187
  void Init_Queue() {
169
188
  cQueue = rb_define_class_under(mPolyphony, "Queue", rb_cData);
170
189
  rb_define_alloc_func(cQueue, Queue_allocate);
@@ -183,5 +202,6 @@ void Init_Queue() {
183
202
  rb_define_method(cQueue, "shift_all", Queue_shift_all, 0);
184
203
  rb_define_method(cQueue, "flush_waiters", Queue_flush_waiters, 1);
185
204
  rb_define_method(cQueue, "empty?", Queue_empty_p, 0);
205
+ rb_define_method(cQueue, "pending?", Queue_pending_p, 0);
186
206
  rb_define_method(cQueue, "size", Queue_size_m, 0);
187
207
  }
@@ -24,7 +24,6 @@ VALUE ring_buffer_shift(ring_buffer *buffer) {
24
24
  value = buffer->entries[buffer->head];
25
25
  buffer->head = (buffer->head + 1) % buffer->size;
26
26
  buffer->count--;
27
- // INSPECT(value);
28
27
  return value;
29
28
  }
30
29
 
@@ -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;
@@ -12,7 +12,7 @@ ID ID_stop;
12
12
 
13
13
  static VALUE Thread_setup_fiber_scheduling(VALUE self) {
14
14
  VALUE queue = rb_funcall(cQueue, ID_new, 0);
15
-
15
+
16
16
  rb_ivar_set(self, ID_ivar_main_fiber, rb_fiber_current());
17
17
  rb_ivar_set(self, ID_run_queue, queue);
18
18
 
@@ -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,23 +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);
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;
64
+ }
65
+
56
66
  rb_ivar_set(fiber, ID_runnable_value, value);
57
- // if fiber is already scheduled, just set the scheduled value, then return
58
- if (rb_ivar_get(fiber, ID_runnable) != Qnil) return self;
67
+ COND_TRACE(3, SYM_fiber_schedule, fiber, value);
59
68
 
60
- queue = rb_ivar_get(self, ID_run_queue);
61
- Queue_push(queue, fiber);
62
- rb_ivar_set(fiber, ID_runnable, Qtrue);
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);
63
73
 
64
- if (rb_thread_current() != self) {
65
- // if the fiber scheduling is done across threads, we need to make sure the
66
- // target thread is woken up in case it is in the middle of running its
67
- // event selector. Otherwise it's gonna be stuck waiting for an event to
68
- // happen, not knowing that it there's already a fiber ready to run in its
69
- // run queue.
70
- VALUE agent = rb_ivar_get(self,ID_ivar_agent);
71
- LibevAgent_break(agent);
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
+ }
72
83
  }
73
84
  return self;
74
85
  }
@@ -78,7 +89,7 @@ VALUE Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value)
78
89
 
79
90
  if (rb_fiber_alive_p(fiber) != Qtrue) return self;
80
91
 
81
- FIBER_TRACE(3, SYM_fiber_schedule, fiber, value);
92
+ COND_TRACE(3, SYM_fiber_schedule, fiber, value);
82
93
  rb_ivar_set(fiber, ID_runnable_value, value);
83
94
 
84
95
  queue = rb_ivar_get(self, ID_run_queue);
@@ -99,8 +110,8 @@ VALUE Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value)
99
110
  // event loop. Otherwise it's gonna be stuck waiting for an event to
100
111
  // happen, not knowing that it there's already a fiber ready to run in its
101
112
  // run queue.
102
- VALUE agent = rb_ivar_get(self, ID_ivar_agent);
103
- LibevAgent_break(agent);
113
+ VALUE backend = rb_ivar_get(self, ID_ivar_backend);
114
+ __BACKEND__.wakeup(backend);
104
115
  }
105
116
  return self;
106
117
  }
@@ -110,43 +121,39 @@ VALUE Thread_switch_fiber(VALUE self) {
110
121
  VALUE queue = rb_ivar_get(self, ID_run_queue);
111
122
  VALUE next_fiber;
112
123
  VALUE value;
113
- VALUE agent = rb_ivar_get(self, ID_ivar_agent);
124
+ VALUE backend = rb_ivar_get(self, ID_ivar_backend);
114
125
  int ref_count;
115
- int agent_was_polled = 0;1;
126
+ int backend_was_polled = 0;
116
127
 
117
- if (__tracing_enabled__) {
118
- if (rb_ivar_get(current_fiber, ID_ivar_running) != Qfalse) {
119
- rb_funcall(rb_cObject, ID_fiber_trace, 2, SYM_fiber_switchpoint, current_fiber);
120
- }
121
- }
128
+ if (__tracing_enabled__ && (rb_ivar_get(current_fiber, ID_ivar_running) != Qfalse))
129
+ TRACE(2, SYM_fiber_switchpoint, current_fiber);
122
130
 
123
- ref_count = LibevAgent_ref_count(agent);
131
+ ref_count = __BACKEND__.ref_count(backend);
124
132
  while (1) {
125
133
  next_fiber = Queue_shift_no_wait(queue);
126
134
  if (next_fiber != Qnil) {
127
- if (agent_was_polled == 0 && ref_count > 0) {
128
- // this mechanism prevents event starvation in case the run queue never
129
- // empties
130
- LibevAgent_poll(agent, Qtrue, current_fiber, queue);
135
+ if (backend_was_polled == 0 && ref_count > 0) {
136
+ // this prevents event starvation in case the run queue never empties
137
+ __BACKEND__.poll(backend, Qtrue, current_fiber, queue);
131
138
  }
132
139
  break;
133
140
  }
134
141
  if (ref_count == 0) break;
135
142
 
136
- LibevAgent_poll(agent, Qnil, current_fiber, queue);
137
- agent_was_polled = 1;
143
+ __BACKEND__.poll(backend, Qnil, current_fiber, queue);
144
+ backend_was_polled = 1;
138
145
  }
139
146
 
140
147
  if (next_fiber == Qnil) return Qnil;
141
148
 
142
149
  // run next fiber
143
150
  value = rb_ivar_get(next_fiber, ID_runnable_value);
144
- FIBER_TRACE(3, SYM_fiber_run, next_fiber, value);
151
+ COND_TRACE(3, SYM_fiber_run, next_fiber, value);
145
152
 
146
153
  rb_ivar_set(next_fiber, ID_runnable, Qnil);
147
154
  RB_GC_GUARD(next_fiber);
148
155
  RB_GC_GUARD(value);
149
- return (next_fiber == current_fiber) ?
156
+ return (next_fiber == current_fiber) ?
150
157
  value : rb_funcall(next_fiber, ID_transfer, 1, value);
151
158
  }
152
159
 
@@ -164,12 +171,12 @@ VALUE Thread_reset_fiber_scheduling(VALUE self) {
164
171
  }
165
172
 
166
173
  VALUE Thread_fiber_break_out_of_ev_loop(VALUE self, VALUE fiber, VALUE resume_obj) {
167
- VALUE agent = rb_ivar_get(self, ID_ivar_agent);
174
+ VALUE backend = rb_ivar_get(self, ID_ivar_backend);
168
175
  if (fiber != Qnil) {
169
176
  Thread_schedule_fiber_with_priority(self, fiber, resume_obj);
170
177
  }
171
178
 
172
- if (LibevAgent_break(agent) == Qnil) {
179
+ if (__BACKEND__.wakeup(backend) == Qnil) {
173
180
  // we're not inside the ev_loop, so we just do a switchpoint
174
181
  Thread_switch_fiber(self);
175
182
  }
@@ -177,6 +184,11 @@ VALUE Thread_fiber_break_out_of_ev_loop(VALUE self, VALUE fiber, VALUE resume_ob
177
184
  return self;
178
185
  }
179
186
 
187
+ VALUE Thread_debug(VALUE self) {
188
+ rb_ivar_set(self, rb_intern("@__debug__"), Qtrue);
189
+ return self;
190
+ }
191
+
180
192
  void Init_Thread() {
181
193
  rb_define_method(rb_cThread, "setup_fiber_scheduling", Thread_setup_fiber_scheduling, 0);
182
194
  rb_define_method(rb_cThread, "reset_fiber_scheduling", Thread_reset_fiber_scheduling, 0);
@@ -189,8 +201,10 @@ void Init_Thread() {
189
201
  rb_define_method(rb_cThread, "switch_fiber", Thread_switch_fiber, 0);
190
202
  rb_define_method(rb_cThread, "run_queue_trace", Thread_run_queue_trace, 0);
191
203
 
204
+ rb_define_method(rb_cThread, "debug!", Thread_debug, 0);
205
+
192
206
  ID_deactivate_all_watchers_post_fork = rb_intern("deactivate_all_watchers_post_fork");
193
- ID_ivar_agent = rb_intern("@agent");
207
+ ID_ivar_backend = rb_intern("@backend");
194
208
  ID_ivar_join_wait_queue = rb_intern("@join_wait_queue");
195
209
  ID_ivar_main_fiber = rb_intern("@main_fiber");
196
210
  ID_ivar_result = rb_intern("@result");
@@ -3,50 +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
19
 
27
- # Main Polyphony API
20
+ # Polyphony API
28
21
  module Polyphony
29
22
  class << self
30
- def wait_for_signal(sig)
31
- raise "should be reimplemented"
32
-
33
- fiber = Fiber.current
34
- # Polyphony.ref
35
- old_trap = trap(sig) do
36
- # Polyphony.unref
37
- fiber.schedule(sig)
38
- trap(sig, old_trap)
39
- end
40
- suspend
41
-
42
- end
43
-
44
23
  def fork(&block)
45
24
  Kernel.fork do
46
- # # Since the fiber doing the fork will become the main fiber of the
47
- # # forked process, we leave it behind by transferring to a new fiber
48
- # # created in the context of the forked process, which rescues *all*
49
- # # 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.
50
36
  spin_forked_block(&block).transfer
51
37
  end
52
38
  end
@@ -57,7 +43,7 @@ module Polyphony
57
43
  rescue SystemExit
58
44
  # fall through to ensure
59
45
  rescue Exception => e
60
- e.full_message
46
+ warn e.full_message
61
47
  exit!
62
48
  ensure
63
49
  exit_forked_process
@@ -65,16 +51,9 @@ module Polyphony
65
51
  end
66
52
 
67
53
  def run_forked_block(&block)
68
- # A race condition can arise if a TERM or INT signal is received before
69
- # the forked process has finished initializing. To prevent this we restore
70
- # the default signal handlers, and then reinstall the custom Polyphony
71
- # handlers just before running the given block.
72
- trap('SIGTERM', 'DEFAULT')
73
- trap('SIGINT', 'DEFAULT')
74
-
75
54
  Thread.current.setup
76
55
  Fiber.current.setup_main_fiber
77
- Thread.current.agent.post_fork
56
+ Thread.current.backend.post_fork
78
57
 
79
58
  install_terminating_signal_handlers
80
59
 
@@ -123,12 +102,20 @@ module Polyphony
123
102
  # processes (see Polyphony.fork).
124
103
  at_exit do
125
104
  next unless @original_pid == ::Process.pid
126
-
105
+
127
106
  Polyphony.terminate_threads
128
107
  Fiber.current.shutdown_all_children
129
108
  end
130
109
  end
131
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
132
119
  end
133
120
 
134
121
  Polyphony.install_terminating_signal_handlers