polyphony 0.71 → 0.72

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -0
  3. data/CHANGELOG.md +18 -4
  4. data/Gemfile.lock +1 -1
  5. data/TODO.md +1 -1
  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 +8 -8
  28. data/ext/polyphony/backend_io_uring.c +26 -33
  29. data/ext/polyphony/backend_io_uring_context.c +1 -1
  30. data/ext/polyphony/backend_io_uring_context.h +1 -1
  31. data/ext/polyphony/backend_libev.c +11 -11
  32. data/ext/polyphony/extconf.rb +24 -13
  33. data/ext/polyphony/queue.c +2 -2
  34. data/ext/polyphony/runqueue_ring_buffer.c +3 -2
  35. data/lib/polyphony/adapters/irb.rb +11 -1
  36. data/lib/polyphony/core/global_api.rb +3 -3
  37. data/lib/polyphony/core/timer.rb +2 -2
  38. data/lib/polyphony/debugger.rb +3 -3
  39. data/lib/polyphony/extensions/fiber.rb +19 -8
  40. data/lib/polyphony/extensions/io.rb +2 -2
  41. data/lib/polyphony/extensions/openssl.rb +20 -5
  42. data/lib/polyphony/extensions/socket.rb +3 -4
  43. data/lib/polyphony/version.rb +1 -1
  44. data/polyphony.gemspec +1 -1
  45. data/test/coverage.rb +2 -2
  46. data/test/test_backend.rb +12 -12
  47. data/test/test_event.rb +1 -1
  48. data/test/test_ext.rb +1 -1
  49. data/test/test_fiber.rb +31 -7
  50. data/test/test_global_api.rb +2 -2
  51. data/test/test_io.rb +3 -3
  52. data/test/test_queue.rb +6 -6
  53. data/test/test_socket.rb +12 -10
  54. data/test/test_supervise.rb +85 -0
  55. data/test/test_sync.rb +2 -2
  56. data/test/test_thread.rb +22 -2
  57. data/test/test_thread_pool.rb +1 -1
  58. data/test/test_throttler.rb +1 -1
  59. data/test/test_timer.rb +2 -2
  60. data/test/test_trace.rb +1 -1
  61. metadata +7 -3
@@ -141,7 +141,7 @@ static inline void io_uring_backend_handle_completion(struct io_uring_cqe *cqe,
141
141
  context_store_release(&backend->store, ctx);
142
142
  }
143
143
 
144
- // adapted from io_uring_peek_batch_cqe in queue.c
144
+ // adapted from io_uring_peek_batch_cqe in queue.c
145
145
  // this peeks at cqes and handles each available cqe
146
146
  void io_uring_backend_handle_ready_cqes(Backend_t *backend) {
147
147
  struct io_uring *ring = &backend->ring;
@@ -200,19 +200,12 @@ inline VALUE Backend_poll(VALUE self, VALUE blocking) {
200
200
  }
201
201
 
202
202
  COND_TRACE(&backend->base, 2, SYM_fiber_event_poll_enter, rb_fiber_current());
203
- // if (SHOULD_TRACE(&backend->base))
204
- // printf(
205
- // "io_uring_poll(blocking_mode: %d, pending: %d, taken: %d, available: %d, runqueue: %d\n",
206
- // is_blocking,
207
- // backend->base.pending_count,
208
- // backend->store.taken_count,
209
- // backend->store.available_count,
210
- // backend->base.runqueue.entries.count
211
- // );
203
+
212
204
  if (is_blocking) io_uring_backend_poll(backend);
213
205
  io_uring_backend_handle_ready_cqes(backend);
214
- COND_TRACE(&backend->base, 2, SYM_fiber_event_poll_leave, rb_fiber_current());
215
206
 
207
+ COND_TRACE(&backend->base, 2, SYM_fiber_event_poll_leave, rb_fiber_current());
208
+
216
209
  return self;
217
210
  }
218
211
 
@@ -255,7 +248,7 @@ VALUE Backend_wakeup(VALUE self) {
255
248
  io_uring_prep_nop(sqe);
256
249
  backend->pending_sqes = 0;
257
250
  io_uring_submit(&backend->ring);
258
-
251
+
259
252
  return Qtrue;
260
253
  }
261
254
 
@@ -411,7 +404,7 @@ VALUE Backend_read_loop(VALUE self, VALUE io, VALUE maxlen) {
411
404
  op_context_t *ctx = context_store_acquire(&backend->store, OP_READ);
412
405
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
413
406
  io_uring_prep_read(sqe, fptr->fd, buf, len, -1);
414
-
407
+
415
408
  ssize_t result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
416
409
  int completed = context_store_release(&backend->store, ctx);
417
410
  if (!completed) {
@@ -461,7 +454,7 @@ VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
461
454
  op_context_t *ctx = context_store_acquire(&backend->store, OP_READ);
462
455
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
463
456
  io_uring_prep_read(sqe, fptr->fd, buf, len, -1);
464
-
457
+
465
458
  ssize_t result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
466
459
  int completed = context_store_release(&backend->store, ctx);
467
460
  if (!completed) {
@@ -507,7 +500,7 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
507
500
  op_context_t *ctx = context_store_acquire(&backend->store, OP_WRITE);
508
501
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
509
502
  io_uring_prep_write(sqe, fptr->fd, buf, left, 0);
510
-
503
+
511
504
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
512
505
  int completed = context_store_release(&backend->store, ctx);
513
506
  if (!completed) {
@@ -516,7 +509,7 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
516
509
  return resume_value;
517
510
  }
518
511
  RB_GC_GUARD(resume_value);
519
-
512
+
520
513
  if (result < 0)
521
514
  rb_syserr_fail(-result, strerror(-result));
522
515
  else {
@@ -559,7 +552,7 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
559
552
  op_context_t *ctx = context_store_acquire(&backend->store, OP_WRITEV);
560
553
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
561
554
  io_uring_prep_writev(sqe, fptr->fd, iov_ptr, iov_count, -1);
562
-
555
+
563
556
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
564
557
  int completed = context_store_release(&backend->store, ctx);
565
558
  if (!completed) {
@@ -635,7 +628,7 @@ VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length, VALUE pos) {
635
628
  op_context_t *ctx = context_store_acquire(&backend->store, OP_RECV);
636
629
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
637
630
  io_uring_prep_recv(sqe, fptr->fd, buf, len - total, 0);
638
-
631
+
639
632
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
640
633
  int completed = context_store_release(&backend->store, ctx);
641
634
  if (!completed) {
@@ -685,7 +678,7 @@ VALUE Backend_recv_loop(VALUE self, VALUE io, VALUE maxlen) {
685
678
  op_context_t *ctx = context_store_acquire(&backend->store, OP_RECV);
686
679
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
687
680
  io_uring_prep_recv(sqe, fptr->fd, buf, len, 0);
688
-
681
+
689
682
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
690
683
  int completed = context_store_release(&backend->store, ctx);
691
684
  if (!completed) {
@@ -734,7 +727,7 @@ VALUE Backend_recv_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method)
734
727
  op_context_t *ctx = context_store_acquire(&backend->store, OP_RECV);
735
728
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
736
729
  io_uring_prep_recv(sqe, fptr->fd, buf, len, 0);
737
-
730
+
738
731
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
739
732
  int completed = context_store_release(&backend->store, ctx);
740
733
  if (!completed) {
@@ -780,7 +773,7 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
780
773
  op_context_t *ctx = context_store_acquire(&backend->store, OP_SEND);
781
774
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
782
775
  io_uring_prep_send(sqe, fptr->fd, buf, left, flags_int);
783
-
776
+
784
777
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
785
778
  int completed = context_store_release(&backend->store, ctx);
786
779
  if (!completed) {
@@ -817,7 +810,7 @@ VALUE io_uring_backend_accept(Backend_t *backend, VALUE server_socket, VALUE soc
817
810
  op_context_t *ctx = context_store_acquire(&backend->store, OP_ACCEPT);
818
811
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
819
812
  io_uring_prep_accept(sqe, fptr->fd, &addr, &len, 0);
820
-
813
+
821
814
  int fd = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
822
815
  int completed = context_store_release(&backend->store, ctx);
823
816
  RAISE_IF_EXCEPTION(resume_value);
@@ -888,7 +881,7 @@ VALUE io_uring_backend_splice(Backend_t *backend, VALUE src, VALUE dest, VALUE m
888
881
  op_context_t *ctx = context_store_acquire(&backend->store, OP_SPLICE);
889
882
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
890
883
  io_uring_prep_splice(sqe, src_fptr->fd, -1, dest_fptr->fd, -1, NUM2INT(maxlen), 0);
891
-
884
+
892
885
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
893
886
  int completed = context_store_release(&backend->store, ctx);
894
887
  RAISE_IF_EXCEPTION(resume_value);
@@ -944,7 +937,7 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
944
937
  RAISE_IF_EXCEPTION(resume_value);
945
938
  if (!completed) return resume_value;
946
939
  RB_GC_GUARD(resume_value);
947
-
940
+
948
941
  if (result < 0) rb_syserr_fail(-result, strerror(-result));
949
942
  return sock;
950
943
  }
@@ -981,7 +974,7 @@ inline struct __kernel_timespec duration_to_timespec(VALUE duration) {
981
974
  int io_uring_backend_submit_timeout_and_await(Backend_t *backend, double duration, VALUE *resume_value) {
982
975
  struct __kernel_timespec ts = double_to_timespec(duration);
983
976
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
984
-
977
+
985
978
  op_context_t *ctx = context_store_acquire(&backend->store, OP_TIMEOUT);
986
979
  io_uring_prep_timeout(sqe, &ts, 0, 0);
987
980
  io_uring_backend_defer_submit_and_await(backend, sqe, ctx, resume_value);
@@ -1010,7 +1003,7 @@ VALUE Backend_timer_loop(VALUE self, VALUE interval) {
1010
1003
  if (next_time == 0.) next_time = current_time() + interval_d;
1011
1004
  double sleep_duration = next_time - now;
1012
1005
  if (sleep_duration < 0) sleep_duration = 0;
1013
-
1006
+
1014
1007
  VALUE resume_value = Qnil;
1015
1008
  int completed = io_uring_backend_submit_timeout_and_await(backend, sleep_duration, &resume_value);
1016
1009
  RAISE_IF_EXCEPTION(resume_value);
@@ -1051,7 +1044,7 @@ VALUE Backend_timeout(int argc, VALUE *argv, VALUE self) {
1051
1044
  VALUE exception;
1052
1045
  VALUE move_on_value = Qnil;
1053
1046
  rb_scan_args(argc, argv, "21", &duration, &exception, &move_on_value);
1054
-
1047
+
1055
1048
  struct __kernel_timespec ts = duration_to_timespec(duration);
1056
1049
  Backend_t *backend;
1057
1050
  GetBackend(self, backend);
@@ -1059,7 +1052,7 @@ VALUE Backend_timeout(int argc, VALUE *argv, VALUE self) {
1059
1052
  VALUE timeout = rb_funcall(cTimeoutException, ID_new, 0);
1060
1053
 
1061
1054
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
1062
-
1055
+
1063
1056
  op_context_t *ctx = context_store_acquire(&backend->store, OP_TIMEOUT);
1064
1057
  ctx->resume_value = timeout;
1065
1058
  io_uring_prep_timeout(sqe, &ts, 0, 0);
@@ -1094,7 +1087,7 @@ VALUE Backend_waitpid(VALUE self, VALUE pid) {
1094
1087
  RAISE_IF_EXCEPTION(resume_value);
1095
1088
  RB_GC_GUARD(resume_value);
1096
1089
  }
1097
-
1090
+
1098
1091
  int status;
1099
1092
  pid_t ret = waitpid(pid_int, &status, WNOHANG);
1100
1093
  if (ret < 0) {
@@ -1244,7 +1237,7 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1244
1237
  }
1245
1238
  rb_raise(rb_eRuntimeError, "Invalid op specified or bad op arity");
1246
1239
  }
1247
-
1240
+
1248
1241
  io_uring_sqe_set_data(last_sqe, ctx);
1249
1242
  unsigned int flags = (i == (argc - 1)) ? IOSQE_ASYNC : IOSQE_ASYNC | IOSQE_IO_LINK;
1250
1243
  io_uring_sqe_set_flags(last_sqe, flags);
@@ -1259,7 +1252,7 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1259
1252
  int completed = context_store_release(&backend->store, ctx);
1260
1253
  if (!completed) {
1261
1254
  Backend_chain_ctx_attach_buffers(ctx, argc, argv);
1262
-
1255
+
1263
1256
  // op was not completed (an exception was raised), so we need to cancel it
1264
1257
  ctx->result = -ECANCELED;
1265
1258
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
@@ -1269,7 +1262,7 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1269
1262
  RAISE_IF_EXCEPTION(resume_value);
1270
1263
  return resume_value;
1271
1264
  }
1272
-
1265
+
1273
1266
  RB_GC_GUARD(resume_value);
1274
1267
  return INT2NUM(result);
1275
1268
  }
@@ -1404,7 +1397,7 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
1404
1397
 
1405
1398
  SPLICE_CHUNKS_AWAIT_OPS(backend, &ctx, &chunk_len, &switchpoint_result);
1406
1399
  if (chunk_len == 0) break;
1407
-
1400
+
1408
1401
  total += chunk_len;
1409
1402
  chunk_len_value = INT2NUM(chunk_len);
1410
1403
 
@@ -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);
@@ -188,7 +188,7 @@ inline void Backend_unschedule_fiber(VALUE self, VALUE fiber) {
188
188
  Backend_t *backend;
189
189
  GetBackend(self, backend);
190
190
 
191
- runqueue_delete(&backend->base.runqueue, fiber);
191
+ runqueue_delete(&backend->base.runqueue, fiber);
192
192
  }
193
193
 
194
194
  inline VALUE Backend_switch_fiber(VALUE self) {
@@ -971,7 +971,7 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
971
971
  io_verify_blocking_mode(dest_fptr, dest, Qfalse);
972
972
 
973
973
  watcher.fiber = Qnil;
974
-
974
+
975
975
  while (1) {
976
976
  backend->base.op_count++;
977
977
  ssize_t n = read(src_fptr->fd, buf, len);
@@ -1047,7 +1047,7 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
1047
1047
 
1048
1048
  watcher.fiber = Qnil;
1049
1049
 
1050
- while (1) {
1050
+ while (1) {
1051
1051
  char *ptr = buf;
1052
1052
  while (1) {
1053
1053
  backend->base.op_count++;
@@ -1159,8 +1159,8 @@ noreturn VALUE Backend_timer_loop(VALUE self, VALUE interval) {
1159
1159
  if (next_time == 0.) next_time = current_time() + interval_d;
1160
1160
  double sleep_duration = next_time - now;
1161
1161
  if (sleep_duration < 0) sleep_duration = 0;
1162
-
1163
- VALUE switchpoint_result = Qnil;
1162
+
1163
+ VALUE switchpoint_result = Qnil;
1164
1164
  ev_timer_init(&watcher.timer, Backend_timer_callback, sleep_duration, 0.);
1165
1165
  ev_timer_start(backend->ev_loop, &watcher.timer);
1166
1166
  backend->base.op_count++;
@@ -1219,7 +1219,7 @@ VALUE Backend_timeout(int argc,VALUE *argv, VALUE self) {
1219
1219
 
1220
1220
  struct Backend_timeout_ctx timeout_ctx = {backend, &watcher};
1221
1221
  result = rb_ensure(Backend_timeout_ensure_safe, Qnil, Backend_timeout_ensure, (VALUE)&timeout_ctx);
1222
-
1222
+
1223
1223
  if (result == timeout) {
1224
1224
  if (exception == Qnil) return move_on_value;
1225
1225
  RAISE_EXCEPTION(backend_timeout_exception(exception));
@@ -1337,7 +1337,7 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1337
1337
  else
1338
1338
  rb_raise(rb_eRuntimeError, "Invalid op specified or bad op arity");
1339
1339
  }
1340
-
1340
+
1341
1341
  RB_GC_GUARD(result);
1342
1342
  return result;
1343
1343
  }
@@ -1386,14 +1386,14 @@ inline int splice_chunks_write(Backend_t *backend, int fd, VALUE str, struct lib
1386
1386
  return 0;
1387
1387
  }
1388
1388
 
1389
- 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,
1390
1390
  struct libev_rw_io *watcher, VALUE *result, int *chunk_len) {
1391
1391
  #ifdef POLYPHONY_LINUX
1392
1392
  backend->base.op_count++;
1393
1393
  while (1) {
1394
1394
  *chunk_len = splice(src_fd, 0, dest_fd, 0, maxlen, 0);
1395
1395
  if (*chunk_len >= 0) return 0;
1396
-
1396
+
1397
1397
  int err = errno;
1398
1398
  if (err != EWOULDBLOCK && err != EAGAIN) return err;
1399
1399
 
@@ -1489,7 +1489,7 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
1489
1489
  err = splice_chunks_splice(backend, src_fptr->fd, pipefd[1], maxlen, &watcher, &result, &chunk_len);
1490
1490
  if (err == -1) goto error; else if (err) goto syscallerror;
1491
1491
  if (chunk_len == 0) break;
1492
-
1492
+
1493
1493
  total += chunk_len;
1494
1494
  chunk_len_value = INT2NUM(chunk_len);
1495
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)
@@ -83,6 +82,8 @@ module Polyphony
83
82
  def supervise(*fibers, **opts, &block)
84
83
  block ||= supervise_opts_to_block(opts)
85
84
 
85
+ @supervise_mode = true
86
+ fibers = children if fibers.empty?
86
87
  fibers.each do |f|
87
88
  f.attach_to(self) unless f.parent == self
88
89
  f.monitor(self)
@@ -94,15 +95,18 @@ module Polyphony
94
95
  (fiber, result) = mailbox.shift
95
96
  block&.call(fiber, result)
96
97
  end
98
+ ensure
99
+ @supervise_mode = false
97
100
  end
98
101
 
99
102
  def supervise_opts_to_block(opts)
100
103
  block = opts[:on_done] || opts[:on_error]
101
- return nil unless block || opts[:restart]
104
+ restart = opts[:restart]
105
+ return nil unless block || restart
102
106
 
103
107
  error_only = !!opts[:on_error]
104
- restart_always = opts[:restart] == :always
105
- restart_on_error = opts[:restart] == :on_error
108
+ restart_always = (restart == :always) || (restart == true)
109
+ restart_on_error = restart == :on_error
106
110
 
107
111
  ->(f, r) do
108
112
  is_error = r.is_a?(Exception)
@@ -149,7 +153,7 @@ module Polyphony
149
153
 
150
154
  def select(*fibers)
151
155
  return nil if fibers.empty?
152
-
156
+
153
157
  current_fiber = self.current
154
158
  mailbox = current_fiber.monitor_mailbox
155
159
  fibers.each do |f|
@@ -163,7 +167,7 @@ module Polyphony
163
167
  while true
164
168
  (fiber, result) = mailbox.shift
165
169
  next unless fibers.include?(fiber)
166
-
170
+
167
171
  fibers.each { |f| f.unmonitor(current_fiber) }
168
172
  if result.is_a?(Exception)
169
173
  raise result
@@ -197,6 +201,7 @@ module Polyphony
197
201
 
198
202
  def add_child(child_fiber)
199
203
  (@children ||= {})[child_fiber] = true
204
+ child_fiber.monitor(self) if @supervise_mode
200
205
  end
201
206
 
202
207
  def remove_child(child_fiber)
@@ -207,6 +212,7 @@ module Polyphony
207
212
  f = Fiber.new { |v| f.run(v) }
208
213
  f.prepare(tag, block, orig_caller, self)
209
214
  (@children ||= {})[f] = true
215
+ f.monitor(self) if @supervise_mode
210
216
  f
211
217
  end
212
218
 
@@ -237,10 +243,15 @@ module Polyphony
237
243
  end
238
244
  end
239
245
 
246
+ def attach_all_children_to(fiber)
247
+ @children&.keys.each { |c| c.attach_to(fiber) }
248
+ end
249
+
240
250
  def detach
241
251
  @parent.remove_child(self)
242
252
  @parent = @thread.main_fiber
243
253
  @parent.add_child(self)
254
+ self
244
255
  end
245
256
 
246
257
  def attach_to(fiber)
@@ -328,7 +339,7 @@ module Polyphony
328
339
  # the children are shut down, it is returned along with the uncaught_exception
329
340
  # flag set. Otherwise, it returns the given arguments.
330
341
  def finalize_children(result, uncaught_exception)
331
- shutdown_all_children
342
+ shutdown_all_children(graceful_shutdown?)
332
343
  [result, uncaught_exception]
333
344
  rescue Exception => e
334
345
  [e, true]
@@ -103,7 +103,7 @@ class ::IO
103
103
  alias_method :orig_getc, :getc
104
104
  def getc
105
105
  return @read_buffer.slice!(0) if @read_buffer && !@read_buffer.empty?
106
-
106
+
107
107
  @read_buffer ||= +''
108
108
  Polyphony.backend_read(self, @read_buffer, 8192, false, -1)
109
109
  return @read_buffer.slice!(0) if !@read_buffer.empty?
@@ -116,7 +116,7 @@ class ::IO
116
116
  if buf
117
117
  return Polyphony.backend_read(self, buf, len, true, buf_pos)
118
118
  end
119
-
119
+
120
120
  @read_buffer ||= +''
121
121
  result = Polyphony.backend_read(self, @read_buffer, len, true, -1)
122
122
  return nil unless result