polyphony 0.43.8

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 (221) hide show
  1. checksums.yaml +7 -0
  2. data/.gitbook.yaml +4 -0
  3. data/.github/workflows/test.yml +29 -0
  4. data/.gitignore +59 -0
  5. data/.rubocop.yml +175 -0
  6. data/CHANGELOG.md +393 -0
  7. data/Gemfile +3 -0
  8. data/Gemfile.lock +141 -0
  9. data/LICENSE +21 -0
  10. data/README.md +51 -0
  11. data/Rakefile +26 -0
  12. data/TODO.md +201 -0
  13. data/bin/polyphony-debug +87 -0
  14. data/docs/_config.yml +64 -0
  15. data/docs/_includes/head.html +40 -0
  16. data/docs/_includes/title.html +1 -0
  17. data/docs/_sass/custom/custom.scss +10 -0
  18. data/docs/_sass/overrides.scss +0 -0
  19. data/docs/_user-guide/all-about-timers.md +126 -0
  20. data/docs/_user-guide/index.md +9 -0
  21. data/docs/_user-guide/web-server.md +136 -0
  22. data/docs/api-reference/exception.md +27 -0
  23. data/docs/api-reference/fiber.md +425 -0
  24. data/docs/api-reference/index.md +9 -0
  25. data/docs/api-reference/io.md +36 -0
  26. data/docs/api-reference/object.md +99 -0
  27. data/docs/api-reference/polyphony-baseexception.md +33 -0
  28. data/docs/api-reference/polyphony-cancel.md +26 -0
  29. data/docs/api-reference/polyphony-moveon.md +24 -0
  30. data/docs/api-reference/polyphony-net.md +20 -0
  31. data/docs/api-reference/polyphony-process.md +28 -0
  32. data/docs/api-reference/polyphony-resourcepool.md +59 -0
  33. data/docs/api-reference/polyphony-restart.md +18 -0
  34. data/docs/api-reference/polyphony-terminate.md +18 -0
  35. data/docs/api-reference/polyphony-threadpool.md +67 -0
  36. data/docs/api-reference/polyphony-throttler.md +77 -0
  37. data/docs/api-reference/polyphony.md +36 -0
  38. data/docs/api-reference/thread.md +88 -0
  39. data/docs/assets/img/echo-fibers.svg +1 -0
  40. data/docs/assets/img/sleeping-fiber.svg +1 -0
  41. data/docs/faq.md +195 -0
  42. data/docs/favicon.ico +0 -0
  43. data/docs/getting-started/index.md +10 -0
  44. data/docs/getting-started/installing.md +34 -0
  45. data/docs/getting-started/overview.md +486 -0
  46. data/docs/getting-started/tutorial.md +359 -0
  47. data/docs/index.md +94 -0
  48. data/docs/main-concepts/concurrency.md +151 -0
  49. data/docs/main-concepts/design-principles.md +161 -0
  50. data/docs/main-concepts/exception-handling.md +291 -0
  51. data/docs/main-concepts/extending.md +89 -0
  52. data/docs/main-concepts/fiber-scheduling.md +197 -0
  53. data/docs/main-concepts/index.md +9 -0
  54. data/docs/polyphony-logo.png +0 -0
  55. data/examples/adapters/concurrent-ruby.rb +9 -0
  56. data/examples/adapters/pg_client.rb +36 -0
  57. data/examples/adapters/pg_notify.rb +35 -0
  58. data/examples/adapters/pg_pool.rb +43 -0
  59. data/examples/adapters/pg_transaction.rb +31 -0
  60. data/examples/adapters/redis_blpop.rb +12 -0
  61. data/examples/adapters/redis_channels.rb +122 -0
  62. data/examples/adapters/redis_client.rb +19 -0
  63. data/examples/adapters/redis_pubsub.rb +26 -0
  64. data/examples/adapters/redis_pubsub_perf.rb +68 -0
  65. data/examples/core/01-spinning-up-fibers.rb +18 -0
  66. data/examples/core/02-awaiting-fibers.rb +20 -0
  67. data/examples/core/03-interrupting.rb +39 -0
  68. data/examples/core/04-handling-signals.rb +19 -0
  69. data/examples/core/xx-agent.rb +102 -0
  70. data/examples/core/xx-at_exit.rb +29 -0
  71. data/examples/core/xx-caller.rb +12 -0
  72. data/examples/core/xx-channels.rb +45 -0
  73. data/examples/core/xx-daemon.rb +14 -0
  74. data/examples/core/xx-deadlock.rb +8 -0
  75. data/examples/core/xx-deferring-an-operation.rb +14 -0
  76. data/examples/core/xx-erlang-style-genserver.rb +81 -0
  77. data/examples/core/xx-exception-backtrace.rb +40 -0
  78. data/examples/core/xx-fork-cleanup.rb +22 -0
  79. data/examples/core/xx-fork-spin.rb +42 -0
  80. data/examples/core/xx-fork-terminate.rb +27 -0
  81. data/examples/core/xx-forking.rb +24 -0
  82. data/examples/core/xx-move_on.rb +23 -0
  83. data/examples/core/xx-pingpong.rb +18 -0
  84. data/examples/core/xx-queue-async.rb +120 -0
  85. data/examples/core/xx-readpartial.rb +18 -0
  86. data/examples/core/xx-recurrent-timer.rb +12 -0
  87. data/examples/core/xx-resource_delegate.rb +31 -0
  88. data/examples/core/xx-signals.rb +16 -0
  89. data/examples/core/xx-sleep-forever.rb +9 -0
  90. data/examples/core/xx-sleeping.rb +25 -0
  91. data/examples/core/xx-snooze-starve.rb +16 -0
  92. data/examples/core/xx-spin-fork.rb +49 -0
  93. data/examples/core/xx-spin_error_backtrace.rb +33 -0
  94. data/examples/core/xx-state-machine.rb +51 -0
  95. data/examples/core/xx-stop.rb +20 -0
  96. data/examples/core/xx-supervise-process.rb +30 -0
  97. data/examples/core/xx-supervisors.rb +21 -0
  98. data/examples/core/xx-thread-selector-sleep.rb +51 -0
  99. data/examples/core/xx-thread-selector-snooze.rb +46 -0
  100. data/examples/core/xx-thread-sleep.rb +17 -0
  101. data/examples/core/xx-thread-snooze.rb +34 -0
  102. data/examples/core/xx-thread_pool.rb +17 -0
  103. data/examples/core/xx-throttling.rb +18 -0
  104. data/examples/core/xx-timeout.rb +10 -0
  105. data/examples/core/xx-timer-gc.rb +17 -0
  106. data/examples/core/xx-trace.rb +79 -0
  107. data/examples/core/xx-using-a-mutex.rb +21 -0
  108. data/examples/core/xx-worker-thread.rb +30 -0
  109. data/examples/io/tunnel.rb +48 -0
  110. data/examples/io/xx-backticks.rb +11 -0
  111. data/examples/io/xx-echo_client.rb +25 -0
  112. data/examples/io/xx-echo_client_from_stdin.rb +21 -0
  113. data/examples/io/xx-echo_pipe.rb +16 -0
  114. data/examples/io/xx-echo_server.rb +17 -0
  115. data/examples/io/xx-echo_server_with_timeout.rb +34 -0
  116. data/examples/io/xx-echo_stdin.rb +14 -0
  117. data/examples/io/xx-happy-eyeballs.rb +36 -0
  118. data/examples/io/xx-httparty.rb +38 -0
  119. data/examples/io/xx-irb.rb +17 -0
  120. data/examples/io/xx-net-http.rb +15 -0
  121. data/examples/io/xx-open.rb +16 -0
  122. data/examples/io/xx-switch.rb +15 -0
  123. data/examples/io/xx-system.rb +11 -0
  124. data/examples/io/xx-tcpserver.rb +15 -0
  125. data/examples/io/xx-tcpsocket.rb +18 -0
  126. data/examples/io/xx-zip.rb +19 -0
  127. data/examples/performance/fiber_transfer.rb +47 -0
  128. data/examples/performance/fs_read.rb +38 -0
  129. data/examples/performance/mem-usage.rb +56 -0
  130. data/examples/performance/messaging.rb +29 -0
  131. data/examples/performance/multi_snooze.rb +33 -0
  132. data/examples/performance/snooze.rb +39 -0
  133. data/examples/performance/snooze_raw.rb +39 -0
  134. data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +74 -0
  135. data/examples/performance/thread-vs-fiber/polyphony_server.rb +45 -0
  136. data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +58 -0
  137. data/examples/performance/thread-vs-fiber/threaded_server.rb +27 -0
  138. data/examples/performance/thread-vs-fiber/xx-httparty_multi.rb +36 -0
  139. data/examples/performance/thread-vs-fiber/xx-httparty_threaded.rb +29 -0
  140. data/examples/performance/thread_pool_perf.rb +63 -0
  141. data/examples/performance/xx-array.rb +11 -0
  142. data/examples/performance/xx-fiber-switch.rb +9 -0
  143. data/examples/performance/xx-snooze.rb +15 -0
  144. data/examples/xx-spin.rb +32 -0
  145. data/ext/libev/Changes +548 -0
  146. data/ext/libev/LICENSE +37 -0
  147. data/ext/libev/README +59 -0
  148. data/ext/libev/README.embed +3 -0
  149. data/ext/libev/ev.c +5279 -0
  150. data/ext/libev/ev.h +856 -0
  151. data/ext/libev/ev_epoll.c +296 -0
  152. data/ext/libev/ev_kqueue.c +224 -0
  153. data/ext/libev/ev_linuxaio.c +642 -0
  154. data/ext/libev/ev_poll.c +156 -0
  155. data/ext/libev/ev_port.c +192 -0
  156. data/ext/libev/ev_select.c +316 -0
  157. data/ext/libev/ev_vars.h +215 -0
  158. data/ext/libev/ev_win32.c +162 -0
  159. data/ext/libev/ev_wrap.h +216 -0
  160. data/ext/libev/test_libev_win32.c +123 -0
  161. data/ext/polyphony/extconf.rb +20 -0
  162. data/ext/polyphony/fiber.c +109 -0
  163. data/ext/polyphony/libev.c +2 -0
  164. data/ext/polyphony/libev.h +9 -0
  165. data/ext/polyphony/libev_agent.c +882 -0
  166. data/ext/polyphony/polyphony.c +71 -0
  167. data/ext/polyphony/polyphony.h +97 -0
  168. data/ext/polyphony/polyphony_ext.c +21 -0
  169. data/ext/polyphony/queue.c +168 -0
  170. data/ext/polyphony/ring_buffer.c +96 -0
  171. data/ext/polyphony/ring_buffer.h +28 -0
  172. data/ext/polyphony/thread.c +208 -0
  173. data/ext/polyphony/tracing.c +11 -0
  174. data/lib/polyphony.rb +136 -0
  175. data/lib/polyphony/adapters/fs.rb +19 -0
  176. data/lib/polyphony/adapters/irb.rb +52 -0
  177. data/lib/polyphony/adapters/postgres.rb +110 -0
  178. data/lib/polyphony/adapters/process.rb +33 -0
  179. data/lib/polyphony/adapters/redis.rb +67 -0
  180. data/lib/polyphony/adapters/trace.rb +138 -0
  181. data/lib/polyphony/core/channel.rb +46 -0
  182. data/lib/polyphony/core/exceptions.rb +36 -0
  183. data/lib/polyphony/core/global_api.rb +124 -0
  184. data/lib/polyphony/core/resource_pool.rb +117 -0
  185. data/lib/polyphony/core/sync.rb +21 -0
  186. data/lib/polyphony/core/thread_pool.rb +64 -0
  187. data/lib/polyphony/core/throttler.rb +41 -0
  188. data/lib/polyphony/event.rb +17 -0
  189. data/lib/polyphony/extensions/core.rb +174 -0
  190. data/lib/polyphony/extensions/fiber.rb +379 -0
  191. data/lib/polyphony/extensions/io.rb +221 -0
  192. data/lib/polyphony/extensions/openssl.rb +81 -0
  193. data/lib/polyphony/extensions/socket.rb +150 -0
  194. data/lib/polyphony/extensions/thread.rb +108 -0
  195. data/lib/polyphony/net.rb +77 -0
  196. data/lib/polyphony/version.rb +5 -0
  197. data/polyphony.gemspec +40 -0
  198. data/test/coverage.rb +54 -0
  199. data/test/eg.rb +27 -0
  200. data/test/helper.rb +56 -0
  201. data/test/q.rb +24 -0
  202. data/test/run.rb +5 -0
  203. data/test/stress.rb +25 -0
  204. data/test/test_agent.rb +130 -0
  205. data/test/test_event.rb +59 -0
  206. data/test/test_ext.rb +196 -0
  207. data/test/test_fiber.rb +988 -0
  208. data/test/test_global_api.rb +352 -0
  209. data/test/test_io.rb +249 -0
  210. data/test/test_kernel.rb +57 -0
  211. data/test/test_process_supervision.rb +46 -0
  212. data/test/test_queue.rb +112 -0
  213. data/test/test_resource_pool.rb +138 -0
  214. data/test/test_signal.rb +100 -0
  215. data/test/test_socket.rb +34 -0
  216. data/test/test_supervise.rb +103 -0
  217. data/test/test_thread.rb +170 -0
  218. data/test/test_thread_pool.rb +101 -0
  219. data/test/test_throttler.rb +50 -0
  220. data/test/test_trace.rb +68 -0
  221. metadata +482 -0
@@ -0,0 +1,28 @@
1
+ #ifndef RING_BUFFER_H
2
+ #define RING_BUFFER_H
3
+
4
+ #include "ruby.h"
5
+
6
+ typedef struct ring_buffer {
7
+ VALUE *entries;
8
+ unsigned int size;
9
+ unsigned int count;
10
+ unsigned int head;
11
+ unsigned int tail;
12
+ } ring_buffer;
13
+
14
+ void ring_buffer_init(ring_buffer *buffer);
15
+ void ring_buffer_free(ring_buffer *buffer);
16
+ void ring_buffer_mark(ring_buffer *buffer);
17
+ int ring_buffer_empty_p(ring_buffer *buffer);
18
+ void ring_buffer_clear(ring_buffer *buffer);
19
+
20
+ VALUE ring_buffer_shift(ring_buffer *buffer);
21
+ void ring_buffer_unshift(ring_buffer *buffer, VALUE value);
22
+ void ring_buffer_push(ring_buffer *buffer, VALUE value);
23
+
24
+ void ring_buffer_shift_each(ring_buffer *buffer);
25
+ VALUE ring_buffer_shift_all(ring_buffer *buffer);
26
+ void ring_buffer_delete(ring_buffer *buffer, VALUE value);
27
+
28
+ #endif /* RING_BUFFER_H */
@@ -0,0 +1,208 @@
1
+ #include "polyphony.h"
2
+
3
+ ID ID_deactivate_all_watchers_post_fork;
4
+ ID ID_ivar_agent;
5
+ ID ID_ivar_join_wait_queue;
6
+ ID ID_ivar_main_fiber;
7
+ ID ID_ivar_result;
8
+ ID ID_ivar_terminated;
9
+ ID ID_run_queue;
10
+ ID ID_runnable_next;
11
+ ID ID_stop;
12
+
13
+ static VALUE Thread_setup_fiber_scheduling(VALUE self) {
14
+ VALUE queue = rb_funcall(cQueue, ID_new, 0);
15
+
16
+ rb_ivar_set(self, ID_ivar_main_fiber, rb_fiber_current());
17
+ rb_ivar_set(self, ID_run_queue, queue);
18
+
19
+ return self;
20
+ }
21
+
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));
25
+ }
26
+
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);
30
+ }
31
+
32
+ static VALUE SYM_scheduled_fibers;
33
+ static VALUE SYM_pending_watchers;
34
+
35
+ static VALUE Thread_fiber_scheduling_stats(VALUE self) {
36
+ VALUE agent = rb_ivar_get(self,ID_ivar_agent);
37
+ VALUE stats = rb_hash_new();
38
+ VALUE queue = rb_ivar_get(self, ID_run_queue);
39
+ long pending_count;
40
+
41
+ long scheduled_count = RARRAY_LEN(queue);
42
+ rb_hash_aset(stats, SYM_scheduled_fibers, INT2NUM(scheduled_count));
43
+
44
+ pending_count = LibevAgent_pending_count(agent);
45
+ rb_hash_aset(stats, SYM_pending_watchers, INT2NUM(pending_count));
46
+
47
+ return stats;
48
+ }
49
+
50
+ VALUE Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
51
+ VALUE queue;
52
+
53
+ if (rb_fiber_alive_p(fiber) != Qtrue) return self;
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;
60
+ }
61
+
62
+ queue = rb_ivar_get(self, ID_run_queue);
63
+ Queue_push(queue, fiber);
64
+ rb_ivar_set(fiber, ID_runnable, Qtrue);
65
+
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);
74
+ }
75
+ return self;
76
+ }
77
+
78
+ VALUE Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value) {
79
+ VALUE queue;
80
+
81
+ if (rb_fiber_alive_p(fiber) != Qtrue) return self;
82
+
83
+ FIBER_TRACE(3, SYM_fiber_schedule, fiber, value);
84
+ rb_ivar_set(fiber, ID_runnable_value, value);
85
+
86
+ queue = rb_ivar_get(self, ID_run_queue);
87
+
88
+ // if fiber is already scheduled, remove it from the run queue
89
+ if (rb_ivar_get(fiber, ID_runnable) != Qnil) {
90
+ Queue_delete(queue, fiber);
91
+ } else {
92
+ rb_ivar_set(fiber, ID_runnable, Qtrue);
93
+ }
94
+
95
+ // the fiber is given priority by putting it at the front of the run queue
96
+ Queue_unshift(queue, fiber);
97
+
98
+ if (rb_thread_current() != self) {
99
+ // if the fiber scheduling is done across threads, we need to make sure the
100
+ // target thread is woken up in case it is in the middle of running its
101
+ // event loop. Otherwise it's gonna be stuck waiting for an event to
102
+ // happen, not knowing that it there's already a fiber ready to run in its
103
+ // run queue.
104
+ VALUE agent = rb_ivar_get(self, ID_ivar_agent);
105
+ LibevAgent_break(agent);
106
+ }
107
+ return self;
108
+ }
109
+
110
+ VALUE Thread_switch_fiber(VALUE self) {
111
+ VALUE current_fiber = rb_fiber_current();
112
+ VALUE queue = rb_ivar_get(self, ID_run_queue);
113
+ VALUE next_fiber;
114
+ VALUE value;
115
+ VALUE agent = rb_ivar_get(self, ID_ivar_agent);
116
+ int ref_count;
117
+ int agent_was_polled = 0;1;
118
+
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
+ }
124
+
125
+ ref_count = LibevAgent_ref_count(agent);
126
+ while (1) {
127
+ next_fiber = Queue_shift_no_wait(queue);
128
+ if (next_fiber != Qnil) {
129
+ if (agent_was_polled == 0 && ref_count > 0) {
130
+ // this mechanism prevents event starvation in case the run queue never
131
+ // empties
132
+ LibevAgent_poll(agent, Qtrue, current_fiber, queue);
133
+ }
134
+ break;
135
+ }
136
+ if (ref_count == 0) break;
137
+
138
+ LibevAgent_poll(agent, Qnil, current_fiber, queue);
139
+ agent_was_polled = 1;
140
+ }
141
+
142
+ if (next_fiber == Qnil) return Qnil;
143
+
144
+ // run next fiber
145
+ value = rb_ivar_get(next_fiber, ID_runnable_value);
146
+ FIBER_TRACE(3, SYM_fiber_run, next_fiber, value);
147
+
148
+ rb_ivar_set(next_fiber, ID_runnable, Qnil);
149
+ RB_GC_GUARD(next_fiber);
150
+ RB_GC_GUARD(value);
151
+ return (next_fiber == current_fiber) ?
152
+ value : rb_funcall(next_fiber, ID_transfer, 1, value);
153
+ }
154
+
155
+ VALUE Thread_run_queue_trace(VALUE self) {
156
+ VALUE queue = rb_ivar_get(self, ID_run_queue);
157
+ Queue_trace(queue);
158
+ return self;
159
+ }
160
+
161
+ VALUE Thread_reset_fiber_scheduling(VALUE self) {
162
+ VALUE queue = rb_ivar_get(self, ID_run_queue);
163
+ Queue_clear(queue);
164
+ Thread_fiber_reset_ref_count(self);
165
+ return self;
166
+ }
167
+
168
+ 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);
170
+ if (fiber != Qnil) {
171
+ Thread_schedule_fiber_with_priority(self, fiber, resume_obj);
172
+ }
173
+
174
+ if (LibevAgent_break(agent) == Qnil) {
175
+ // we're not inside the ev_loop, so we just do a switchpoint
176
+ Thread_switch_fiber(self);
177
+ }
178
+
179
+ return self;
180
+ }
181
+
182
+ void Init_Thread() {
183
+ rb_define_method(rb_cThread, "setup_fiber_scheduling", Thread_setup_fiber_scheduling, 0);
184
+ rb_define_method(rb_cThread, "reset_fiber_scheduling", Thread_reset_fiber_scheduling, 0);
185
+ rb_define_method(rb_cThread, "fiber_scheduling_stats", Thread_fiber_scheduling_stats, 0);
186
+ rb_define_method(rb_cThread, "break_out_of_ev_loop", Thread_fiber_break_out_of_ev_loop, 2);
187
+
188
+ rb_define_method(rb_cThread, "schedule_fiber", Thread_schedule_fiber, 2);
189
+ rb_define_method(rb_cThread, "schedule_fiber_with_priority",
190
+ Thread_schedule_fiber_with_priority, 2);
191
+ rb_define_method(rb_cThread, "switch_fiber", Thread_switch_fiber, 0);
192
+ rb_define_method(rb_cThread, "run_queue_trace", Thread_run_queue_trace, 0);
193
+
194
+ ID_deactivate_all_watchers_post_fork = rb_intern("deactivate_all_watchers_post_fork");
195
+ ID_ivar_agent = rb_intern("@agent");
196
+ ID_ivar_join_wait_queue = rb_intern("@join_wait_queue");
197
+ ID_ivar_main_fiber = rb_intern("@main_fiber");
198
+ ID_ivar_result = rb_intern("@result");
199
+ ID_ivar_terminated = rb_intern("@terminated");
200
+ ID_run_queue = rb_intern("run_queue");
201
+ ID_runnable_next = rb_intern("runnable_next");
202
+ ID_stop = rb_intern("stop");
203
+
204
+ SYM_scheduled_fibers = ID2SYM(rb_intern("scheduled_fibers"));
205
+ SYM_pending_watchers = ID2SYM(rb_intern("pending_watchers"));
206
+ rb_global_variable(&SYM_scheduled_fibers);
207
+ rb_global_variable(&SYM_pending_watchers);
208
+ }
@@ -0,0 +1,11 @@
1
+ #include "polyphony.h"
2
+
3
+ int __tracing_enabled__ = 0;
4
+
5
+ VALUE __fiber_trace__(int argc, VALUE *argv, VALUE self) {
6
+ return rb_ary_new4(argc, argv);
7
+ }
8
+
9
+ void Init_Tracing() {
10
+ rb_define_global_function("__fiber_trace__", __fiber_trace__, -1);
11
+ }
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fiber'
4
+ require_relative './polyphony_ext'
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
+ require_relative './polyphony/extensions/core'
15
+ require_relative './polyphony/extensions/thread'
16
+ require_relative './polyphony/extensions/fiber'
17
+ require_relative './polyphony/extensions/io'
18
+
19
+ Thread.current.setup_fiber_scheduling
20
+ Thread.current.agent = Polyphony::LibevAgent.new
21
+
22
+ require_relative './polyphony/core/global_api'
23
+ require_relative './polyphony/core/resource_pool'
24
+ require_relative './polyphony/net'
25
+ require_relative './polyphony/adapters/process'
26
+ require_relative './polyphony/event'
27
+
28
+ # Main Polyphony API
29
+ module Polyphony
30
+ 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
+ def fork(&block)
46
+ 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.
51
+ spin_forked_block(&block).transfer
52
+ end
53
+ end
54
+
55
+ def spin_forked_block(&block)
56
+ Fiber.new do
57
+ run_forked_block(&block)
58
+ rescue SystemExit
59
+ # fall through to ensure
60
+ rescue Exception => e
61
+ e.full_message
62
+ exit!
63
+ ensure
64
+ exit_forked_process
65
+ end
66
+ end
67
+
68
+ 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
+ Thread.current.setup
77
+ Fiber.current.setup_main_fiber
78
+ Thread.current.agent.post_fork
79
+
80
+ install_terminating_signal_handlers
81
+
82
+ block.()
83
+ end
84
+
85
+ def exit_forked_process
86
+ terminate_threads
87
+ Fiber.current.shutdown_all_children
88
+
89
+ # Since fork could be called from any fiber, we explicitly call exit here.
90
+ # Otherwise, the fiber might want to pass execution to another fiber that
91
+ # previously transferred execution to the forking fiber, but doesn't exist
92
+ # anymore...
93
+ exit
94
+ end
95
+
96
+ def watch_process(cmd = nil, &block)
97
+ Polyphony::Process.watch(cmd, &block)
98
+ end
99
+
100
+ def install_terminating_signal_handlers
101
+ trap('SIGTERM', SystemExit)
102
+ orig_trap('SIGINT') do
103
+ orig_trap('SIGINT') { exit! }
104
+ Thread.current.break_out_of_ev_loop(Thread.main.main_fiber, Interrupt.new)
105
+ end
106
+ end
107
+
108
+ def terminate_threads
109
+ threads = Thread.list - [Thread.current]
110
+ return if threads.empty?
111
+
112
+ threads.each(&:kill)
113
+ threads.each(&:join)
114
+ end
115
+
116
+ attr_accessor :original_pid
117
+
118
+ def install_at_exit_handler
119
+ @original_pid = ::Process.pid
120
+
121
+ # This at_exit handler is needed only when the original process exits. Due to
122
+ # the behaviour of fibers on fork (and especially on exit from forked
123
+ # processes,) we use a separate mechanism to terminate fibers in forked
124
+ # processes (see Polyphony.fork).
125
+ at_exit do
126
+ next unless @original_pid == ::Process.pid
127
+
128
+ Polyphony.terminate_threads
129
+ Fiber.current.shutdown_all_children
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ Polyphony.install_terminating_signal_handlers
136
+ Polyphony.install_at_exit_handler
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+
5
+ require_relative './core/thread_pool'
6
+
7
+ ::File.singleton_class.instance_eval do
8
+ alias_method :orig_stat, :stat
9
+ def stat(path)
10
+ ThreadPool.process { orig_stat(path) }
11
+ end
12
+ end
13
+
14
+ ::IO.singleton_class.instance_eval do
15
+ alias_method :orig_read, :read
16
+ def read(path)
17
+ ThreadPool.process { orig_read(path) }
18
+ end
19
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'polyphony'
4
+
5
+ if Object.constants.include?(:Reline)
6
+ class Reline::ANSI
7
+ def self.select(read_ios = [], write_ios = [], error_ios = [], timeout = nil)
8
+ p [:select, read_ios]
9
+ raise if read_ios.size > 1
10
+ raise if write_ios.size > 0
11
+ raise if error_ios.size > 0
12
+
13
+ fiber = Fiber.current
14
+ timer = spin do
15
+ sleep timeout
16
+ fiber.cancel
17
+ end
18
+ read_ios.each do |io|
19
+ Thread.current.agent.wait_io(io, false)
20
+ return [io]
21
+ end
22
+ rescue Polyphony::Cancel
23
+ return nil
24
+ ensure
25
+ timer.stop
26
+ end
27
+ end
28
+ else
29
+ # readline blocks the current thread, so we offload it to the blocking-ops
30
+ # thread pool. That way, the reactor loop can keep running while waiting for
31
+ # readline to return
32
+ module ::Readline
33
+ alias_method :orig_readline, :readline
34
+
35
+ Workers = Polyphony::ThreadPool.new
36
+
37
+ def readline(*args)
38
+ p :readline
39
+ # caller.each do |l|
40
+ # STDOUT.orig_puts l
41
+ # end
42
+ Workers.process { orig_readline(*args) }
43
+ end
44
+ end
45
+
46
+ # RubyLex patches
47
+ class ::RubyLex
48
+ class TerminateLineInput2 < RuntimeError
49
+ end
50
+ const_set(:TerminateLineInput, TerminateLineInput2)
51
+ end
52
+ end