polyphony 0.68 → 0.72

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