polyphony 0.71 → 0.74

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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -0
  3. data/.github/workflows/test.yml +15 -11
  4. data/.github/workflows/test_io_uring.yml +32 -0
  5. data/.gitignore +3 -1
  6. data/CHANGELOG.md +33 -4
  7. data/Gemfile.lock +16 -13
  8. data/TODO.md +1 -1
  9. data/bin/pdbg +1 -1
  10. data/docs/_user-guide/all-about-timers.md +1 -1
  11. data/docs/api-reference/exception.md +5 -1
  12. data/docs/api-reference/fiber.md +2 -2
  13. data/docs/faq.md +1 -1
  14. data/docs/getting-started/overview.md +8 -8
  15. data/docs/getting-started/tutorial.md +3 -3
  16. data/docs/main-concepts/concurrency.md +1 -1
  17. data/docs/main-concepts/extending.md +3 -3
  18. data/docs/main-concepts/fiber-scheduling.md +1 -1
  19. data/examples/core/calc.rb +37 -0
  20. data/examples/core/calc_with_restart.rb +40 -0
  21. data/examples/core/calc_with_supervise.rb +37 -0
  22. data/examples/core/message_based_supervision.rb +1 -1
  23. data/examples/core/ring.rb +29 -0
  24. data/examples/io/rack_server.rb +1 -1
  25. data/examples/io/tunnel.rb +1 -1
  26. data/examples/performance/fiber_transfer.rb +1 -1
  27. data/examples/performance/line_splitting.rb +1 -1
  28. data/examples/performance/thread-vs-fiber/compare.rb +1 -1
  29. data/ext/polyphony/backend_common.c +88 -18
  30. data/ext/polyphony/backend_common.h +8 -1
  31. data/ext/polyphony/backend_io_uring.c +280 -164
  32. data/ext/polyphony/backend_io_uring_context.c +2 -1
  33. data/ext/polyphony/backend_io_uring_context.h +3 -2
  34. data/ext/polyphony/backend_libev.c +42 -38
  35. data/ext/polyphony/event.c +5 -2
  36. data/ext/polyphony/extconf.rb +25 -13
  37. data/ext/polyphony/polyphony.c +10 -1
  38. data/ext/polyphony/polyphony.h +7 -1
  39. data/ext/polyphony/queue.c +12 -7
  40. data/ext/polyphony/runqueue_ring_buffer.c +6 -3
  41. data/ext/polyphony/socket_extensions.c +5 -2
  42. data/ext/polyphony/thread.c +1 -1
  43. data/lib/polyphony/adapters/irb.rb +11 -1
  44. data/lib/polyphony/{extensions → core}/debug.rb +0 -0
  45. data/lib/polyphony/core/global_api.rb +3 -6
  46. data/lib/polyphony/core/timer.rb +2 -2
  47. data/lib/polyphony/debugger.rb +3 -3
  48. data/lib/polyphony/extensions/exception.rb +45 -0
  49. data/lib/polyphony/extensions/fiber.rb +87 -11
  50. data/lib/polyphony/extensions/io.rb +2 -2
  51. data/lib/polyphony/extensions/{core.rb → kernel.rb} +0 -73
  52. data/lib/polyphony/extensions/openssl.rb +20 -5
  53. data/lib/polyphony/extensions/process.rb +19 -0
  54. data/lib/polyphony/extensions/socket.rb +20 -9
  55. data/lib/polyphony/extensions/thread.rb +9 -3
  56. data/lib/polyphony/extensions/timeout.rb +10 -0
  57. data/lib/polyphony/extensions.rb +9 -0
  58. data/lib/polyphony/version.rb +1 -1
  59. data/lib/polyphony.rb +2 -4
  60. data/polyphony.gemspec +1 -1
  61. data/test/coverage.rb +2 -2
  62. data/test/test_backend.rb +15 -17
  63. data/test/test_event.rb +1 -1
  64. data/test/test_ext.rb +1 -1
  65. data/test/test_fiber.rb +31 -7
  66. data/test/test_global_api.rb +23 -14
  67. data/test/test_io.rb +5 -5
  68. data/test/test_kernel.rb +2 -2
  69. data/test/test_process_supervision.rb +1 -1
  70. data/test/test_queue.rb +6 -6
  71. data/test/test_signal.rb +20 -1
  72. data/test/test_socket.rb +45 -10
  73. data/test/test_supervise.rb +85 -0
  74. data/test/test_sync.rb +2 -2
  75. data/test/test_thread.rb +22 -2
  76. data/test/test_thread_pool.rb +2 -2
  77. data/test/test_throttler.rb +3 -3
  78. data/test/test_timer.rb +3 -3
  79. data/test/test_trace.rb +1 -1
  80. metadata +19 -9
@@ -34,7 +34,7 @@ inline void backend_base_mark(struct Backend_base *base) {
34
34
  void backend_base_reset(struct Backend_base *base) {
35
35
  runqueue_finalize(&base->runqueue);
36
36
  runqueue_finalize(&base->parked_runqueue);
37
-
37
+
38
38
  runqueue_initialize(&base->runqueue);
39
39
  runqueue_initialize(&base->parked_runqueue);
40
40
 
@@ -46,13 +46,13 @@ void backend_base_reset(struct Backend_base *base) {
46
46
  base->idle_gc_period = 0;
47
47
  base->idle_gc_last_time = 0;
48
48
  base->idle_proc = Qnil;
49
- base->trace_proc = Qnil;
49
+ base->trace_proc = Qnil;
50
50
  }
51
51
 
52
52
  const unsigned int ANTI_STARVE_SWITCH_COUNT_THRESHOLD = 64;
53
53
 
54
54
  inline void conditional_nonblocking_poll(VALUE backend, struct Backend_base *base, VALUE current, VALUE next) {
55
- if ((base->switch_count % ANTI_STARVE_SWITCH_COUNT_THRESHOLD) == 0 || next == current)
55
+ if ((base->switch_count % ANTI_STARVE_SWITCH_COUNT_THRESHOLD) == 0 || next == current)
56
56
  Backend_poll(backend, Qnil);
57
57
  }
58
58
 
@@ -62,7 +62,7 @@ VALUE backend_base_switch_fiber(VALUE backend, struct Backend_base *base) {
62
62
  unsigned int pending_ops_count = base->pending_count;
63
63
  unsigned int backend_was_polled = 0;
64
64
  unsigned int idle_tasks_run_count = 0;
65
-
65
+
66
66
  base->switch_count++;
67
67
  COND_TRACE(base, 2, SYM_fiber_switchpoint, current_fiber);
68
68
 
@@ -82,7 +82,7 @@ VALUE backend_base_switch_fiber(VALUE backend, struct Backend_base *base) {
82
82
 
83
83
  break;
84
84
  }
85
-
85
+
86
86
  if (!idle_tasks_run_count) {
87
87
  idle_tasks_run_count++;
88
88
  backend_run_idle_tasks(base);
@@ -106,14 +106,14 @@ VALUE backend_base_switch_fiber(VALUE backend, struct Backend_base *base) {
106
106
 
107
107
  void backend_base_schedule_fiber(VALUE thread, VALUE backend, struct Backend_base *base, VALUE fiber, VALUE value, int prioritize) {
108
108
  int already_runnable;
109
+ runqueue_t *runqueue;
109
110
 
110
111
  if (rb_fiber_alive_p(fiber) != Qtrue) return;
111
112
  already_runnable = rb_ivar_get(fiber, ID_ivar_runnable) != Qnil;
112
113
 
113
114
  COND_TRACE(base, 4, SYM_fiber_schedule, fiber, value, prioritize ? Qtrue : Qfalse);
114
115
 
115
- runqueue_t *runqueue = rb_ivar_get(fiber, ID_ivar_parked) == Qtrue ?
116
- &base->parked_runqueue : &base->runqueue;
116
+ runqueue = rb_ivar_get(fiber, ID_ivar_parked) == Qtrue ? &base->parked_runqueue : &base->runqueue;
117
117
 
118
118
  (prioritize ? runqueue_unshift : runqueue_push)(runqueue, fiber, value, already_runnable);
119
119
  if (!already_runnable) {
@@ -202,11 +202,40 @@ inline rb_encoding* io_read_encoding(rb_io_t *fptr) {
202
202
  }
203
203
 
204
204
  inline VALUE io_enc_str(VALUE str, rb_io_t *fptr) {
205
- OBJ_TAINT(str);
206
205
  rb_enc_associate(str, io_read_encoding(fptr));
207
206
  return str;
208
207
  }
209
208
 
209
+ static inline void free_io_buffer(rb_io_buffer_t *buf)
210
+ {
211
+ if (buf->ptr) {
212
+ ruby_xfree(buf->ptr);
213
+ buf->ptr = NULL;
214
+ }
215
+ }
216
+
217
+ static inline void clear_codeconv(rb_io_t *fptr) {
218
+ if (fptr->readconv) {
219
+ rb_econv_close(fptr->readconv);
220
+ fptr->readconv = NULL;
221
+ }
222
+ free_io_buffer(&fptr->cbuf);
223
+
224
+ if (fptr->writeconv) {
225
+ rb_econv_close(fptr->writeconv);
226
+ fptr->writeconv = NULL;
227
+ }
228
+ fptr->writeconv_initialized = 0;
229
+ }
230
+
231
+ void fptr_finalize(rb_io_t *fptr) {
232
+ fptr->fd = -1;
233
+ fptr->stdio_file = 0;
234
+ free_io_buffer(&fptr->rbuf);
235
+ free_io_buffer(&fptr->wbuf);
236
+ clear_codeconv(fptr);
237
+ }
238
+
210
239
  //////////////////////////////////////////////////////////////////////
211
240
  //////////////////////////////////////////////////////////////////////
212
241
 
@@ -239,13 +268,25 @@ inline void rectify_io_file_pos(rb_io_t *fptr) {
239
268
 
240
269
  inline double current_time() {
241
270
  struct timespec ts;
271
+ double t;
272
+ uint64_t ns;
273
+
242
274
  clock_gettime(CLOCK_MONOTONIC, &ts);
243
- long long ns = ts.tv_sec;
275
+ ns = ts.tv_sec;
244
276
  ns = ns * 1e9 + ts.tv_nsec;
245
- double t = ns;
277
+ t = ns;
246
278
  return t / 1e9;
247
279
  }
248
280
 
281
+ inline uint64_t current_time_ns() {
282
+ struct timespec ts;
283
+ uint64_t ns;
284
+
285
+ clock_gettime(CLOCK_MONOTONIC, &ts);
286
+ ns = ts.tv_sec;
287
+ return ns * 1e9 + ts.tv_nsec;
288
+ }
289
+
249
290
  inline VALUE backend_timeout_exception(VALUE exception) {
250
291
  if (rb_obj_is_kind_of(exception, rb_cArray) == Qtrue)
251
292
  return rb_funcall(rb_ary_entry(exception, 0), ID_new, 1, rb_ary_entry(exception, 1));
@@ -270,6 +311,9 @@ VALUE Backend_timeout_ensure_safe(VALUE arg) {
270
311
  static VALUE empty_string = Qnil;
271
312
 
272
313
  VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags) {
314
+ VALUE joined;
315
+ VALUE result;
316
+
273
317
  switch (RARRAY_LEN(ary)) {
274
318
  case 0:
275
319
  return Qnil;
@@ -280,14 +324,16 @@ VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags) {
280
324
  empty_string = rb_str_new_literal("");
281
325
  rb_global_variable(&empty_string);
282
326
  }
283
- VALUE joined = rb_ary_join(ary, empty_string);
284
- VALUE result = Backend_send(self, io, joined, flags);
327
+ joined = rb_ary_join(ary, empty_string);
328
+ result = Backend_send(self, io, joined, flags);
285
329
  RB_GC_GUARD(joined);
286
330
  return result;
287
331
  }
288
332
  }
289
333
 
290
334
  inline void io_verify_blocking_mode(rb_io_t *fptr, VALUE io, VALUE blocking) {
335
+ int flags;
336
+ int is_nonblocking;
291
337
  VALUE blocking_mode = rb_ivar_get(io, ID_ivar_blocking_mode);
292
338
  if (blocking == blocking_mode) return;
293
339
 
@@ -297,10 +343,10 @@ inline void io_verify_blocking_mode(rb_io_t *fptr, VALUE io, VALUE blocking) {
297
343
  if (blocking != Qtrue)
298
344
  rb_w32_set_nonblock(fptr->fd);
299
345
  #elif defined(F_GETFL)
300
- int flags = fcntl(fptr->fd, F_GETFL);
346
+ flags = fcntl(fptr->fd, F_GETFL);
301
347
  if (flags == -1) return;
302
- int is_nonblocking = flags & O_NONBLOCK;
303
-
348
+ is_nonblocking = flags & O_NONBLOCK;
349
+
304
350
  if (blocking == Qtrue) {
305
351
  if (!is_nonblocking) return;
306
352
  flags &= ~O_NONBLOCK;
@@ -313,12 +359,14 @@ inline void io_verify_blocking_mode(rb_io_t *fptr, VALUE io, VALUE blocking) {
313
359
  }
314
360
 
315
361
  inline void backend_run_idle_tasks(struct Backend_base *base) {
362
+ double now;
363
+
316
364
  if (base->idle_proc != Qnil)
317
365
  rb_funcall(base->idle_proc, ID_call, 0);
318
366
 
319
367
  if (base->idle_gc_period == 0) return;
320
368
 
321
- double now = current_time();
369
+ now = current_time();
322
370
  if (now - base->idle_gc_last_time < base->idle_gc_period) return;
323
371
 
324
372
  base->idle_gc_last_time = now;
@@ -375,7 +423,7 @@ void backend_setup_stats_symbols() {
375
423
  SYM_switch_count = ID2SYM(rb_intern("switch_count"));
376
424
  SYM_poll_count = ID2SYM(rb_intern("poll_count"));
377
425
  SYM_pending_ops = ID2SYM(rb_intern("pending_ops"));
378
-
426
+
379
427
  rb_global_variable(&SYM_runqueue_size);
380
428
  rb_global_variable(&SYM_runqueue_length);
381
429
  rb_global_variable(&SYM_runqueue_max_length);
@@ -383,4 +431,26 @@ void backend_setup_stats_symbols() {
383
431
  rb_global_variable(&SYM_switch_count);
384
432
  rb_global_variable(&SYM_poll_count);
385
433
  rb_global_variable(&SYM_pending_ops);
386
- }
434
+ }
435
+
436
+ int backend_getaddrinfo(VALUE host, VALUE port, struct sockaddr **ai_addr) {
437
+ VALUE port_string;
438
+ struct addrinfo hints;
439
+ struct addrinfo *addrinfo_result;
440
+ int ret;
441
+
442
+ memset(&hints, 0, sizeof(struct addrinfo));
443
+ hints.ai_family = AF_UNSPEC; /* allow IPv4 or IPv6 */
444
+ hints.ai_socktype = SOCK_STREAM;
445
+
446
+ port_string = rb_funcall(port, ID_to_s, 0);
447
+ ret = getaddrinfo(StringValueCStr(host), StringValueCStr(port_string), &hints, &addrinfo_result);
448
+ RB_GC_GUARD(port_string);
449
+ if (ret != 0) {
450
+ VALUE msg = rb_str_new2(gai_strerror(ret));
451
+ rb_funcall(rb_mKernel, ID_raise, 1, msg);
452
+ RB_GC_GUARD(msg);
453
+ }
454
+ *ai_addr = addrinfo_result->ai_addr;
455
+ return addrinfo_result->ai_addrlen;
456
+ }
@@ -1,6 +1,11 @@
1
1
  #ifndef BACKEND_COMMON_H
2
2
  #define BACKEND_COMMON_H
3
3
 
4
+ #include <sys/types.h>
5
+ #include <arpa/inet.h>
6
+ #include <netinet/in.h>
7
+ #include <netdb.h>
8
+
4
9
  #include "ruby.h"
5
10
  #include "ruby/io.h"
6
11
  #include "runqueue.h"
@@ -68,6 +73,7 @@ void io_shrink_read_string(VALUE str, long n);
68
73
  void io_set_read_length(VALUE str, long n, int shrinkable);
69
74
  rb_encoding* io_read_encoding(rb_io_t *fptr);
70
75
  VALUE io_enc_str(VALUE str, rb_io_t *fptr);
76
+ void fptr_finalize(rb_io_t *fptr);
71
77
 
72
78
  //////////////////////////////////////////////////////////////////////
73
79
  //////////////////////////////////////////////////////////////////////
@@ -82,7 +88,6 @@ VALUE backend_snooze();
82
88
  shrinkable = io_setstrbuf(&str, len); \
83
89
  buf = RSTRING_PTR(str); \
84
90
  total = 0; \
85
- OBJ_TAINT(str); \
86
91
  }
87
92
 
88
93
  #define READ_LOOP_YIELD_STR() { \
@@ -101,6 +106,7 @@ VALUE backend_snooze();
101
106
 
102
107
  void rectify_io_file_pos(rb_io_t *fptr);
103
108
  double current_time();
109
+ uint64_t current_time_ns();
104
110
  VALUE backend_timeout_exception(VALUE exception);
105
111
  VALUE Backend_timeout_ensure_safe(VALUE arg);
106
112
  VALUE Backend_timeout_ensure_safe(VALUE arg);
@@ -109,5 +115,6 @@ VALUE Backend_stats(VALUE self);
109
115
  void backend_run_idle_tasks(struct Backend_base *base);
110
116
  void io_verify_blocking_mode(rb_io_t *fptr, VALUE io, VALUE blocking);
111
117
  void backend_setup_stats_symbols();
118
+ int backend_getaddrinfo(VALUE host, VALUE port, struct sockaddr **ai_addr);
112
119
 
113
120
  #endif /* BACKEND_COMMON_H */