polyphony 1.5 → 1.6

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 (99) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/CHANGELOG.md +14 -0
  4. data/TODO.md +0 -4
  5. data/ext/polyphony/backend_io_uring.c +34 -1
  6. data/ext/polyphony/backend_io_uring_context.c +24 -18
  7. data/ext/polyphony/backend_io_uring_context.h +4 -2
  8. data/ext/polyphony/backend_libev.c +4 -7
  9. data/ext/polyphony/event.c +21 -0
  10. data/ext/polyphony/extconf.rb +20 -18
  11. data/ext/polyphony/fiber.c +0 -2
  12. data/ext/polyphony/polyphony.c +2 -0
  13. data/ext/polyphony/polyphony.h +5 -0
  14. data/ext/polyphony/ring_buffer.c +1 -0
  15. data/ext/polyphony/runqueue_ring_buffer.c +1 -0
  16. data/ext/polyphony/thread.c +63 -0
  17. data/lib/polyphony/adapters/open3.rb +190 -0
  18. data/lib/polyphony/core/sync.rb +83 -13
  19. data/lib/polyphony/core/timer.rb +7 -25
  20. data/lib/polyphony/extensions/exception.rb +15 -0
  21. data/lib/polyphony/extensions/fiber.rb +14 -13
  22. data/lib/polyphony/extensions/io.rb +56 -14
  23. data/lib/polyphony/extensions/kernel.rb +1 -1
  24. data/lib/polyphony/extensions/object.rb +1 -13
  25. data/lib/polyphony/extensions/process.rb +76 -1
  26. data/lib/polyphony/extensions/thread.rb +19 -27
  27. data/lib/polyphony/version.rb +1 -1
  28. data/lib/polyphony.rb +11 -5
  29. data/test/helper.rb +46 -4
  30. data/test/open3/envutil.rb +380 -0
  31. data/test/open3/find_executable.rb +24 -0
  32. data/test/stress.rb +11 -7
  33. data/test/test_backend.rb +7 -2
  34. data/test/test_event.rb +10 -3
  35. data/test/test_ext.rb +2 -1
  36. data/test/test_fiber.rb +16 -4
  37. data/test/test_global_api.rb +13 -12
  38. data/test/test_io.rb +39 -0
  39. data/test/test_kernel.rb +2 -2
  40. data/test/test_monitor.rb +356 -0
  41. data/test/test_open3.rb +338 -0
  42. data/test/test_signal.rb +5 -1
  43. data/test/test_socket.rb +6 -3
  44. data/test/test_sync.rb +46 -0
  45. data/test/test_thread.rb +10 -1
  46. data/test/test_thread_pool.rb +5 -0
  47. data/test/test_throttler.rb +1 -1
  48. data/test/test_timer.rb +8 -2
  49. data/test/test_trace.rb +2 -0
  50. data/vendor/liburing/.github/workflows/build.yml +8 -0
  51. data/vendor/liburing/.gitignore +1 -0
  52. data/vendor/liburing/CHANGELOG +8 -0
  53. data/vendor/liburing/configure +17 -25
  54. data/vendor/liburing/debian/liburing-dev.manpages +2 -0
  55. data/vendor/liburing/debian/rules +2 -1
  56. data/vendor/liburing/examples/Makefile +2 -1
  57. data/vendor/liburing/examples/io_uring-udp.c +11 -3
  58. data/vendor/liburing/examples/rsrc-update-bench.c +100 -0
  59. data/vendor/liburing/liburing.spec +1 -1
  60. data/vendor/liburing/make-debs.sh +4 -2
  61. data/vendor/liburing/src/Makefile +5 -5
  62. data/vendor/liburing/src/arch/aarch64/lib.h +1 -1
  63. data/vendor/liburing/src/include/liburing/io_uring.h +41 -16
  64. data/vendor/liburing/src/include/liburing.h +86 -11
  65. data/vendor/liburing/src/int_flags.h +1 -0
  66. data/vendor/liburing/src/liburing-ffi.map +12 -0
  67. data/vendor/liburing/src/liburing.map +8 -0
  68. data/vendor/liburing/src/register.c +7 -2
  69. data/vendor/liburing/src/setup.c +373 -81
  70. data/vendor/liburing/test/232c93d07b74.c +3 -3
  71. data/vendor/liburing/test/Makefile +10 -3
  72. data/vendor/liburing/test/accept.c +2 -1
  73. data/vendor/liburing/test/buf-ring.c +35 -75
  74. data/vendor/liburing/test/connect-rep.c +204 -0
  75. data/vendor/liburing/test/coredump.c +59 -0
  76. data/vendor/liburing/test/fallocate.c +9 -0
  77. data/vendor/liburing/test/fd-pass.c +34 -3
  78. data/vendor/liburing/test/file-verify.c +27 -6
  79. data/vendor/liburing/test/helpers.c +3 -1
  80. data/vendor/liburing/test/io_uring_register.c +25 -28
  81. data/vendor/liburing/test/io_uring_setup.c +1 -1
  82. data/vendor/liburing/test/poll-cancel-all.c +29 -5
  83. data/vendor/liburing/test/poll-race-mshot.c +6 -22
  84. data/vendor/liburing/test/read-write.c +53 -0
  85. data/vendor/liburing/test/recv-msgall.c +21 -23
  86. data/vendor/liburing/test/reg-fd-only.c +55 -0
  87. data/vendor/liburing/test/reg-hint.c +56 -0
  88. data/vendor/liburing/test/regbuf-merge.c +91 -0
  89. data/vendor/liburing/test/ringbuf-read.c +2 -10
  90. data/vendor/liburing/test/send_recvmsg.c +5 -16
  91. data/vendor/liburing/test/shutdown.c +2 -1
  92. data/vendor/liburing/test/socket-io-cmd.c +215 -0
  93. data/vendor/liburing/test/socket-rw-eagain.c +2 -1
  94. data/vendor/liburing/test/socket-rw-offset.c +2 -1
  95. data/vendor/liburing/test/socket-rw.c +2 -1
  96. data/vendor/liburing/test/timeout.c +276 -0
  97. data/vendor/liburing/test/xattr.c +38 -25
  98. metadata +14 -3
  99. data/vendor/liburing/test/timeout-overflow.c +0 -204
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8ec9aae8d4c83ff8dd3332187fdd12fc598596dc4bbab11eaad6400e61769684
4
- data.tar.gz: a42cedfc33172dbacc4f61e16fa0a6fce3eb772e78e8829a2dfe744601c77b1c
3
+ metadata.gz: 9bed700ffe759bddcee9727be8e3c11d194f45fe44f05a0a879c92c7b221c6c5
4
+ data.tar.gz: 0f864fa278709cb00c6b2fae7e3725ef581b05f2b111661a7b5a8d4be1696346
5
5
  SHA512:
6
- metadata.gz: 2f6c4a70a56c854dfaa707baeac665ed04adcf04a0f67c25419b2de50d367b155ed6aad3e6bb3dcdc6391af08f4a4dafc3afcadecc499f26cb4b5a06a5d0c69e
7
- data.tar.gz: e8b74bd9dbec6a0d05e5510514ccff97792e04c0742e85624087eb92fe55d195b553288c0e3132ab468ba7a28a921ead91ce20ce26d6dab93f5b5a467f5f61bd
6
+ metadata.gz: e94ad30b3e9392f8afc0ae85a22afbe6ee6ab4b60c6fa1c412ab6d571d51ebec6e7bf8280106f6d0d7ad6c479dd8b6d748f4aed70b8dc2791f16a1d650fd7667
7
+ data.tar.gz: 2d8a3f5f2b9dd4d5107dedba344d5e4c46efc43546878779918997e3cb81c9087428e4872e70a631169a0b9d2ff3f1b8b745aa3668817a30475f921106b117e9
data/.rubocop.yml CHANGED
@@ -81,6 +81,8 @@ Metrics/MethodLength:
81
81
  Exclude:
82
82
  - lib/polyphony/extensions/io.rb
83
83
  - lib/polyphony/extensions/fiber.rb
84
+ - lib/polyphony/extensions/thread.rb
85
+ - lib/polyphony/adapters/open3.rb
84
86
  - test/**/*.rb
85
87
  - examples/**/*.rb
86
88
 
@@ -94,6 +96,7 @@ Metrics/ClassLength:
94
96
  - lib/polyphony/extensions/io.rb
95
97
  - lib/polyphony/extensions/fiber.rb
96
98
  - lib/polyphony/extensions/object.rb
99
+ - lib/polyphony/extensions/thread.rb
97
100
  - test/**/*.rb
98
101
  - examples/**/*.rb
99
102
 
data/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## 1.6 2023-08-05
2
+
3
+ - Refactor exception instantiation
4
+ - Fix race condition in `Thread#join`
5
+ - Fix race condition in `Event` class
6
+ - Add support for open3 API
7
+ - Update liburing to version 2.4
8
+ - Various fixes to `IO` instance methods
9
+ - Fix `IO#double_splice` on non-Linux OSes
10
+ - Implement `IO.copy_stream`
11
+ - Add `Fiber#value` as alias to `Fiber#await`
12
+ - Implement fiber-aware `Monitor` class (#113)
13
+ - Implement Thread#value
14
+
1
15
  ## 1.5 2023-07-28
2
16
 
3
17
  - Refactor backend_await in io_uring backend
data/TODO.md CHANGED
@@ -1,6 +1,3 @@
1
- - Look at RPC benchmark more closely: is there a way to reduce the overhead of
2
- the `backend_base_switch_fiber` function?
3
-
4
1
  - io_uring backend:
5
2
  - if `io_uring_get_sqe` returns null, call `io_uring_submit`, (snooze fiber)?
6
3
  and try again
@@ -25,7 +22,6 @@
25
22
 
26
23
  ## Roadmap for Polyphony 2
27
24
 
28
- - allow backend selection at runtime?
29
25
  - Debugging
30
26
  - Eat your own dogfood: need a good tool to check what's going on when some
31
27
  test fails
@@ -186,10 +186,30 @@ static void handle_multishot_accept_completion(op_context_t *ctx, struct io_urin
186
186
  }
187
187
  }
188
188
 
189
+ static void handle_multishot_timeout_completion(
190
+ op_context_t *ctx, struct io_uring_cqe *cqe, Backend_t *backend
191
+ )
192
+ {
193
+ if (ctx->result == -ECANCELED) {
194
+ context_store_release(&backend->store, ctx);
195
+ }
196
+ else {
197
+ int has_more = cqe->flags & IORING_CQE_F_MORE;
198
+ if (!has_more) {
199
+ context_store_release(&backend->store, ctx);
200
+ }
201
+ if (ctx->fiber) {
202
+ Fiber_make_runnable(ctx->fiber, has_more ? Qtrue : Qnil);
203
+ }
204
+ }
205
+ }
206
+
189
207
  static void handle_multishot_completion(op_context_t *ctx, struct io_uring_cqe *cqe, Backend_t *backend) {
190
208
  switch (ctx->type) {
191
209
  case OP_MULTISHOT_ACCEPT:
192
210
  return handle_multishot_accept_completion(ctx, cqe, backend);
211
+ case OP_MULTISHOT_TIMEOUT:
212
+ return handle_multishot_timeout_completion(ctx, cqe, backend);
193
213
  default:
194
214
  printf("Unexpected multishot completion for op type %d\n", ctx->type);
195
215
  }
@@ -199,6 +219,12 @@ static inline void io_uring_backend_handle_completion(struct io_uring_cqe *cqe,
199
219
  op_context_t *ctx = io_uring_cqe_get_data(cqe);
200
220
  if (!ctx) return;
201
221
 
222
+ // if (ctx->type == OP_TIMEOUT) {
223
+ // double now = current_time_ns() / 1e9;
224
+ // double elapsed = now - ctx->ts;
225
+ // printf("%13.6f CQE timeout %p:%d (elapsed: %9.6f)\n", now, ctx, ctx->id, elapsed);
226
+ // }
227
+
202
228
  // printf("cqe ctx %p id: %d result: %d (%s, ref_count: %d)\n", ctx, ctx->id, cqe->res, op_type_to_str(ctx->type), ctx->ref_count);
203
229
  ctx->result = cqe->res;
204
230
  if (ctx->ref_count == MULTISHOT_REFCOUNT) {
@@ -663,6 +689,7 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
663
689
  result = io_uring_backend_defer_submit_and_await(self, backend, sqe, ctx, &resume_value);
664
690
  completed = context_store_release(&backend->store, ctx);
665
691
  if (!completed) {
692
+ TRACE_FREE(iov);
666
693
  free(iov);
667
694
  context_attach_buffers(ctx, argc, argv);
668
695
  RAISE_IF_EXCEPTION(resume_value);
@@ -671,6 +698,7 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
671
698
  RB_GC_GUARD(resume_value);
672
699
 
673
700
  if (result < 0) {
701
+ TRACE_FREE(iov);
674
702
  free(iov);
675
703
  rb_syserr_fail(-result, strerror(-result));
676
704
  }
@@ -693,6 +721,7 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
693
721
  }
694
722
  }
695
723
 
724
+ TRACE_FREE(iov);
696
725
  free(iov);
697
726
  return INT2FIX(total_written);
698
727
  }
@@ -1433,6 +1462,10 @@ int io_uring_backend_submit_timeout_and_await(VALUE self, Backend_t *backend, do
1433
1462
  struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
1434
1463
  op_context_t *ctx = context_store_acquire(&backend->store, OP_TIMEOUT);
1435
1464
 
1465
+ // double now = current_time_ns() / 1e9;
1466
+ // ctx->ts = now;
1467
+ // printf("%13.6f SQE timeout %p:%d (%g)\n", now, ctx, ctx->id, duration);
1468
+
1436
1469
  io_uring_prep_timeout(sqe, &ts, 0, 0);
1437
1470
  io_uring_backend_defer_submit_and_await(self, backend, sqe, ctx, resume_value);
1438
1471
  return context_store_release(&backend->store, ctx);
@@ -1572,7 +1605,7 @@ VALUE Backend_waitpid(VALUE self, VALUE pid) {
1572
1605
  else
1573
1606
  rb_syserr_fail(e, strerror(e));
1574
1607
  }
1575
- return rb_ary_new_from_args(2, INT2FIX(ret), INT2FIX(WEXITSTATUS(status)));
1608
+ return rb_ary_new_from_args(2, INT2FIX(ret), INT2FIX(status));
1576
1609
  }
1577
1610
 
1578
1611
  /*
@@ -6,20 +6,23 @@
6
6
 
7
7
  const char *op_type_to_str(enum op_type type) {
8
8
  switch (type) {
9
- case OP_ACCEPT: return "ACCEPT";
10
- case OP_CHAIN: return "CHAIN";
11
- case OP_CLOSE: return "CLOSE";
12
- case OP_CONNECT: return "CONNECT";
13
- case OP_POLL: return "POLL";
14
- case OP_READ: return "READ";
15
- case OP_RECV: return "RECV";
16
- case OP_RECVMSG: return "RECVMSG";
17
- case OP_SEND: return "SEND";
18
- case OP_SENDMSG: return "SENDMSG";
19
- case OP_SPLICE: return "SPLICE";
20
- case OP_TIMEOUT: return "TIMEOUT";
21
- case OP_WRITEV: return "WRITEV";
22
- case OP_WRITE: return "WRITE";
9
+ case OP_ACCEPT: return "ACCEPT";
10
+ case OP_CHAIN: return "CHAIN";
11
+ case OP_CLOSE: return "CLOSE";
12
+ case OP_CONNECT: return "CONNECT";
13
+ case OP_POLL: return "POLL";
14
+ case OP_READ: return "READ";
15
+ case OP_RECV: return "RECV";
16
+ case OP_RECVMSG: return "RECVMSG";
17
+ case OP_SEND: return "SEND";
18
+ case OP_SENDMSG: return "SENDMSG";
19
+ case OP_SPLICE: return "SPLICE";
20
+ case OP_TIMEOUT: return "TIMEOUT";
21
+ case OP_WRITEV: return "WRITEV";
22
+ case OP_WRITE: return "WRITE";
23
+
24
+ case OP_MULTISHOT_ACCEPT: return "ACCEPT MULTISHOT";
25
+ case OP_MULTISHOT_TIMEOUT: return "ACCEPT MULTISHOT";
23
26
 
24
27
  default: return "";
25
28
  };
@@ -71,14 +74,15 @@ inline int context_store_release(op_context_store_t *store, op_context_t *ctx) {
71
74
 
72
75
  // If a multishot ctx is released, we pretend its ref count is 1, so it will
73
76
  // be returned to the store.
74
- if (ctx->ref_count == MULTISHOT_REFCOUNT) {
75
- ctx->ref_count = 1;
76
- }
77
+ if (ctx->ref_count == MULTISHOT_REFCOUNT) ctx->ref_count = 1;
77
78
 
78
79
  ctx->ref_count--;
79
80
  if (ctx->ref_count) return 0;
80
81
 
81
- if (ctx->buffer_count > 1) free(ctx->buffers);
82
+ if (ctx->buffer_count > 1) {
83
+ TRACE_FREE(ctx->buffers);
84
+ free(ctx->buffers);
85
+ }
82
86
 
83
87
  store->taken_count--;
84
88
  store->available_count++;
@@ -97,11 +101,13 @@ inline int context_store_release(op_context_store_t *store, op_context_t *ctx) {
97
101
  void context_store_free(op_context_store_t *store) {
98
102
  while (store->available) {
99
103
  op_context_t *next = store->available->next;
104
+ TRACE_FREE(store->available);
100
105
  free(store->available);
101
106
  store->available = next;
102
107
  }
103
108
  while (store->taken) {
104
109
  op_context_t *next = store->taken->next;
110
+ TRACE_FREE(store->taken);
105
111
  free(store->taken);
106
112
  store->taken = next;
107
113
  }
@@ -8,7 +8,6 @@ enum op_type {
8
8
  OP_CHAIN,
9
9
  OP_CLOSE,
10
10
  OP_CONNECT,
11
- OP_MULTISHOT_ACCEPT,
12
11
  OP_NONE,
13
12
  OP_POLL,
14
13
  OP_READ,
@@ -19,7 +18,9 @@ enum op_type {
19
18
  OP_SPLICE,
20
19
  OP_TIMEOUT,
21
20
  OP_WRITEV,
22
- OP_WRITE
21
+ OP_WRITE,
22
+ OP_MULTISHOT_ACCEPT,
23
+ OP_MULTISHOT_TIMEOUT
23
24
  };
24
25
 
25
26
  #define MULTISHOT_REFCOUNT 0xFFFF
@@ -36,6 +37,7 @@ typedef struct op_context {
36
37
  unsigned int buffer_count;
37
38
  VALUE buffer0;
38
39
  VALUE *buffers;
40
+ double ts; // timestamp for tracing
39
41
  } op_context_t;
40
42
 
41
43
  typedef struct op_context_store {
@@ -1325,7 +1325,7 @@ VALUE Backend_waitpid(VALUE self, VALUE pid) {
1325
1325
  int e = errno;
1326
1326
  rb_syserr_fail(e, strerror(e));
1327
1327
  }
1328
- return rb_ary_new_from_args(2, INT2FIX(ret), INT2FIX(WEXITSTATUS(status)));
1328
+ return rb_ary_new_from_args(2, INT2FIX(ret), INT2FIX(status));
1329
1329
  }
1330
1330
  #else
1331
1331
  struct libev_child {
@@ -1336,10 +1336,7 @@ struct libev_child {
1336
1336
  #ifndef POLYPHONY_WINDOWS
1337
1337
  void Backend_child_callback(EV_P_ ev_child *w, int revents) {
1338
1338
  struct libev_child *watcher = (struct libev_child *)w;
1339
- int exit_status = WEXITSTATUS(w->rstatus);
1340
- VALUE status;
1341
-
1342
- status = rb_ary_new_from_args(2, INT2FIX(w->rpid), INT2FIX(exit_status));
1339
+ VALUE status = rb_ary_new_from_args(2, INT2FIX(w->rpid), INT2FIX(w->rstatus));
1343
1340
  Fiber_make_runnable(watcher->fiber, status);
1344
1341
  }
1345
1342
  #endif
@@ -1365,9 +1362,9 @@ VALUE Backend_waitpid(VALUE self, VALUE pid) {
1365
1362
  RB_GC_GUARD(watcher.fiber);
1366
1363
  RB_GC_GUARD(switchpoint_result);
1367
1364
  return switchpoint_result;
1368
- #endif
1365
+ #endif // #ifdef POLYPHONY_WINDOWS
1369
1366
  }
1370
- #endif
1367
+ #endif // #ifdef POLYPHONY_USE_PIDFD_OPEN
1371
1368
 
1372
1369
  void Backend_async_callback(EV_P_ ev_async *w, int revents) { }
1373
1370
 
@@ -3,6 +3,8 @@
3
3
 
4
4
  typedef struct event {
5
5
  VALUE waiting_fiber;
6
+ int signaled;
7
+ VALUE result;
6
8
  } Event_t;
7
9
 
8
10
  VALUE cEvent = Qnil;
@@ -10,6 +12,7 @@ VALUE cEvent = Qnil;
10
12
  static void Event_mark(void *ptr) {
11
13
  Event_t *event = ptr;
12
14
  rb_gc_mark(event->waiting_fiber);
15
+ rb_gc_mark(event->result);
13
16
  }
14
17
 
15
18
  static void Event_free(void *ptr) {
@@ -41,6 +44,8 @@ static VALUE Event_initialize(VALUE self) {
41
44
  GetEvent(self, event);
42
45
 
43
46
  event->waiting_fiber = Qnil;
47
+ event->signaled = 0;
48
+ event->result = Qnil;
44
49
 
45
50
  return self;
46
51
  }
@@ -50,10 +55,17 @@ VALUE Event_signal(int argc, VALUE *argv, VALUE self) {
50
55
  Event_t *event;
51
56
  GetEvent(self, event);
52
57
 
58
+ if (event->signaled) goto done;
59
+
60
+ event->signaled = 1;
61
+ event->result = value;
62
+
53
63
  if (event->waiting_fiber != Qnil) {
54
64
  Fiber_make_runnable(event->waiting_fiber, value);
55
65
  event->waiting_fiber = Qnil;
56
66
  }
67
+
68
+ done:
57
69
  return self;
58
70
  }
59
71
 
@@ -67,10 +79,19 @@ VALUE Event_await(VALUE self) {
67
79
  if (event->waiting_fiber != Qnil)
68
80
  rb_raise(rb_eRuntimeError, "Event is already awaited by another fiber");
69
81
 
82
+ if (event->signaled) {
83
+ VALUE result = event->result;
84
+ event->signaled = 0;
85
+ event->result = Qnil;
86
+ return result;
87
+ }
88
+
70
89
  backend = rb_ivar_get(rb_thread_current(), ID_ivar_backend);
71
90
  event->waiting_fiber = rb_fiber_current();
72
91
  switchpoint_result = Backend_wait_event(backend, Qnil);
73
92
  event->waiting_fiber = Qnil;
93
+ event->signaled = 0;
94
+ event->result = Qnil;
74
95
 
75
96
  RAISE_IF_EXCEPTION(switchpoint_result);
76
97
  RB_GC_GUARD(backend);
@@ -22,9 +22,10 @@ def get_config
22
22
 
23
23
  config[:kernel_version] = combined_version
24
24
  config[:pidfd_open] = combined_version > 503
25
+ config[:multishot_accept] = combined_version >= 519
25
26
  config[:multishot_recv] = combined_version >= 600
26
27
  config[:multishot_recvmsg] = combined_version >= 600
27
- config[:multishot_accept] = combined_version >= 519
28
+ config[:multishot_timeout] = combined_version >= 640
28
29
  config[:submit_all_flag] = combined_version >= 518
29
30
  config[:coop_taskrun_flag] = combined_version >= 519
30
31
 
@@ -62,21 +63,22 @@ end
62
63
 
63
64
  $defs << '-DPOLYPHONY_USE_PIDFD_OPEN' if config[:pidfd_open]
64
65
  if config[:io_uring]
65
- $defs << "-DPOLYPHONY_BACKEND_LIBURING"
66
- $defs << "-DPOLYPHONY_LINUX"
67
- $defs << "-DPOLYPHONY_UNSET_NONBLOCK" if RUBY_VERSION =~ /^3/
68
- $defs << "-DHAVE_IO_URING_PREP_MULTISHOT_ACCEPT" if config[:multishot_accept]
69
- $defs << "-DHAVE_IO_URING_PREP_RECV_MULTISHOT" if config[:multishot_recv]
70
- $defs << "-DHAVE_IO_URING_PREP_RECVMSG_MULTISHOT" if config[:multishot_recvmsg]
71
- $defs << "-DHAVE_IORING_SETUP_SUBMIT_ALL" if config[:submit_all_flag]
72
- $defs << "-DHAVE_IORING_SETUP_COOP_TASKRUN" if config[:coop_taskrun_flag]
73
- $CFLAGS << " -Wno-pointer-arith"
66
+ $defs << '-DPOLYPHONY_BACKEND_LIBURING'
67
+ $defs << '-DPOLYPHONY_LINUX'
68
+ $defs << '-DPOLYPHONY_UNSET_NONBLOCK' if RUBY_VERSION =~ /^3/
69
+ $defs << '-DHAVE_IO_URING_PREP_MULTISHOT_ACCEPT' if config[:multishot_accept]
70
+ $defs << '-DHAVE_IO_URING_PREP_RECV_MULTISHOT' if config[:multishot_recv]
71
+ $defs << '-DHAVE_IO_URING_PREP_RECVMSG_MULTISHOT' if config[:multishot_recvmsg]
72
+ $defs << '-DHAVE_IO_URING_TIMEOUT_MULTISHOT' if config[:multishot_timeout]
73
+ $defs << '-DHAVE_IORING_SETUP_SUBMIT_ALL' if config[:submit_all_flag]
74
+ $defs << '-DHAVE_IORING_SETUP_COOP_TASKRUN' if config[:coop_taskrun_flag]
75
+ $CFLAGS << ' -Wno-pointer-arith'
74
76
  else
75
- $defs << "-DPOLYPHONY_BACKEND_LIBEV"
76
- $defs << "-DPOLYPHONY_LINUX" if config[:linux]
77
- $defs << "-DPOLYPHONY_WINDOWS" if config[:windows]
77
+ $defs << '-DPOLYPHONY_BACKEND_LIBEV'
78
+ $defs << '-DPOLYPHONY_LINUX' if config[:linux]
79
+ $defs << '-DPOLYPHONY_WINDOWS' if config[:windows]
78
80
 
79
- $defs << "-DEV_STANDALONE" # prevent libev from assuming "config.h" exists
81
+ $defs << '-DEV_STANDALONE' # prevent libev from assuming "config.h" exists
80
82
 
81
83
  define_bool('EV_USE_EPOLL', have_header('sys/epoll.h'))
82
84
  define_bool('EV_USE_KQUEUE', have_header('sys/event.h') && have_header('sys/queue.h'))
@@ -88,10 +90,10 @@ else
88
90
 
89
91
  $defs << '-DHAVE_SYS_RESOURCE_H' if have_header('sys/resource.h')
90
92
 
91
- $CFLAGS << " -Wno-comment"
92
- $CFLAGS << " -Wno-unused-result"
93
- $CFLAGS << " -Wno-dangling-else"
94
- $CFLAGS << " -Wno-parentheses"
93
+ $CFLAGS << ' -Wno-comment'
94
+ $CFLAGS << ' -Wno-unused-result'
95
+ $CFLAGS << ' -Wno-dangling-else'
96
+ $CFLAGS << ' -Wno-parentheses'
95
97
  end
96
98
 
97
99
  $defs << '-DPOLYPHONY_PLAYGROUND' if ENV['POLYPHONY_PLAYGROUND']
@@ -3,7 +3,6 @@
3
3
 
4
4
  ID ID_ivar_auto_watcher;
5
5
  ID ID_ivar_mailbox;
6
- ID ID_ivar_result;
7
6
  ID ID_ivar_waiting_fibers;
8
7
 
9
8
  VALUE SYM_dead;
@@ -239,7 +238,6 @@ void Init_Fiber(void) {
239
238
 
240
239
  ID_ivar_auto_watcher = rb_intern("@auto_watcher");
241
240
  ID_ivar_mailbox = rb_intern("@mailbox");
242
- ID_ivar_result = rb_intern("@result");
243
241
  ID_ivar_waiting_fibers = rb_intern("@waiting_fibers");
244
242
 
245
243
  SYM_spin = ID2SYM(rb_intern("spin"));
@@ -13,6 +13,7 @@ ID ID_ivar_blocking_mode;
13
13
  ID ID_ivar_io;
14
14
  ID ID_ivar_multishot_accept_queue;
15
15
  ID ID_ivar_parked;
16
+ ID ID_ivar_result;
16
17
  ID ID_ivar_runnable;
17
18
  ID ID_ivar_running;
18
19
  ID ID_ivar_thread;
@@ -474,6 +475,7 @@ void Init_Polyphony(void) {
474
475
  ID_ivar_io = rb_intern("@io");
475
476
  ID_ivar_multishot_accept_queue = rb_intern("@multishot_accept_queue");
476
477
  ID_ivar_parked = rb_intern("@parked");
478
+ ID_ivar_result = rb_intern("@result");
477
479
  ID_ivar_runnable = rb_intern("@runnable");
478
480
  ID_ivar_running = rb_intern("@running");
479
481
  ID_ivar_thread = rb_intern("@thread");
@@ -10,6 +10,7 @@
10
10
  #define INSPECT(str, obj) { printf(str); VALUE s = rb_funcall(obj, rb_intern("inspect"), 0); printf(": %s\n", StringValueCStr(s)); }
11
11
  #define CALLER() rb_funcall(rb_mKernel, rb_intern("caller"), 0)
12
12
  #define TRACE_CALLER() INSPECT("caller: ", CALLER())
13
+ #define TRACE_FREE(ptr) //printf("Free %p %s:%d\n", ptr, __FILE__, __LINE__)
13
14
 
14
15
  // exceptions
15
16
  #define TEST_EXCEPTION(ret) (rb_obj_is_kind_of(ret, rb_eException) == Qtrue)
@@ -47,6 +48,7 @@ extern ID ID_ivar_blocking_mode;
47
48
  extern ID ID_ivar_io;
48
49
  extern ID ID_ivar_multishot_accept_queue;
49
50
  extern ID ID_ivar_parked;
51
+ extern ID ID_ivar_result;
50
52
  extern ID ID_ivar_runnable;
51
53
  extern ID ID_ivar_running;
52
54
  extern ID ID_ivar_thread;
@@ -147,6 +149,9 @@ void Thread_schedule_fiber(VALUE thread, VALUE fiber, VALUE value);
147
149
  void Thread_schedule_fiber_with_priority(VALUE thread, VALUE fiber, VALUE value);
148
150
  VALUE Thread_switch_fiber(VALUE thread);
149
151
 
152
+ VALUE Event_signal(int argc, VALUE *argv, VALUE event);
153
+ VALUE Event_await(VALUE event);
154
+
150
155
  VALUE Polyphony_snooze(VALUE self);
151
156
 
152
157
  #endif /* POLYPHONY_H */
@@ -10,6 +10,7 @@ void ring_buffer_init(ring_buffer *buffer) {
10
10
  }
11
11
 
12
12
  void ring_buffer_free(ring_buffer *buffer) {
13
+ TRACE_FREE(buffer->entries);
13
14
  free(buffer->entries);
14
15
  }
15
16
 
@@ -10,6 +10,7 @@ inline void runqueue_ring_buffer_init(runqueue_ring_buffer *buffer) {
10
10
  }
11
11
 
12
12
  inline void runqueue_ring_buffer_free(runqueue_ring_buffer *buffer) {
13
+ TRACE_FREE(buffer->entries);
13
14
  free(buffer->entries);
14
15
  }
15
16
 
@@ -3,9 +3,12 @@
3
3
 
4
4
  ID ID_deactivate_all_watchers_post_fork;
5
5
  ID ID_ivar_backend;
6
+ ID ID_ivar_done;
6
7
  ID ID_ivar_join_wait_queue;
7
8
  ID ID_ivar_main_fiber;
9
+ ID ID_ivar_ready;
8
10
  ID ID_ivar_terminated;
11
+ ID ID_ivar_waiters;
9
12
  ID ID_stop;
10
13
 
11
14
  /* :nop-doc: */
@@ -83,20 +86,80 @@ VALUE Thread_class_backend(VALUE _self) {
83
86
  return rb_ivar_get(rb_thread_current(), ID_ivar_backend);
84
87
  }
85
88
 
89
+
90
+ VALUE Thread_done_p(VALUE self)
91
+ {
92
+ return rb_ivar_get(self, ID_ivar_done);
93
+ }
94
+
95
+ VALUE Thread_kill_safe(VALUE self)
96
+ {
97
+ static VALUE eTerminate = Qnil;
98
+ if (rb_ivar_get(self, ID_ivar_done) == Qtrue) return self;
99
+
100
+ if (eTerminate == Qnil)
101
+ eTerminate = rb_const_get(mPolyphony, rb_intern("Terminate"));
102
+
103
+ while (rb_ivar_get(self, ID_ivar_ready) != Qtrue)
104
+ rb_thread_schedule();
105
+
106
+ VALUE main_fiber = rb_ivar_get(self, ID_ivar_main_fiber);
107
+ VALUE exception = rb_funcall(eTerminate, ID_new, 0);
108
+ Thread_schedule_fiber(self, main_fiber, exception);
109
+ return self;
110
+ }
111
+
112
+ VALUE Thread_mark_as_done(VALUE self, VALUE result)
113
+ {
114
+ rb_ivar_set(self, ID_ivar_done, Qtrue);
115
+ VALUE waiters = rb_ivar_get(self, ID_ivar_waiters);
116
+ if (waiters == Qnil) return self;
117
+
118
+ int len = RARRAY_LEN(waiters);
119
+ for (int i = 0; i < len; i++) {
120
+ VALUE waiter = RARRAY_AREF(waiters, i);
121
+ Event_signal(1, &result, waiter);
122
+ }
123
+ return self;
124
+ }
125
+
126
+ VALUE Thread_await_done(VALUE self)
127
+ {
128
+ if (Thread_done_p(self) == Qtrue) return rb_ivar_get(self, ID_ivar_result);
129
+
130
+ VALUE waiter = Fiber_auto_watcher(rb_fiber_current());
131
+ VALUE waiters = rb_ivar_get(self, ID_ivar_waiters);
132
+ if (waiters == Qnil) {
133
+ waiters = rb_ary_new();
134
+ rb_ivar_set(self, ID_ivar_waiters, waiters);
135
+ }
136
+ rb_ary_push(waiters, waiter);
137
+
138
+ return Event_await(waiter);
139
+ }
140
+
86
141
  void Init_Thread(void) {
87
142
  rb_define_method(rb_cThread, "setup_fiber_scheduling", Thread_setup_fiber_scheduling, 0);
88
143
  rb_define_method(rb_cThread, "schedule_and_wakeup", Thread_fiber_schedule_and_wakeup, 2);
89
144
  rb_define_method(rb_cThread, "switch_fiber", Thread_switch_fiber, 0);
90
145
  rb_define_method(rb_cThread, "fiber_unschedule", Thread_fiber_unschedule, 1);
91
146
 
147
+ rb_define_method(rb_cThread, "done?", Thread_done_p, 0);
148
+ rb_define_method(rb_cThread, "kill_safe", Thread_kill_safe, 0);
149
+ rb_define_method(rb_cThread, "mark_as_done", Thread_mark_as_done, 1);
150
+ rb_define_method(rb_cThread, "await_done", Thread_await_done, 0);
151
+
92
152
  rb_define_singleton_method(rb_cThread, "backend", Thread_class_backend, 0);
93
153
 
94
154
  rb_define_method(rb_cThread, "debug!", Thread_debug, 0);
95
155
 
96
156
  ID_deactivate_all_watchers_post_fork = rb_intern("deactivate_all_watchers_post_fork");
97
157
  ID_ivar_backend = rb_intern("@backend");
158
+ ID_ivar_done = rb_intern("@done");
98
159
  ID_ivar_join_wait_queue = rb_intern("@join_wait_queue");
99
160
  ID_ivar_main_fiber = rb_intern("@main_fiber");
161
+ ID_ivar_ready = rb_intern("@ready");
100
162
  ID_ivar_terminated = rb_intern("@terminated");
163
+ ID_ivar_waiters = rb_intern("@waiters");
101
164
  ID_stop = rb_intern("stop");
102
165
  }