polyphony 0.68 → 0.72

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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -0
  3. data/CHANGELOG.md +32 -5
  4. data/Gemfile.lock +2 -2
  5. data/TODO.md +1 -24
  6. data/bin/pdbg +1 -1
  7. data/bin/polyphony-debug +0 -0
  8. data/bin/stress.rb +0 -0
  9. data/bin/test +0 -0
  10. data/docs/_user-guide/all-about-timers.md +1 -1
  11. data/docs/api-reference/fiber.md +2 -2
  12. data/docs/faq.md +1 -1
  13. data/docs/getting-started/overview.md +8 -8
  14. data/docs/getting-started/tutorial.md +3 -3
  15. data/docs/main-concepts/concurrency.md +1 -1
  16. data/docs/main-concepts/extending.md +3 -3
  17. data/docs/main-concepts/fiber-scheduling.md +1 -1
  18. data/examples/core/calc.rb +37 -0
  19. data/examples/core/calc_with_restart.rb +40 -0
  20. data/examples/core/calc_with_supervise.rb +37 -0
  21. data/examples/core/message_based_supervision.rb +1 -1
  22. data/examples/io/rack_server.rb +1 -1
  23. data/examples/io/tunnel.rb +1 -1
  24. data/examples/performance/fiber_transfer.rb +1 -1
  25. data/examples/performance/line_splitting.rb +1 -1
  26. data/examples/performance/thread-vs-fiber/compare.rb +1 -1
  27. data/ext/polyphony/backend_common.c +24 -6
  28. data/ext/polyphony/backend_common.h +1 -0
  29. data/ext/polyphony/backend_io_uring.c +27 -40
  30. data/ext/polyphony/backend_io_uring_context.c +1 -1
  31. data/ext/polyphony/backend_io_uring_context.h +1 -1
  32. data/ext/polyphony/backend_libev.c +13 -11
  33. data/ext/polyphony/extconf.rb +24 -13
  34. data/ext/polyphony/queue.c +2 -2
  35. data/ext/polyphony/runqueue_ring_buffer.c +3 -2
  36. data/lib/polyphony/adapters/irb.rb +11 -1
  37. data/lib/polyphony/core/global_api.rb +3 -3
  38. data/lib/polyphony/core/timer.rb +2 -2
  39. data/lib/polyphony/debugger.rb +3 -3
  40. data/lib/polyphony/extensions/fiber.rb +30 -15
  41. data/lib/polyphony/extensions/io.rb +3 -3
  42. data/lib/polyphony/extensions/openssl.rb +21 -6
  43. data/lib/polyphony/extensions/socket.rb +4 -5
  44. data/lib/polyphony/net.rb +0 -1
  45. data/lib/polyphony/version.rb +1 -1
  46. data/polyphony.gemspec +1 -1
  47. data/test/coverage.rb +2 -2
  48. data/test/stress.rb +1 -1
  49. data/test/test_backend.rb +12 -12
  50. data/test/test_event.rb +1 -1
  51. data/test/test_ext.rb +1 -1
  52. data/test/test_fiber.rb +52 -12
  53. data/test/test_global_api.rb +2 -3
  54. data/test/test_io.rb +3 -3
  55. data/test/test_process_supervision.rb +38 -9
  56. data/test/test_queue.rb +6 -6
  57. data/test/test_socket.rb +12 -10
  58. data/test/test_supervise.rb +249 -81
  59. data/test/test_sync.rb +2 -2
  60. data/test/test_thread.rb +22 -2
  61. data/test/test_thread_pool.rb +1 -1
  62. data/test/test_throttler.rb +1 -1
  63. data/test/test_timer.rb +2 -2
  64. data/test/test_trace.rb +1 -1
  65. metadata +7 -3
@@ -106,9 +106,7 @@ VALUE Backend_post_fork(VALUE self) {
106
106
  io_uring_queue_exit(&backend->ring);
107
107
  io_uring_queue_init(backend->prepared_limit, &backend->ring, 0);
108
108
  context_store_free(&backend->store);
109
- backend->base.currently_polling = 0;
110
- backend->base.pending_count = 0;
111
- backend->pending_sqes = 0;
109
+ backend_base_reset(&backend->base);
112
110
 
113
111
  return self;
114
112
  }
@@ -143,7 +141,7 @@ static inline void io_uring_backend_handle_completion(struct io_uring_cqe *cqe,
143
141
  context_store_release(&backend->store, ctx);
144
142
  }
145
143
 
146
- // adapted from io_uring_peek_batch_cqe in queue.c
144
+ // adapted from io_uring_peek_batch_cqe in queue.c
147
145
  // this peeks at cqes and handles each available cqe
148
146
  void io_uring_backend_handle_ready_cqes(Backend_t *backend) {
149
147
  struct io_uring *ring = &backend->ring;
@@ -202,19 +200,12 @@ inline VALUE Backend_poll(VALUE self, VALUE blocking) {
202
200
  }
203
201
 
204
202
  COND_TRACE(&backend->base, 2, SYM_fiber_event_poll_enter, rb_fiber_current());
205
- // if (SHOULD_TRACE(&backend->base))
206
- // printf(
207
- // "io_uring_poll(blocking_mode: %d, pending: %d, taken: %d, available: %d, runqueue: %d\n",
208
- // is_blocking,
209
- // backend->base.pending_count,
210
- // backend->store.taken_count,
211
- // backend->store.available_count,
212
- // backend->base.runqueue.entries.count
213
- // );
203
+
214
204
  if (is_blocking) io_uring_backend_poll(backend);
215
205
  io_uring_backend_handle_ready_cqes(backend);
216
- COND_TRACE(&backend->base, 2, SYM_fiber_event_poll_leave, rb_fiber_current());
217
206
 
207
+ COND_TRACE(&backend->base, 2, SYM_fiber_event_poll_leave, rb_fiber_current());
208
+
218
209
  return self;
219
210
  }
220
211
 
@@ -257,7 +248,7 @@ VALUE Backend_wakeup(VALUE self) {
257
248
  io_uring_prep_nop(sqe);
258
249
  backend->pending_sqes = 0;
259
250
  io_uring_submit(&backend->ring);
260
-
251
+
261
252
  return Qtrue;
262
253
  }
263
254
 
@@ -413,7 +404,7 @@ VALUE Backend_read_loop(VALUE self, VALUE io, VALUE maxlen) {
413
404
  op_context_t *ctx = context_store_acquire(&backend->store, OP_READ);
414
405
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
415
406
  io_uring_prep_read(sqe, fptr->fd, buf, len, -1);
416
-
407
+
417
408
  ssize_t result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
418
409
  int completed = context_store_release(&backend->store, ctx);
419
410
  if (!completed) {
@@ -463,7 +454,7 @@ VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
463
454
  op_context_t *ctx = context_store_acquire(&backend->store, OP_READ);
464
455
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
465
456
  io_uring_prep_read(sqe, fptr->fd, buf, len, -1);
466
-
457
+
467
458
  ssize_t result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
468
459
  int completed = context_store_release(&backend->store, ctx);
469
460
  if (!completed) {
@@ -509,7 +500,7 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
509
500
  op_context_t *ctx = context_store_acquire(&backend->store, OP_WRITE);
510
501
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
511
502
  io_uring_prep_write(sqe, fptr->fd, buf, left, 0);
512
-
503
+
513
504
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
514
505
  int completed = context_store_release(&backend->store, ctx);
515
506
  if (!completed) {
@@ -518,7 +509,7 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
518
509
  return resume_value;
519
510
  }
520
511
  RB_GC_GUARD(resume_value);
521
-
512
+
522
513
  if (result < 0)
523
514
  rb_syserr_fail(-result, strerror(-result));
524
515
  else {
@@ -561,7 +552,7 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
561
552
  op_context_t *ctx = context_store_acquire(&backend->store, OP_WRITEV);
562
553
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
563
554
  io_uring_prep_writev(sqe, fptr->fd, iov_ptr, iov_count, -1);
564
-
555
+
565
556
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
566
557
  int completed = context_store_release(&backend->store, ctx);
567
558
  if (!completed) {
@@ -637,7 +628,7 @@ VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length, VALUE pos) {
637
628
  op_context_t *ctx = context_store_acquire(&backend->store, OP_RECV);
638
629
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
639
630
  io_uring_prep_recv(sqe, fptr->fd, buf, len - total, 0);
640
-
631
+
641
632
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
642
633
  int completed = context_store_release(&backend->store, ctx);
643
634
  if (!completed) {
@@ -687,7 +678,7 @@ VALUE Backend_recv_loop(VALUE self, VALUE io, VALUE maxlen) {
687
678
  op_context_t *ctx = context_store_acquire(&backend->store, OP_RECV);
688
679
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
689
680
  io_uring_prep_recv(sqe, fptr->fd, buf, len, 0);
690
-
681
+
691
682
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
692
683
  int completed = context_store_release(&backend->store, ctx);
693
684
  if (!completed) {
@@ -736,7 +727,7 @@ VALUE Backend_recv_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method)
736
727
  op_context_t *ctx = context_store_acquire(&backend->store, OP_RECV);
737
728
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
738
729
  io_uring_prep_recv(sqe, fptr->fd, buf, len, 0);
739
-
730
+
740
731
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
741
732
  int completed = context_store_release(&backend->store, ctx);
742
733
  if (!completed) {
@@ -782,7 +773,7 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
782
773
  op_context_t *ctx = context_store_acquire(&backend->store, OP_SEND);
783
774
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
784
775
  io_uring_prep_send(sqe, fptr->fd, buf, left, flags_int);
785
-
776
+
786
777
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
787
778
  int completed = context_store_release(&backend->store, ctx);
788
779
  if (!completed) {
@@ -819,7 +810,7 @@ VALUE io_uring_backend_accept(Backend_t *backend, VALUE server_socket, VALUE soc
819
810
  op_context_t *ctx = context_store_acquire(&backend->store, OP_ACCEPT);
820
811
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
821
812
  io_uring_prep_accept(sqe, fptr->fd, &addr, &len, 0);
822
-
813
+
823
814
  int fd = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
824
815
  int completed = context_store_release(&backend->store, ctx);
825
816
  RAISE_IF_EXCEPTION(resume_value);
@@ -890,7 +881,7 @@ VALUE io_uring_backend_splice(Backend_t *backend, VALUE src, VALUE dest, VALUE m
890
881
  op_context_t *ctx = context_store_acquire(&backend->store, OP_SPLICE);
891
882
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
892
883
  io_uring_prep_splice(sqe, src_fptr->fd, -1, dest_fptr->fd, -1, NUM2INT(maxlen), 0);
893
-
884
+
894
885
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
895
886
  int completed = context_store_release(&backend->store, ctx);
896
887
  RAISE_IF_EXCEPTION(resume_value);
@@ -946,7 +937,7 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
946
937
  RAISE_IF_EXCEPTION(resume_value);
947
938
  if (!completed) return resume_value;
948
939
  RB_GC_GUARD(resume_value);
949
-
940
+
950
941
  if (result < 0) rb_syserr_fail(-result, strerror(-result));
951
942
  return sock;
952
943
  }
@@ -983,7 +974,7 @@ inline struct __kernel_timespec duration_to_timespec(VALUE duration) {
983
974
  int io_uring_backend_submit_timeout_and_await(Backend_t *backend, double duration, VALUE *resume_value) {
984
975
  struct __kernel_timespec ts = double_to_timespec(duration);
985
976
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
986
-
977
+
987
978
  op_context_t *ctx = context_store_acquire(&backend->store, OP_TIMEOUT);
988
979
  io_uring_prep_timeout(sqe, &ts, 0, 0);
989
980
  io_uring_backend_defer_submit_and_await(backend, sqe, ctx, resume_value);
@@ -1012,7 +1003,7 @@ VALUE Backend_timer_loop(VALUE self, VALUE interval) {
1012
1003
  if (next_time == 0.) next_time = current_time() + interval_d;
1013
1004
  double sleep_duration = next_time - now;
1014
1005
  if (sleep_duration < 0) sleep_duration = 0;
1015
-
1006
+
1016
1007
  VALUE resume_value = Qnil;
1017
1008
  int completed = io_uring_backend_submit_timeout_and_await(backend, sleep_duration, &resume_value);
1018
1009
  RAISE_IF_EXCEPTION(resume_value);
@@ -1053,7 +1044,7 @@ VALUE Backend_timeout(int argc, VALUE *argv, VALUE self) {
1053
1044
  VALUE exception;
1054
1045
  VALUE move_on_value = Qnil;
1055
1046
  rb_scan_args(argc, argv, "21", &duration, &exception, &move_on_value);
1056
-
1047
+
1057
1048
  struct __kernel_timespec ts = duration_to_timespec(duration);
1058
1049
  Backend_t *backend;
1059
1050
  GetBackend(self, backend);
@@ -1061,7 +1052,7 @@ VALUE Backend_timeout(int argc, VALUE *argv, VALUE self) {
1061
1052
  VALUE timeout = rb_funcall(cTimeoutException, ID_new, 0);
1062
1053
 
1063
1054
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
1064
-
1055
+
1065
1056
  op_context_t *ctx = context_store_acquire(&backend->store, OP_TIMEOUT);
1066
1057
  ctx->resume_value = timeout;
1067
1058
  io_uring_prep_timeout(sqe, &ts, 0, 0);
@@ -1096,7 +1087,7 @@ VALUE Backend_waitpid(VALUE self, VALUE pid) {
1096
1087
  RAISE_IF_EXCEPTION(resume_value);
1097
1088
  RB_GC_GUARD(resume_value);
1098
1089
  }
1099
-
1090
+
1100
1091
  int status;
1101
1092
  pid_t ret = waitpid(pid_int, &status, WNOHANG);
1102
1093
  if (ret < 0) {
@@ -1246,7 +1237,7 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1246
1237
  }
1247
1238
  rb_raise(rb_eRuntimeError, "Invalid op specified or bad op arity");
1248
1239
  }
1249
-
1240
+
1250
1241
  io_uring_sqe_set_data(last_sqe, ctx);
1251
1242
  unsigned int flags = (i == (argc - 1)) ? IOSQE_ASYNC : IOSQE_ASYNC | IOSQE_IO_LINK;
1252
1243
  io_uring_sqe_set_flags(last_sqe, flags);
@@ -1261,7 +1252,7 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1261
1252
  int completed = context_store_release(&backend->store, ctx);
1262
1253
  if (!completed) {
1263
1254
  Backend_chain_ctx_attach_buffers(ctx, argc, argv);
1264
-
1255
+
1265
1256
  // op was not completed (an exception was raised), so we need to cancel it
1266
1257
  ctx->result = -ECANCELED;
1267
1258
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
@@ -1271,7 +1262,7 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1271
1262
  RAISE_IF_EXCEPTION(resume_value);
1272
1263
  return resume_value;
1273
1264
  }
1274
-
1265
+
1275
1266
  RB_GC_GUARD(resume_value);
1276
1267
  return INT2NUM(result);
1277
1268
  }
@@ -1406,7 +1397,7 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
1406
1397
 
1407
1398
  SPLICE_CHUNKS_AWAIT_OPS(backend, &ctx, &chunk_len, &switchpoint_result);
1408
1399
  if (chunk_len == 0) break;
1409
-
1400
+
1410
1401
  total += chunk_len;
1411
1402
  chunk_len_value = INT2NUM(chunk_len);
1412
1403
 
@@ -1528,10 +1519,6 @@ void Init_Backend() {
1528
1519
  rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
1529
1520
  rb_define_method(cBackend, "write", Backend_write_m, -1);
1530
1521
 
1531
- #ifdef POLYPHONY_UNSET_NONBLOCK
1532
- ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
1533
- #endif
1534
-
1535
1522
  SYM_io_uring = ID2SYM(rb_intern("io_uring"));
1536
1523
  SYM_send = ID2SYM(rb_intern("send"));
1537
1524
  SYM_splice = ID2SYM(rb_intern("splice"));
@@ -64,7 +64,7 @@ inline int context_store_release(op_context_store_t *store, op_context_t *ctx) {
64
64
  // printf("release %p %d (%s, ref_count: %d)\n", ctx, ctx->id, op_type_to_str(ctx->type), ctx->ref_count);
65
65
 
66
66
  assert(ctx->ref_count);
67
-
67
+
68
68
  ctx->ref_count--;
69
69
  if (ctx->ref_count) return 0;
70
70
 
@@ -22,7 +22,7 @@ typedef struct op_context {
22
22
  struct op_context *prev;
23
23
  struct op_context *next;
24
24
  enum op_type type: 16;
25
- unsigned int ref_count : 16;
25
+ unsigned int ref_count : 16;
26
26
  int id;
27
27
  int result;
28
28
  VALUE fiber;
@@ -118,7 +118,7 @@ inline struct ev_loop *libev_new_loop() {
118
118
 
119
119
  static VALUE Backend_initialize(VALUE self) {
120
120
  Backend_t *backend;
121
-
121
+
122
122
  GetBackend(self, backend);
123
123
 
124
124
  backend_base_initialize(&backend->base);
@@ -157,6 +157,8 @@ VALUE Backend_post_fork(VALUE self) {
157
157
  ev_loop_destroy(backend->ev_loop);
158
158
  backend->ev_loop = EV_DEFAULT;
159
159
 
160
+ backend_base_reset(&backend->base);
161
+
160
162
  return self;
161
163
  }
162
164
 
@@ -186,7 +188,7 @@ inline void Backend_unschedule_fiber(VALUE self, VALUE fiber) {
186
188
  Backend_t *backend;
187
189
  GetBackend(self, backend);
188
190
 
189
- runqueue_delete(&backend->base.runqueue, fiber);
191
+ runqueue_delete(&backend->base.runqueue, fiber);
190
192
  }
191
193
 
192
194
  inline VALUE Backend_switch_fiber(VALUE self) {
@@ -969,7 +971,7 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
969
971
  io_verify_blocking_mode(dest_fptr, dest, Qfalse);
970
972
 
971
973
  watcher.fiber = Qnil;
972
-
974
+
973
975
  while (1) {
974
976
  backend->base.op_count++;
975
977
  ssize_t n = read(src_fptr->fd, buf, len);
@@ -1045,7 +1047,7 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
1045
1047
 
1046
1048
  watcher.fiber = Qnil;
1047
1049
 
1048
- while (1) {
1050
+ while (1) {
1049
1051
  char *ptr = buf;
1050
1052
  while (1) {
1051
1053
  backend->base.op_count++;
@@ -1157,8 +1159,8 @@ noreturn VALUE Backend_timer_loop(VALUE self, VALUE interval) {
1157
1159
  if (next_time == 0.) next_time = current_time() + interval_d;
1158
1160
  double sleep_duration = next_time - now;
1159
1161
  if (sleep_duration < 0) sleep_duration = 0;
1160
-
1161
- VALUE switchpoint_result = Qnil;
1162
+
1163
+ VALUE switchpoint_result = Qnil;
1162
1164
  ev_timer_init(&watcher.timer, Backend_timer_callback, sleep_duration, 0.);
1163
1165
  ev_timer_start(backend->ev_loop, &watcher.timer);
1164
1166
  backend->base.op_count++;
@@ -1217,7 +1219,7 @@ VALUE Backend_timeout(int argc,VALUE *argv, VALUE self) {
1217
1219
 
1218
1220
  struct Backend_timeout_ctx timeout_ctx = {backend, &watcher};
1219
1221
  result = rb_ensure(Backend_timeout_ensure_safe, Qnil, Backend_timeout_ensure, (VALUE)&timeout_ctx);
1220
-
1222
+
1221
1223
  if (result == timeout) {
1222
1224
  if (exception == Qnil) return move_on_value;
1223
1225
  RAISE_EXCEPTION(backend_timeout_exception(exception));
@@ -1335,7 +1337,7 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1335
1337
  else
1336
1338
  rb_raise(rb_eRuntimeError, "Invalid op specified or bad op arity");
1337
1339
  }
1338
-
1340
+
1339
1341
  RB_GC_GUARD(result);
1340
1342
  return result;
1341
1343
  }
@@ -1384,14 +1386,14 @@ inline int splice_chunks_write(Backend_t *backend, int fd, VALUE str, struct lib
1384
1386
  return 0;
1385
1387
  }
1386
1388
 
1387
- static inline int splice_chunks_splice(Backend_t *backend, int src_fd, int dest_fd, int maxlen,
1389
+ static inline int splice_chunks_splice(Backend_t *backend, int src_fd, int dest_fd, int maxlen,
1388
1390
  struct libev_rw_io *watcher, VALUE *result, int *chunk_len) {
1389
1391
  #ifdef POLYPHONY_LINUX
1390
1392
  backend->base.op_count++;
1391
1393
  while (1) {
1392
1394
  *chunk_len = splice(src_fd, 0, dest_fd, 0, maxlen, 0);
1393
1395
  if (*chunk_len >= 0) return 0;
1394
-
1396
+
1395
1397
  int err = errno;
1396
1398
  if (err != EWOULDBLOCK && err != EAGAIN) return err;
1397
1399
 
@@ -1487,7 +1489,7 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
1487
1489
  err = splice_chunks_splice(backend, src_fptr->fd, pipefd[1], maxlen, &watcher, &result, &chunk_len);
1488
1490
  if (err == -1) goto error; else if (err) goto syscallerror;
1489
1491
  if (chunk_len == 0) break;
1490
-
1492
+
1491
1493
  total += chunk_len;
1492
1494
  chunk_len_value = INT2NUM(chunk_len);
1493
1495
 
@@ -3,32 +3,43 @@
3
3
  require 'rubygems'
4
4
  require 'mkmf'
5
5
 
6
- use_liburing = false
7
- use_pidfd_open = false
8
- force_use_libev = ENV['POLYPHONY_USE_LIBEV'] != nil
9
- linux = RUBY_PLATFORM =~ /linux/
10
-
11
- if linux && `uname -sr` =~ /Linux 5\.(\d+)/
12
- kernel_minor_version = $1.to_i
13
- use_liburing = !force_use_libev && kernel_minor_version >= 6
14
- use_pidfd_open = kernel_minor_version >= 3
6
+
7
+ KERNEL_INFO_RE = /Linux (\d)\.(\d+)\.(?:\d+)\-(?:\d+\-)?(\w+)/
8
+ def get_config
9
+ config = { linux: !!(RUBY_PLATFORM =~ /linux/) }
10
+ return config if !config[:linux]
11
+
12
+ kernel_info = `uname -sr`
13
+ m = kernel_info.match(KERNEL_INFO_RE)
14
+ raise "Could not parse Linux kernel information (#{kernel_info.inspect})" if !m
15
+
16
+ version, major_revision, distribution = m[1].to_i, m[2].to_i, m[3]
17
+ config[:pidfd_open] = (version == 5) && (major_revision >= 3)
18
+
19
+ force_libev = ENV['POLYPHONY_USE_LIBEV'] != nil
20
+ config[:io_uring] = !force_libev &&
21
+ (version == 5) && (major_revision >= 6) && (distribution != 'linuxkit')
22
+ config
15
23
  end
16
24
 
17
- $defs << '-DPOLYPHONY_USE_PIDFD_OPEN' if use_pidfd_open
18
- if use_liburing
25
+ config = get_config
26
+ puts "Building Polyphony... (#{config.inspect})"
27
+
28
+ $defs << '-DPOLYPHONY_USE_PIDFD_OPEN' if config[:pidfd_open]
29
+ if config[:io_uring]
19
30
  $defs << "-DPOLYPHONY_BACKEND_LIBURING"
20
31
  $defs << "-DPOLYPHONY_UNSET_NONBLOCK" if RUBY_VERSION =~ /^3/
21
32
  $CFLAGS << " -Wno-pointer-arith"
22
33
  else
23
34
  $defs << "-DPOLYPHONY_BACKEND_LIBEV"
24
- $defs << "-DPOLYPHONY_LINUX" if linux
35
+ $defs << "-DPOLYPHONY_LINUX" if config[:linux]
25
36
  $defs << '-DEV_USE_LINUXAIO' if have_header('linux/aio_abi.h')
26
37
  $defs << '-DEV_USE_SELECT' if have_header('sys/select.h')
27
38
  $defs << '-DEV_USE_POLL' if have_type('port_event_t', 'poll.h')
28
39
  $defs << '-DEV_USE_EPOLL' if have_header('sys/epoll.h')
29
40
  $defs << '-DEV_USE_KQUEUE' if have_header('sys/event.h') && have_header('sys/queue.h')
30
41
  $defs << '-DEV_USE_PORT' if have_type('port_event_t', 'port.h')
31
- $defs << '-DHAVE_SYS_RESOURCE_H' if have_header('sys/resource.h')
42
+ $defs << '-DHAVE_SYS_RESOURCE_H' if have_header('sys/resource.h')
32
43
 
33
44
  $CFLAGS << " -Wno-comment"
34
45
  $CFLAGS << " -Wno-unused-result"
@@ -162,12 +162,12 @@ VALUE Queue_cap(VALUE self, VALUE cap) {
162
162
  Queue_t *queue;
163
163
  GetQueue(self, queue);
164
164
  queue->capacity = new_capacity;
165
-
165
+
166
166
  if (queue->capacity)
167
167
  queue_schedule_blocked_fibers_to_capacity(queue);
168
168
  else
169
169
  queue_schedule_all_blocked_fibers(&queue->push_queue);
170
-
170
+
171
171
  return self;
172
172
  }
173
173
 
@@ -61,8 +61,9 @@ inline void runqueue_ring_buffer_push(runqueue_ring_buffer *buffer, VALUE fiber,
61
61
 
62
62
  inline void runqueue_ring_buffer_mark(runqueue_ring_buffer *buffer) {
63
63
  for (unsigned int i = 0; i < buffer->count; i++) {
64
- rb_gc_mark(buffer->entries[(buffer->head + i) % buffer->size].fiber);
65
- rb_gc_mark(buffer->entries[(buffer->head + i) % buffer->size].value);
64
+ runqueue_entry entry = buffer->entries[(buffer->head + i) % buffer->size];
65
+ rb_gc_mark(entry.fiber);
66
+ rb_gc_mark(entry.value);
66
67
  }
67
68
  }
68
69
 
@@ -3,26 +3,36 @@
3
3
  require 'polyphony'
4
4
 
5
5
  if Object.constants.include?(:Reline)
6
+ puts "reline"
6
7
  class Reline::ANSI
7
8
  def self.select(read_ios = [], write_ios = [], error_ios = [], timeout = nil)
8
- p [:select, read_ios]
9
+ # p [:select, read_ios, timeout]
10
+ # puts caller.join("\n")
9
11
  raise if read_ios.size > 1
10
12
  raise if write_ios.size > 0
11
13
  raise if error_ios.size > 0
12
14
 
15
+ # p 1
13
16
  fiber = Fiber.current
14
17
  timer = spin do
15
18
  sleep timeout
16
19
  fiber.cancel
17
20
  end
21
+ # p 2
18
22
  read_ios.each do |io|
23
+ # p wait: io
19
24
  Polyphony.backend_wait_io(io, false)
25
+ # p :done_wait
20
26
  return [io]
21
27
  end
28
+ # p 3
22
29
  rescue Polyphony::Cancel
30
+ # p :cancel
23
31
  return nil
24
32
  ensure
33
+ # p :ensure
25
34
  timer.stop
35
+ # p :ensure_done
26
36
  end
27
37
  end
28
38
  else
@@ -73,7 +73,7 @@ module Polyphony
73
73
 
74
74
  def spin_scope
75
75
  raise unless block_given?
76
-
76
+
77
77
  spin do
78
78
  result = yield
79
79
  Fiber.current.await_all_children
@@ -122,8 +122,8 @@ module Polyphony
122
122
  Fiber.current.receive_all_pending
123
123
  end
124
124
 
125
- def supervise(*args, &block)
126
- Fiber.current.supervise(*args, &block)
125
+ def supervise(*args, **opts, &block)
126
+ Fiber.current.supervise(*args, **opts, &block)
127
127
  end
128
128
 
129
129
  def sleep(duration = nil)
@@ -44,7 +44,7 @@ module Polyphony
44
44
  ensure
45
45
  @timeouts.delete(fiber)
46
46
  end
47
-
47
+
48
48
  def cancel_after(interval, with_exception: Polyphony::Cancel)
49
49
  fiber = Fiber.current
50
50
  @timeouts[fiber] = {
@@ -74,7 +74,7 @@ module Polyphony
74
74
  def reset
75
75
  record = @timeouts[Fiber.current]
76
76
  return unless record
77
-
77
+
78
78
  record[:target_stamp] = now + record[:interval]
79
79
  end
80
80
 
@@ -9,11 +9,11 @@ module Polyphony
9
9
  :return,
10
10
  :b_call,
11
11
  :b_return
12
- ]
12
+ ]
13
13
 
14
14
  def self.start_debug_server(socket_path)
15
15
  server = DebugServer.new(socket_path)
16
- controller = DebugController.new(server)
16
+ controller = DebugController.new(server)
17
17
  trace = TracePoint.new(*TP_EVENTS) { |tp| controller.handle_tp(trace, tp) }
18
18
  trace.enable
19
19
 
@@ -124,7 +124,7 @@ module Polyphony
124
124
  h
125
125
  end
126
126
  end
127
-
127
+
128
128
  def cmd_step(cmd)
129
129
  tp = nil
130
130
  fiber = nil
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'fiber'
4
-
5
4
  require_relative '../core/exceptions'
6
5
 
7
6
  module Polyphony
@@ -19,7 +18,7 @@ module Polyphony
19
18
  alias_method :stop, :interrupt
20
19
 
21
20
  def restart(value = nil)
22
- raise "Can''t restart main fiber" if @main
21
+ raise "Can't restart main fiber" if @main
23
22
 
24
23
  if @running
25
24
  schedule Polyphony::Restart.new(value)
@@ -27,6 +26,7 @@ module Polyphony
27
26
  end
28
27
 
29
28
  fiber = parent.spin(@tag, @caller, &@block)
29
+ @monitors&.each_key { |f| fiber.monitor(f) }
30
30
  fiber.schedule(value) unless value.nil?
31
31
  fiber
32
32
  end
@@ -80,10 +80,10 @@ module Polyphony
80
80
  # Fiber supervision
81
81
  module FiberSupervision
82
82
  def supervise(*fibers, **opts, &block)
83
- block ||= opts[:on_done] ||
84
- (opts[:on_error] && supervise_on_error_proc(opts[:on_error]))
85
- raise "No block given" unless block
83
+ block ||= supervise_opts_to_block(opts)
86
84
 
85
+ @supervise_mode = true
86
+ fibers = children if fibers.empty?
87
87
  fibers.each do |f|
88
88
  f.attach_to(self) unless f.parent == self
89
89
  f.monitor(self)
@@ -95,10 +95,24 @@ module Polyphony
95
95
  (fiber, result) = mailbox.shift
96
96
  block&.call(fiber, result)
97
97
  end
98
+ ensure
99
+ @supervise_mode = false
98
100
  end
99
101
 
100
- def supervise_on_error_proc(on_error)
101
- ->(f, r) { opts[:on_error].(f, r) if r.is_a?(Exception) }
102
+ def supervise_opts_to_block(opts)
103
+ block = opts[:on_done] || opts[:on_error]
104
+ restart = opts[:restart]
105
+ return nil unless block || restart
106
+
107
+ error_only = !!opts[:on_error]
108
+ restart_always = (restart == :always) || (restart == true)
109
+ restart_on_error = restart == :on_error
110
+
111
+ ->(f, r) do
112
+ is_error = r.is_a?(Exception)
113
+ block.(f, r) if block && (!error_only || is_error)
114
+ f.restart if restart_always || (restart_on_error && is_error)
115
+ end
102
116
  end
103
117
  end
104
118
 
@@ -139,7 +153,7 @@ module Polyphony
139
153
 
140
154
  def select(*fibers)
141
155
  return nil if fibers.empty?
142
-
156
+
143
157
  current_fiber = self.current
144
158
  mailbox = current_fiber.monitor_mailbox
145
159
  fibers.each do |f|
@@ -153,7 +167,7 @@ module Polyphony
153
167
  while true
154
168
  (fiber, result) = mailbox.shift
155
169
  next unless fibers.include?(fiber)
156
-
170
+
157
171
  fibers.each { |f| f.unmonitor(current_fiber) }
158
172
  if result.is_a?(Exception)
159
173
  raise result
@@ -187,6 +201,7 @@ module Polyphony
187
201
 
188
202
  def add_child(child_fiber)
189
203
  (@children ||= {})[child_fiber] = true
204
+ child_fiber.monitor(self) if @supervise_mode
190
205
  end
191
206
 
192
207
  def remove_child(child_fiber)
@@ -197,6 +212,7 @@ module Polyphony
197
212
  f = Fiber.new { |v| f.run(v) }
198
213
  f.prepare(tag, block, orig_caller, self)
199
214
  (@children ||= {})[f] = true
215
+ f.monitor(self) if @supervise_mode
200
216
  f
201
217
  end
202
218
 
@@ -225,19 +241,17 @@ module Polyphony
225
241
  c.terminate(graceful)
226
242
  c.await
227
243
  end
228
- reap_dead_children
229
244
  end
230
245
 
231
- def reap_dead_children
232
- return unless @children
233
-
234
- @children.reject! { |f| f.dead? }
246
+ def attach_all_children_to(fiber)
247
+ @children&.keys.each { |c| c.attach_to(fiber) }
235
248
  end
236
249
 
237
250
  def detach
238
251
  @parent.remove_child(self)
239
252
  @parent = @thread.main_fiber
240
253
  @parent.add_child(self)
254
+ self
241
255
  end
242
256
 
243
257
  def attach_to(fiber)
@@ -315,6 +329,7 @@ module Polyphony
315
329
  inform_monitors(result, uncaught_exception)
316
330
  @running = false
317
331
  ensure
332
+ @parent&.remove_child(self)
318
333
  # Prevent fiber from being resumed after terminating
319
334
  @thread.fiber_unschedule(self)
320
335
  Thread.current.switch_fiber
@@ -324,7 +339,7 @@ module Polyphony
324
339
  # the children are shut down, it is returned along with the uncaught_exception
325
340
  # flag set. Otherwise, it returns the given arguments.
326
341
  def finalize_children(result, uncaught_exception)
327
- shutdown_all_children
342
+ shutdown_all_children(graceful_shutdown?)
328
343
  [result, uncaught_exception]
329
344
  rescue Exception => e
330
345
  [e, true]