polyphony 0.71 → 0.74

Sign up to get free protection for your applications and to get access to all the features.
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 */