polyphony 0.68 → 0.72
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +1 -0
- data/CHANGELOG.md +32 -5
- data/Gemfile.lock +2 -2
- data/TODO.md +1 -24
- data/bin/pdbg +1 -1
- data/bin/polyphony-debug +0 -0
- data/bin/stress.rb +0 -0
- data/bin/test +0 -0
- data/docs/_user-guide/all-about-timers.md +1 -1
- data/docs/api-reference/fiber.md +2 -2
- data/docs/faq.md +1 -1
- data/docs/getting-started/overview.md +8 -8
- data/docs/getting-started/tutorial.md +3 -3
- data/docs/main-concepts/concurrency.md +1 -1
- data/docs/main-concepts/extending.md +3 -3
- data/docs/main-concepts/fiber-scheduling.md +1 -1
- data/examples/core/calc.rb +37 -0
- data/examples/core/calc_with_restart.rb +40 -0
- data/examples/core/calc_with_supervise.rb +37 -0
- data/examples/core/message_based_supervision.rb +1 -1
- data/examples/io/rack_server.rb +1 -1
- data/examples/io/tunnel.rb +1 -1
- data/examples/performance/fiber_transfer.rb +1 -1
- data/examples/performance/line_splitting.rb +1 -1
- data/examples/performance/thread-vs-fiber/compare.rb +1 -1
- data/ext/polyphony/backend_common.c +24 -6
- data/ext/polyphony/backend_common.h +1 -0
- data/ext/polyphony/backend_io_uring.c +27 -40
- data/ext/polyphony/backend_io_uring_context.c +1 -1
- data/ext/polyphony/backend_io_uring_context.h +1 -1
- data/ext/polyphony/backend_libev.c +13 -11
- data/ext/polyphony/extconf.rb +24 -13
- data/ext/polyphony/queue.c +2 -2
- data/ext/polyphony/runqueue_ring_buffer.c +3 -2
- data/lib/polyphony/adapters/irb.rb +11 -1
- data/lib/polyphony/core/global_api.rb +3 -3
- data/lib/polyphony/core/timer.rb +2 -2
- data/lib/polyphony/debugger.rb +3 -3
- data/lib/polyphony/extensions/fiber.rb +30 -15
- data/lib/polyphony/extensions/io.rb +3 -3
- data/lib/polyphony/extensions/openssl.rb +21 -6
- data/lib/polyphony/extensions/socket.rb +4 -5
- data/lib/polyphony/net.rb +0 -1
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +1 -1
- data/test/coverage.rb +2 -2
- data/test/stress.rb +1 -1
- data/test/test_backend.rb +12 -12
- data/test/test_event.rb +1 -1
- data/test/test_ext.rb +1 -1
- data/test/test_fiber.rb +52 -12
- data/test/test_global_api.rb +2 -3
- data/test/test_io.rb +3 -3
- data/test/test_process_supervision.rb +38 -9
- data/test/test_queue.rb +6 -6
- data/test/test_socket.rb +12 -10
- data/test/test_supervise.rb +249 -81
- data/test/test_sync.rb +2 -2
- data/test/test_thread.rb +22 -2
- data/test/test_thread_pool.rb +1 -1
- data/test/test_throttler.rb +1 -1
- data/test/test_timer.rb +2 -2
- data/test/test_trace.rb +1 -1
- 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
|
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
|
-
|
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
|
|
@@ -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
|
|
data/ext/polyphony/extconf.rb
CHANGED
@@ -3,32 +3,43 @@
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'mkmf'
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
18
|
-
|
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"
|
data/ext/polyphony/queue.c
CHANGED
@@ -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
|
-
|
65
|
-
rb_gc_mark(
|
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)
|
data/lib/polyphony/core/timer.rb
CHANGED
@@ -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
|
|
data/lib/polyphony/debugger.rb
CHANGED
@@ -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'
|
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
|
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
|
101
|
-
|
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
|
232
|
-
|
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]
|