polyphony 1.5 → 1.6

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