polyphony 0.49.2 → 0.53.0
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.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +1 -1
- data/CHANGELOG.md +34 -0
- data/Gemfile.lock +7 -68
- data/TODO.md +37 -3
- data/examples/core/forking.rb +2 -2
- data/examples/core/nested.rb +21 -0
- data/examples/core/suspend.rb +13 -0
- data/examples/core/terminate_main_fiber.rb +12 -0
- data/examples/io/echo_server.rb +1 -0
- data/examples/io/tcp_proxy.rb +2 -2
- data/ext/polyphony/backend_common.h +58 -8
- data/ext/polyphony/backend_io_uring.c +223 -41
- data/ext/polyphony/backend_io_uring_context.c +1 -0
- data/ext/polyphony/backend_io_uring_context.h +1 -0
- data/ext/polyphony/backend_libev.c +322 -34
- data/ext/polyphony/event.c +1 -1
- data/ext/polyphony/extconf.rb +9 -2
- data/ext/polyphony/fiber.c +2 -1
- data/ext/polyphony/polyphony.c +102 -0
- data/ext/polyphony/polyphony.h +33 -2
- data/ext/polyphony/polyphony_ext.c +3 -0
- data/ext/polyphony/queue.c +1 -1
- data/ext/polyphony/runqueue.c +7 -1
- data/ext/polyphony/runqueue_ring_buffer.c +9 -0
- data/ext/polyphony/runqueue_ring_buffer.h +1 -0
- data/ext/polyphony/socket_extensions.c +33 -0
- data/ext/polyphony/thread.c +14 -0
- data/lib/polyphony/adapters/irb.rb +1 -1
- data/lib/polyphony/adapters/mysql2.rb +1 -1
- data/lib/polyphony/adapters/postgres.rb +5 -5
- data/lib/polyphony/adapters/process.rb +4 -4
- data/lib/polyphony/core/exceptions.rb +1 -0
- data/lib/polyphony/core/global_api.rb +6 -6
- data/lib/polyphony/core/sync.rb +1 -1
- data/lib/polyphony/core/throttler.rb +1 -1
- data/lib/polyphony/core/timer.rb +63 -20
- data/lib/polyphony/extensions/core.rb +5 -5
- data/lib/polyphony/extensions/fiber.rb +2 -0
- data/lib/polyphony/extensions/io.rb +21 -22
- data/lib/polyphony/extensions/openssl.rb +6 -6
- data/lib/polyphony/extensions/socket.rb +56 -47
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +6 -5
- data/test/helper.rb +1 -1
- data/test/stress.rb +2 -0
- data/test/test_backend.rb +69 -5
- data/test/test_fiber.rb +16 -0
- data/test/test_global_api.rb +2 -2
- data/test/test_io.rb +84 -1
- data/test/test_kernel.rb +1 -1
- data/test/test_signal.rb +1 -1
- data/test/test_socket.rb +61 -0
- data/test/test_timer.rb +41 -8
- metadata +22 -60
|
@@ -16,18 +16,36 @@
|
|
|
16
16
|
|
|
17
17
|
#include "polyphony.h"
|
|
18
18
|
#include "../liburing/liburing.h"
|
|
19
|
-
#include "ruby/thread.h"
|
|
20
19
|
#include "backend_io_uring_context.h"
|
|
20
|
+
#include "ruby/thread.h"
|
|
21
|
+
#include "ruby/io.h"
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
#define __NR_pidfd_open 434 /* System call # on most architectures */
|
|
24
|
-
#endif
|
|
23
|
+
VALUE SYM_io_uring;
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
#ifdef POLYPHONY_UNSET_NONBLOCK
|
|
26
|
+
ID ID_ivar_is_nonblocking;
|
|
27
|
+
|
|
28
|
+
// One of the changes introduced in Ruby 3.0 as part of the work on the
|
|
29
|
+
// FiberScheduler interface is that all created sockets are marked as
|
|
30
|
+
// non-blocking. This prevents the io_uring backend from working correctly,
|
|
31
|
+
// since it will return an EAGAIN error just like a normal syscall. So here
|
|
32
|
+
// instead of setting O_NONBLOCK (which is required for the libev backend), we
|
|
33
|
+
// unset it.
|
|
34
|
+
inline void io_unset_nonblock(rb_io_t *fptr, VALUE io) {
|
|
35
|
+
VALUE is_nonblocking = rb_ivar_get(io, ID_ivar_is_nonblocking);
|
|
36
|
+
if (is_nonblocking == Qfalse) return;
|
|
37
|
+
|
|
38
|
+
rb_ivar_set(io, ID_ivar_is_nonblocking, Qfalse);
|
|
39
|
+
|
|
40
|
+
int oflags = fcntl(fptr->fd, F_GETFL);
|
|
41
|
+
if ((oflags == -1) && (oflags & O_NONBLOCK)) return;
|
|
42
|
+
oflags &= !O_NONBLOCK;
|
|
43
|
+
fcntl(fptr->fd, F_SETFL, oflags);
|
|
28
44
|
}
|
|
29
|
-
|
|
30
|
-
|
|
45
|
+
#else
|
|
46
|
+
// NOP
|
|
47
|
+
#define io_unset_nonblock(fptr, io)
|
|
48
|
+
#endif
|
|
31
49
|
|
|
32
50
|
typedef struct Backend_t {
|
|
33
51
|
// common fields
|
|
@@ -260,7 +278,7 @@ int io_uring_backend_defer_submit_and_await(
|
|
|
260
278
|
VALUE switchpoint_result = Qnil;
|
|
261
279
|
|
|
262
280
|
io_uring_sqe_set_data(sqe, ctx);
|
|
263
|
-
|
|
281
|
+
io_uring_sqe_set_flags(sqe, IOSQE_ASYNC);
|
|
264
282
|
io_uring_backend_defer_submit(backend);
|
|
265
283
|
|
|
266
284
|
switchpoint_result = backend_await(backend);
|
|
@@ -308,6 +326,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
|
|
|
308
326
|
if (underlying_io != Qnil) io = underlying_io;
|
|
309
327
|
GetOpenFile(io, fptr);
|
|
310
328
|
rb_io_check_byte_readable(fptr);
|
|
329
|
+
io_unset_nonblock(fptr, io);
|
|
311
330
|
rectify_io_file_pos(fptr);
|
|
312
331
|
OBJ_TAINT(str);
|
|
313
332
|
|
|
@@ -368,6 +387,7 @@ VALUE Backend_read_loop(VALUE self, VALUE io) {
|
|
|
368
387
|
if (underlying_io != Qnil) io = underlying_io;
|
|
369
388
|
GetOpenFile(io, fptr);
|
|
370
389
|
rb_io_check_byte_readable(fptr);
|
|
390
|
+
io_unset_nonblock(fptr, io);
|
|
371
391
|
rectify_io_file_pos(fptr);
|
|
372
392
|
|
|
373
393
|
while (1) {
|
|
@@ -397,6 +417,53 @@ VALUE Backend_read_loop(VALUE self, VALUE io) {
|
|
|
397
417
|
return io;
|
|
398
418
|
}
|
|
399
419
|
|
|
420
|
+
VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
|
|
421
|
+
Backend_t *backend;
|
|
422
|
+
rb_io_t *fptr;
|
|
423
|
+
VALUE str;
|
|
424
|
+
long total;
|
|
425
|
+
long len = 8192;
|
|
426
|
+
int shrinkable;
|
|
427
|
+
char *buf;
|
|
428
|
+
VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
|
|
429
|
+
ID method_id = SYM2ID(method);
|
|
430
|
+
|
|
431
|
+
READ_LOOP_PREPARE_STR();
|
|
432
|
+
|
|
433
|
+
GetBackend(self, backend);
|
|
434
|
+
if (underlying_io != Qnil) io = underlying_io;
|
|
435
|
+
GetOpenFile(io, fptr);
|
|
436
|
+
rb_io_check_byte_readable(fptr);
|
|
437
|
+
io_unset_nonblock(fptr, io);
|
|
438
|
+
rectify_io_file_pos(fptr);
|
|
439
|
+
|
|
440
|
+
while (1) {
|
|
441
|
+
VALUE resume_value = Qnil;
|
|
442
|
+
op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_READ);
|
|
443
|
+
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
|
444
|
+
io_uring_prep_read(sqe, fptr->fd, buf, len, -1);
|
|
445
|
+
|
|
446
|
+
ssize_t result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
|
447
|
+
OP_CONTEXT_RELEASE(&backend->store, ctx);
|
|
448
|
+
RAISE_IF_EXCEPTION(resume_value);
|
|
449
|
+
if (!ctx->completed) return resume_value;
|
|
450
|
+
RB_GC_GUARD(resume_value);
|
|
451
|
+
|
|
452
|
+
if (result < 0)
|
|
453
|
+
rb_syserr_fail(-result, strerror(-result));
|
|
454
|
+
else if (!result)
|
|
455
|
+
break; // EOF
|
|
456
|
+
else {
|
|
457
|
+
total = result;
|
|
458
|
+
READ_LOOP_PASS_STR_TO_RECEIVER(receiver, method_id);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
RB_GC_GUARD(str);
|
|
463
|
+
|
|
464
|
+
return io;
|
|
465
|
+
}
|
|
466
|
+
|
|
400
467
|
VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
|
|
401
468
|
Backend_t *backend;
|
|
402
469
|
rb_io_t *fptr;
|
|
@@ -407,6 +474,7 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
|
|
|
407
474
|
GetBackend(self, backend);
|
|
408
475
|
io = rb_io_get_write_io(io);
|
|
409
476
|
GetOpenFile(io, fptr);
|
|
477
|
+
io_unset_nonblock(fptr, io);
|
|
410
478
|
|
|
411
479
|
char *buf = StringValuePtr(str);
|
|
412
480
|
long len = RSTRING_LEN(str);
|
|
@@ -450,6 +518,7 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
|
450
518
|
GetBackend(self, backend);
|
|
451
519
|
io = rb_io_get_write_io(io);
|
|
452
520
|
GetOpenFile(io, fptr);
|
|
521
|
+
io_unset_nonblock(fptr, io);
|
|
453
522
|
|
|
454
523
|
iov = malloc(iov_count * sizeof(struct iovec));
|
|
455
524
|
for (int i = 0; i < argc; i++) {
|
|
@@ -529,6 +598,7 @@ VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length) {
|
|
|
529
598
|
if (underlying_io != Qnil) io = underlying_io;
|
|
530
599
|
GetOpenFile(io, fptr);
|
|
531
600
|
rb_io_check_byte_readable(fptr);
|
|
601
|
+
io_unset_nonblock(fptr, io);
|
|
532
602
|
rectify_io_file_pos(fptr);
|
|
533
603
|
OBJ_TAINT(str);
|
|
534
604
|
|
|
@@ -576,6 +646,7 @@ VALUE Backend_recv_loop(VALUE self, VALUE io) {
|
|
|
576
646
|
if (underlying_io != Qnil) io = underlying_io;
|
|
577
647
|
GetOpenFile(io, fptr);
|
|
578
648
|
rb_io_check_byte_readable(fptr);
|
|
649
|
+
io_unset_nonblock(fptr, io);
|
|
579
650
|
rectify_io_file_pos(fptr);
|
|
580
651
|
|
|
581
652
|
while (1) {
|
|
@@ -604,7 +675,53 @@ VALUE Backend_recv_loop(VALUE self, VALUE io) {
|
|
|
604
675
|
return io;
|
|
605
676
|
}
|
|
606
677
|
|
|
607
|
-
VALUE
|
|
678
|
+
VALUE Backend_recv_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
|
|
679
|
+
Backend_t *backend;
|
|
680
|
+
rb_io_t *fptr;
|
|
681
|
+
VALUE str;
|
|
682
|
+
long total;
|
|
683
|
+
long len = 8192;
|
|
684
|
+
int shrinkable;
|
|
685
|
+
char *buf;
|
|
686
|
+
VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
|
|
687
|
+
ID method_id = SYM2ID(method);
|
|
688
|
+
|
|
689
|
+
READ_LOOP_PREPARE_STR();
|
|
690
|
+
|
|
691
|
+
GetBackend(self, backend);
|
|
692
|
+
if (underlying_io != Qnil) io = underlying_io;
|
|
693
|
+
GetOpenFile(io, fptr);
|
|
694
|
+
rb_io_check_byte_readable(fptr);
|
|
695
|
+
io_unset_nonblock(fptr, io);
|
|
696
|
+
rectify_io_file_pos(fptr);
|
|
697
|
+
|
|
698
|
+
while (1) {
|
|
699
|
+
VALUE resume_value = Qnil;
|
|
700
|
+
op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_RECV);
|
|
701
|
+
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
|
702
|
+
io_uring_prep_recv(sqe, fptr->fd, buf, len, 0);
|
|
703
|
+
|
|
704
|
+
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
|
705
|
+
OP_CONTEXT_RELEASE(&backend->store, ctx);
|
|
706
|
+
RAISE_IF_EXCEPTION(resume_value);
|
|
707
|
+
if (!ctx->completed) return resume_value;
|
|
708
|
+
RB_GC_GUARD(resume_value);
|
|
709
|
+
|
|
710
|
+
if (result < 0)
|
|
711
|
+
rb_syserr_fail(-result, strerror(-result));
|
|
712
|
+
else if (!result)
|
|
713
|
+
break; // EOF
|
|
714
|
+
else {
|
|
715
|
+
total = result;
|
|
716
|
+
READ_LOOP_PASS_STR_TO_RECEIVER(receiver, method_id);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
RB_GC_GUARD(str);
|
|
721
|
+
return io;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
|
|
608
725
|
Backend_t *backend;
|
|
609
726
|
rb_io_t *fptr;
|
|
610
727
|
VALUE underlying_io;
|
|
@@ -614,16 +731,18 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str) {
|
|
|
614
731
|
GetBackend(self, backend);
|
|
615
732
|
io = rb_io_get_write_io(io);
|
|
616
733
|
GetOpenFile(io, fptr);
|
|
734
|
+
io_unset_nonblock(fptr, io);
|
|
617
735
|
|
|
618
736
|
char *buf = StringValuePtr(str);
|
|
619
737
|
long len = RSTRING_LEN(str);
|
|
620
738
|
long left = len;
|
|
739
|
+
int flags_int = NUM2INT(flags);
|
|
621
740
|
|
|
622
741
|
while (left > 0) {
|
|
623
742
|
VALUE resume_value = Qnil;
|
|
624
743
|
op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_SEND);
|
|
625
744
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
|
626
|
-
io_uring_prep_send(sqe, fptr->fd, buf, left,
|
|
745
|
+
io_uring_prep_send(sqe, fptr->fd, buf, left, flags_int);
|
|
627
746
|
|
|
628
747
|
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
|
629
748
|
OP_CONTEXT_RELEASE(&backend->store, ctx);
|
|
@@ -651,6 +770,8 @@ VALUE io_uring_backend_accept(Backend_t *backend, VALUE server_socket, VALUE soc
|
|
|
651
770
|
if (underlying_sock != Qnil) server_socket = underlying_sock;
|
|
652
771
|
|
|
653
772
|
GetOpenFile(server_socket, fptr);
|
|
773
|
+
io_unset_nonblock(fptr, server_socket);
|
|
774
|
+
|
|
654
775
|
while (1) {
|
|
655
776
|
VALUE resume_value = Qnil;
|
|
656
777
|
op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_ACCEPT);
|
|
@@ -704,6 +825,60 @@ VALUE Backend_accept_loop(VALUE self, VALUE server_socket, VALUE socket_class) {
|
|
|
704
825
|
return self;
|
|
705
826
|
}
|
|
706
827
|
|
|
828
|
+
VALUE io_uring_backend_splice(Backend_t *backend, VALUE src, VALUE dest, VALUE maxlen, int loop) {
|
|
829
|
+
rb_io_t *src_fptr;
|
|
830
|
+
rb_io_t *dest_fptr;
|
|
831
|
+
VALUE underlying_io;
|
|
832
|
+
int total = 0;
|
|
833
|
+
|
|
834
|
+
underlying_io = rb_ivar_get(src, ID_ivar_io);
|
|
835
|
+
if (underlying_io != Qnil) src = underlying_io;
|
|
836
|
+
GetOpenFile(src, src_fptr);
|
|
837
|
+
io_unset_nonblock(src_fptr, src);
|
|
838
|
+
|
|
839
|
+
underlying_io = rb_ivar_get(dest, ID_ivar_io);
|
|
840
|
+
if (underlying_io != Qnil) dest = underlying_io;
|
|
841
|
+
dest = rb_io_get_write_io(dest);
|
|
842
|
+
GetOpenFile(dest, dest_fptr);
|
|
843
|
+
io_unset_nonblock(dest_fptr, dest);
|
|
844
|
+
|
|
845
|
+
VALUE resume_value = Qnil;
|
|
846
|
+
|
|
847
|
+
while (1) {
|
|
848
|
+
op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_SPLICE);
|
|
849
|
+
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
|
850
|
+
io_uring_prep_splice(sqe, src_fptr->fd, -1, dest_fptr->fd, -1, NUM2INT(maxlen), 0);
|
|
851
|
+
|
|
852
|
+
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
|
853
|
+
OP_CONTEXT_RELEASE(&backend->store, ctx);
|
|
854
|
+
RAISE_IF_EXCEPTION(resume_value);
|
|
855
|
+
if (!ctx->completed) return resume_value;
|
|
856
|
+
|
|
857
|
+
if (result < 0)
|
|
858
|
+
rb_syserr_fail(-result, strerror(-result));
|
|
859
|
+
|
|
860
|
+
if (result == 0 || !loop) return INT2NUM(total);
|
|
861
|
+
total += result;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
RB_GC_GUARD(resume_value);
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
868
|
+
Backend_t *backend;
|
|
869
|
+
GetBackend(self, backend);
|
|
870
|
+
|
|
871
|
+
return io_uring_backend_splice(backend, src, dest, maxlen, 0);
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE chunksize) {
|
|
875
|
+
Backend_t *backend;
|
|
876
|
+
GetBackend(self, backend);
|
|
877
|
+
|
|
878
|
+
return io_uring_backend_splice(backend, src, dest, chunksize, 1);
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
|
|
707
882
|
VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
|
708
883
|
Backend_t *backend;
|
|
709
884
|
rb_io_t *fptr;
|
|
@@ -714,6 +889,7 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
|
|
714
889
|
|
|
715
890
|
GetBackend(self, backend);
|
|
716
891
|
GetOpenFile(sock, fptr);
|
|
892
|
+
io_unset_nonblock(fptr, sock);
|
|
717
893
|
|
|
718
894
|
addr.sin_family = AF_INET;
|
|
719
895
|
addr.sin_addr.s_addr = inet_addr(host_buf);
|
|
@@ -740,6 +916,7 @@ VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
|
|
|
740
916
|
if (underlying_io != Qnil) io = underlying_io;
|
|
741
917
|
GetBackend(self, backend);
|
|
742
918
|
GetOpenFile(io, fptr);
|
|
919
|
+
io_unset_nonblock(fptr, io);
|
|
743
920
|
|
|
744
921
|
VALUE resume_value = io_uring_backend_wait_fd(backend, fptr->fd, RTEST(write));
|
|
745
922
|
RAISE_IF_EXCEPTION(resume_value);
|
|
@@ -811,18 +988,6 @@ VALUE Backend_timer_loop(VALUE self, VALUE interval) {
|
|
|
811
988
|
}
|
|
812
989
|
}
|
|
813
990
|
|
|
814
|
-
VALUE Backend_timeout_safe(VALUE arg) {
|
|
815
|
-
return rb_yield(arg);
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
VALUE Backend_timeout_rescue(VALUE arg, VALUE exception) {
|
|
819
|
-
return exception;
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
VALUE Backend_timeout_ensure_safe(VALUE arg) {
|
|
823
|
-
return rb_rescue2(Backend_timeout_safe, Qnil, Backend_timeout_rescue, Qnil, rb_eException, (VALUE)0);
|
|
824
|
-
}
|
|
825
|
-
|
|
826
991
|
struct Backend_timeout_ctx {
|
|
827
992
|
Backend_t *backend;
|
|
828
993
|
op_context_t *ctx;
|
|
@@ -861,7 +1026,6 @@ VALUE Backend_timeout(int argc, VALUE *argv, VALUE self) {
|
|
|
861
1026
|
ctx->resume_value = timeout;
|
|
862
1027
|
io_uring_prep_timeout(sqe, &ts, 0, 0);
|
|
863
1028
|
io_uring_sqe_set_data(sqe, ctx);
|
|
864
|
-
io_uring_sqe_set_flags(sqe, IOSQE_ASYNC);
|
|
865
1029
|
io_uring_backend_defer_submit(backend);
|
|
866
1030
|
|
|
867
1031
|
struct Backend_timeout_ctx timeout_ctx = {backend, ctx};
|
|
@@ -879,19 +1043,28 @@ VALUE Backend_timeout(int argc, VALUE *argv, VALUE self) {
|
|
|
879
1043
|
}
|
|
880
1044
|
|
|
881
1045
|
VALUE Backend_waitpid(VALUE self, VALUE pid) {
|
|
882
|
-
Backend_t *backend;
|
|
883
1046
|
int pid_int = NUM2INT(pid);
|
|
884
1047
|
int fd = pidfd_open(pid_int, 0);
|
|
885
|
-
GetBackend(self, backend);
|
|
886
|
-
|
|
887
|
-
VALUE resume_value = io_uring_backend_wait_fd(backend, fd, 0);
|
|
888
|
-
close(fd);
|
|
889
1048
|
|
|
890
|
-
|
|
891
|
-
|
|
1049
|
+
if (fd >= 0) {
|
|
1050
|
+
Backend_t *backend;
|
|
1051
|
+
GetBackend(self, backend);
|
|
892
1052
|
|
|
1053
|
+
VALUE resume_value = io_uring_backend_wait_fd(backend, fd, 0);
|
|
1054
|
+
close(fd);
|
|
1055
|
+
RAISE_IF_EXCEPTION(resume_value);
|
|
1056
|
+
RB_GC_GUARD(resume_value);
|
|
1057
|
+
}
|
|
1058
|
+
|
|
893
1059
|
int status;
|
|
894
1060
|
pid_t ret = waitpid(pid_int, &status, WNOHANG);
|
|
1061
|
+
if (ret < 0) {
|
|
1062
|
+
int e = errno;
|
|
1063
|
+
if (e == ECHILD)
|
|
1064
|
+
ret = pid_int;
|
|
1065
|
+
else
|
|
1066
|
+
rb_syserr_fail(e, strerror(e));
|
|
1067
|
+
}
|
|
895
1068
|
return rb_ary_new_from_args(2, INT2NUM(ret), INT2NUM(WEXITSTATUS(status)));
|
|
896
1069
|
}
|
|
897
1070
|
|
|
@@ -918,7 +1091,7 @@ VALUE Backend_kind(VALUE self) {
|
|
|
918
1091
|
}
|
|
919
1092
|
|
|
920
1093
|
void Init_Backend() {
|
|
921
|
-
VALUE cBackend = rb_define_class_under(mPolyphony, "Backend",
|
|
1094
|
+
VALUE cBackend = rb_define_class_under(mPolyphony, "Backend", rb_cObject);
|
|
922
1095
|
rb_define_alloc_func(cBackend, Backend_allocate);
|
|
923
1096
|
|
|
924
1097
|
rb_define_method(cBackend, "initialize", Backend_initialize, 0);
|
|
@@ -927,24 +1100,33 @@ void Init_Backend() {
|
|
|
927
1100
|
|
|
928
1101
|
rb_define_method(cBackend, "poll", Backend_poll, 3);
|
|
929
1102
|
rb_define_method(cBackend, "break", Backend_wakeup, 0);
|
|
1103
|
+
rb_define_method(cBackend, "kind", Backend_kind, 0);
|
|
930
1104
|
|
|
1105
|
+
rb_define_method(cBackend, "accept", Backend_accept, 2);
|
|
1106
|
+
rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
|
|
1107
|
+
rb_define_method(cBackend, "connect", Backend_connect, 3);
|
|
1108
|
+
rb_define_method(cBackend, "feed_loop", Backend_feed_loop, 3);
|
|
931
1109
|
rb_define_method(cBackend, "read", Backend_read, 4);
|
|
932
1110
|
rb_define_method(cBackend, "read_loop", Backend_read_loop, 1);
|
|
933
|
-
rb_define_method(cBackend, "write", Backend_write_m, -1);
|
|
934
1111
|
rb_define_method(cBackend, "recv", Backend_recv, 3);
|
|
1112
|
+
rb_define_method(cBackend, "recv_feed_loop", Backend_recv_feed_loop, 3);
|
|
935
1113
|
rb_define_method(cBackend, "recv_loop", Backend_recv_loop, 1);
|
|
936
|
-
rb_define_method(cBackend, "send", Backend_send,
|
|
937
|
-
rb_define_method(cBackend, "
|
|
938
|
-
rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
|
|
939
|
-
rb_define_method(cBackend, "connect", Backend_connect, 3);
|
|
940
|
-
rb_define_method(cBackend, "wait_io", Backend_wait_io, 2);
|
|
1114
|
+
rb_define_method(cBackend, "send", Backend_send, 3);
|
|
1115
|
+
rb_define_method(cBackend, "sendv", Backend_sendv, 3);
|
|
941
1116
|
rb_define_method(cBackend, "sleep", Backend_sleep, 1);
|
|
942
|
-
rb_define_method(cBackend, "
|
|
1117
|
+
rb_define_method(cBackend, "splice", Backend_splice, 3);
|
|
1118
|
+
rb_define_method(cBackend, "splice_to_eof", Backend_splice_to_eof, 3);
|
|
943
1119
|
rb_define_method(cBackend, "timeout", Backend_timeout, -1);
|
|
944
|
-
rb_define_method(cBackend, "
|
|
1120
|
+
rb_define_method(cBackend, "timer_loop", Backend_timer_loop, 1);
|
|
945
1121
|
rb_define_method(cBackend, "wait_event", Backend_wait_event, 1);
|
|
1122
|
+
rb_define_method(cBackend, "wait_io", Backend_wait_io, 2);
|
|
1123
|
+
rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
|
|
1124
|
+
rb_define_method(cBackend, "write", Backend_write_m, -1);
|
|
946
1125
|
|
|
947
|
-
|
|
1126
|
+
|
|
1127
|
+
#ifdef POLYPHONY_UNSET_NONBLOCK
|
|
1128
|
+
ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
|
|
1129
|
+
#endif
|
|
948
1130
|
|
|
949
1131
|
SYM_io_uring = ID2SYM(rb_intern("io_uring"));
|
|
950
1132
|
}
|
|
@@ -10,6 +10,7 @@ const char *op_type_to_str(enum op_type type) {
|
|
|
10
10
|
case OP_WRITE: return "WRITE";
|
|
11
11
|
case OP_RECV: return "RECV";
|
|
12
12
|
case OP_SEND: return "SEND";
|
|
13
|
+
case OP_SPLICE: return "SPLICE";
|
|
13
14
|
case OP_TIMEOUT: return "TIMEOUT";
|
|
14
15
|
case OP_POLL: return "POLL";
|
|
15
16
|
case OP_ACCEPT: return "ACCEPT";
|
|
@@ -1,13 +1,57 @@
|
|
|
1
|
+
/*
|
|
2
|
+
# Libev-based blocking ops backend for Polyphony
|
|
3
|
+
|
|
4
|
+
## Backend initialization
|
|
5
|
+
|
|
6
|
+
The backend is initialized by creating an event loop. For the main thread the
|
|
7
|
+
default event loop is used, but we since we don't need to handle any signals
|
|
8
|
+
(see the waitpid implementation below) we might as well use a non-default event
|
|
9
|
+
loop for the main thread at some time in the future.
|
|
10
|
+
|
|
11
|
+
In addition, we create an async watcher that is used for interrupting the #poll
|
|
12
|
+
method from another thread.
|
|
13
|
+
|
|
14
|
+
## Blocking operations
|
|
15
|
+
|
|
16
|
+
I/O operations start by making sure the io has been set to non-blocking
|
|
17
|
+
operation (O_NONBLOCK). That way, if the syscall would block, we'd get an
|
|
18
|
+
EWOULDBLOCK or EAGAIN instead of blocking.
|
|
19
|
+
|
|
20
|
+
Once the OS has indicated that the operation would block, we start a watcher
|
|
21
|
+
(its type corresponding to the desired operation), and call ev_xxxx_start. in We
|
|
22
|
+
then call Thread_switch_fiber and switch to another fiber while waiting for the
|
|
23
|
+
watcher to be triggered.
|
|
24
|
+
|
|
25
|
+
## Polling for events
|
|
26
|
+
|
|
27
|
+
Backend_poll is called either once the corresponding thread has no more work to
|
|
28
|
+
do (no runnable fibers) or periodically while the thread is scheduling fibers in
|
|
29
|
+
order to prevent event starvation.
|
|
30
|
+
|
|
31
|
+
## Behaviour of waitpid
|
|
32
|
+
|
|
33
|
+
On Linux 5.3+, pidfd_open will be used, otherwise a libev child watcher will be
|
|
34
|
+
used. Note that if a child watcher is used, waitpid will only work from the main
|
|
35
|
+
thread.
|
|
36
|
+
|
|
37
|
+
*/
|
|
38
|
+
|
|
1
39
|
#ifdef POLYPHONY_BACKEND_LIBEV
|
|
2
40
|
|
|
41
|
+
#ifdef POLYPHONY_LINUX
|
|
42
|
+
#define _GNU_SOURCE 1
|
|
43
|
+
#endif
|
|
44
|
+
|
|
45
|
+
#include <fcntl.h>
|
|
3
46
|
#include <netdb.h>
|
|
4
47
|
#include <sys/socket.h>
|
|
5
48
|
#include <sys/uio.h>
|
|
6
49
|
#include <unistd.h>
|
|
7
|
-
#include <fcntl.h>
|
|
8
50
|
#include <netinet/in.h>
|
|
9
51
|
#include <arpa/inet.h>
|
|
10
52
|
#include <stdnoreturn.h>
|
|
53
|
+
#include <sys/types.h>
|
|
54
|
+
#include <sys/wait.h>
|
|
11
55
|
|
|
12
56
|
#include "polyphony.h"
|
|
13
57
|
#include "../libev/ev.h"
|
|
@@ -74,17 +118,27 @@ void break_async_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, in
|
|
|
74
118
|
// of a *blocking* event loop (waking it up) in a thread-safe, signal-safe manner
|
|
75
119
|
}
|
|
76
120
|
|
|
121
|
+
inline struct ev_loop *libev_new_loop() {
|
|
122
|
+
#ifdef POLYPHONY_USE_PIDFD_OPEN
|
|
123
|
+
return ev_loop_new(EVFLAG_NOSIGMASK);
|
|
124
|
+
#else
|
|
125
|
+
int is_main_thread = (rb_thread_current() == rb_thread_main());
|
|
126
|
+
return is_main_thread ? EV_DEFAULT : ev_loop_new(EVFLAG_NOSIGMASK);
|
|
127
|
+
#endif
|
|
128
|
+
}
|
|
129
|
+
|
|
77
130
|
static VALUE Backend_initialize(VALUE self) {
|
|
78
131
|
Backend_t *backend;
|
|
79
|
-
|
|
80
|
-
int is_main_thread = (thread == rb_thread_main());
|
|
81
|
-
|
|
132
|
+
|
|
82
133
|
GetBackend(self, backend);
|
|
83
|
-
backend->ev_loop =
|
|
134
|
+
backend->ev_loop = libev_new_loop();
|
|
84
135
|
|
|
136
|
+
// start async watcher used for breaking a poll op (from another thread)
|
|
85
137
|
ev_async_init(&backend->break_async, break_async_callback);
|
|
86
138
|
ev_async_start(backend->ev_loop, &backend->break_async);
|
|
87
|
-
|
|
139
|
+
// the break_async watcher is unreferenced, in order for Backend_poll to not
|
|
140
|
+
// block when no other watcher is active
|
|
141
|
+
ev_unref(backend->ev_loop);
|
|
88
142
|
|
|
89
143
|
backend->currently_polling = 0;
|
|
90
144
|
backend->pending_count = 0;
|
|
@@ -332,6 +386,58 @@ error:
|
|
|
332
386
|
return RAISE_EXCEPTION(switchpoint_result);
|
|
333
387
|
}
|
|
334
388
|
|
|
389
|
+
VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
|
|
390
|
+
Backend_t *backend;
|
|
391
|
+
struct libev_io watcher;
|
|
392
|
+
rb_io_t *fptr;
|
|
393
|
+
VALUE str;
|
|
394
|
+
long total;
|
|
395
|
+
long len = 8192;
|
|
396
|
+
int shrinkable;
|
|
397
|
+
char *buf;
|
|
398
|
+
VALUE switchpoint_result = Qnil;
|
|
399
|
+
VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
|
|
400
|
+
ID method_id = SYM2ID(method);
|
|
401
|
+
|
|
402
|
+
READ_LOOP_PREPARE_STR();
|
|
403
|
+
|
|
404
|
+
GetBackend(self, backend);
|
|
405
|
+
if (underlying_io != Qnil) io = underlying_io;
|
|
406
|
+
GetOpenFile(io, fptr);
|
|
407
|
+
rb_io_check_byte_readable(fptr);
|
|
408
|
+
io_set_nonblock(fptr, io);
|
|
409
|
+
rectify_io_file_pos(fptr);
|
|
410
|
+
watcher.fiber = Qnil;
|
|
411
|
+
|
|
412
|
+
while (1) {
|
|
413
|
+
ssize_t n = read(fptr->fd, buf, len);
|
|
414
|
+
if (n < 0) {
|
|
415
|
+
int e = errno;
|
|
416
|
+
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
|
417
|
+
|
|
418
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
|
|
419
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
switchpoint_result = backend_snooze();
|
|
423
|
+
|
|
424
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
425
|
+
|
|
426
|
+
if (n == 0) break; // EOF
|
|
427
|
+
total = n;
|
|
428
|
+
READ_LOOP_PASS_STR_TO_RECEIVER(receiver, method_id);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
RB_GC_GUARD(str);
|
|
433
|
+
RB_GC_GUARD(watcher.fiber);
|
|
434
|
+
RB_GC_GUARD(switchpoint_result);
|
|
435
|
+
|
|
436
|
+
return io;
|
|
437
|
+
error:
|
|
438
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
|
439
|
+
}
|
|
440
|
+
|
|
335
441
|
VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
|
|
336
442
|
Backend_t *backend;
|
|
337
443
|
struct libev_io watcher;
|
|
@@ -618,6 +724,173 @@ error:
|
|
|
618
724
|
return RAISE_EXCEPTION(switchpoint_result);
|
|
619
725
|
}
|
|
620
726
|
|
|
727
|
+
VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
|
|
728
|
+
Backend_t *backend;
|
|
729
|
+
struct libev_io watcher;
|
|
730
|
+
rb_io_t *fptr;
|
|
731
|
+
VALUE switchpoint_result = Qnil;
|
|
732
|
+
VALUE underlying_io;
|
|
733
|
+
char *buf = StringValuePtr(str);
|
|
734
|
+
long len = RSTRING_LEN(str);
|
|
735
|
+
long left = len;
|
|
736
|
+
int flags_int = NUM2INT(flags);
|
|
737
|
+
|
|
738
|
+
underlying_io = rb_ivar_get(io, ID_ivar_io);
|
|
739
|
+
if (underlying_io != Qnil) io = underlying_io;
|
|
740
|
+
GetBackend(self, backend);
|
|
741
|
+
io = rb_io_get_write_io(io);
|
|
742
|
+
GetOpenFile(io, fptr);
|
|
743
|
+
io_set_nonblock(fptr, io);
|
|
744
|
+
watcher.fiber = Qnil;
|
|
745
|
+
|
|
746
|
+
while (left > 0) {
|
|
747
|
+
ssize_t n = send(fptr->fd, buf, left, flags_int);
|
|
748
|
+
if (n < 0) {
|
|
749
|
+
int e = errno;
|
|
750
|
+
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
|
751
|
+
|
|
752
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_WRITE);
|
|
753
|
+
|
|
754
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
755
|
+
}
|
|
756
|
+
else {
|
|
757
|
+
buf += n;
|
|
758
|
+
left -= n;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
if (watcher.fiber == Qnil) {
|
|
763
|
+
switchpoint_result = backend_snooze();
|
|
764
|
+
|
|
765
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
RB_GC_GUARD(watcher.fiber);
|
|
769
|
+
RB_GC_GUARD(switchpoint_result);
|
|
770
|
+
|
|
771
|
+
return INT2NUM(len);
|
|
772
|
+
error:
|
|
773
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
777
|
+
Backend_t *backend;
|
|
778
|
+
struct libev_io watcher;
|
|
779
|
+
VALUE switchpoint_result = Qnil;
|
|
780
|
+
VALUE underlying_io;
|
|
781
|
+
rb_io_t *src_fptr;
|
|
782
|
+
rb_io_t *dest_fptr;
|
|
783
|
+
int len;
|
|
784
|
+
|
|
785
|
+
#ifndef POLYPHONY_LINUX
|
|
786
|
+
rb_raise(rb_eRuntimeError, "splice not supported");
|
|
787
|
+
#endif
|
|
788
|
+
|
|
789
|
+
GetBackend(self, backend);
|
|
790
|
+
|
|
791
|
+
underlying_io = rb_ivar_get(src, ID_ivar_io);
|
|
792
|
+
if (underlying_io != Qnil) src = underlying_io;
|
|
793
|
+
GetOpenFile(src, src_fptr);
|
|
794
|
+
io_set_nonblock(src_fptr, src);
|
|
795
|
+
|
|
796
|
+
underlying_io = rb_ivar_get(dest, ID_ivar_io);
|
|
797
|
+
if (underlying_io != Qnil) dest = underlying_io;
|
|
798
|
+
dest = rb_io_get_write_io(dest);
|
|
799
|
+
GetOpenFile(dest, dest_fptr);
|
|
800
|
+
io_set_nonblock(dest_fptr, dest);
|
|
801
|
+
|
|
802
|
+
watcher.fiber = Qnil;
|
|
803
|
+
while (1) {
|
|
804
|
+
len = splice(src_fptr->fd, 0, dest_fptr->fd, 0, NUM2INT(maxlen), 0);
|
|
805
|
+
if (len < 0) {
|
|
806
|
+
int e = errno;
|
|
807
|
+
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
|
808
|
+
|
|
809
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, src_fptr->fd, &watcher, EV_READ);
|
|
810
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
811
|
+
|
|
812
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, dest_fptr->fd, &watcher, EV_WRITE);
|
|
813
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
814
|
+
}
|
|
815
|
+
else {
|
|
816
|
+
break;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
if (watcher.fiber == Qnil) {
|
|
821
|
+
switchpoint_result = backend_snooze();
|
|
822
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
RB_GC_GUARD(watcher.fiber);
|
|
826
|
+
RB_GC_GUARD(switchpoint_result);
|
|
827
|
+
|
|
828
|
+
return INT2NUM(len);
|
|
829
|
+
error:
|
|
830
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
834
|
+
Backend_t *backend;
|
|
835
|
+
struct libev_io watcher;
|
|
836
|
+
VALUE switchpoint_result = Qnil;
|
|
837
|
+
VALUE underlying_io;
|
|
838
|
+
rb_io_t *src_fptr;
|
|
839
|
+
rb_io_t *dest_fptr;
|
|
840
|
+
int len;
|
|
841
|
+
int total = 0;
|
|
842
|
+
|
|
843
|
+
#ifndef POLYPHONY_LINUX
|
|
844
|
+
rb_raise(rb_eRuntimeError, "splice not supported");
|
|
845
|
+
#endif
|
|
846
|
+
|
|
847
|
+
GetBackend(self, backend);
|
|
848
|
+
|
|
849
|
+
underlying_io = rb_ivar_get(src, ID_ivar_io);
|
|
850
|
+
if (underlying_io != Qnil) src = underlying_io;
|
|
851
|
+
GetOpenFile(src, src_fptr);
|
|
852
|
+
io_set_nonblock(src_fptr, src);
|
|
853
|
+
|
|
854
|
+
underlying_io = rb_ivar_get(dest, ID_ivar_io);
|
|
855
|
+
if (underlying_io != Qnil) dest = underlying_io;
|
|
856
|
+
dest = rb_io_get_write_io(dest);
|
|
857
|
+
GetOpenFile(dest, dest_fptr);
|
|
858
|
+
io_set_nonblock(dest_fptr, dest);
|
|
859
|
+
|
|
860
|
+
watcher.fiber = Qnil;
|
|
861
|
+
while (1) {
|
|
862
|
+
len = splice(src_fptr->fd, 0, dest_fptr->fd, 0, NUM2INT(maxlen), 0);
|
|
863
|
+
if (len < 0) {
|
|
864
|
+
int e = errno;
|
|
865
|
+
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
|
866
|
+
|
|
867
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, src_fptr->fd, &watcher, EV_READ);
|
|
868
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
869
|
+
|
|
870
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, dest_fptr->fd, &watcher, EV_WRITE);
|
|
871
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
872
|
+
}
|
|
873
|
+
else if (len == 0) {
|
|
874
|
+
break;
|
|
875
|
+
}
|
|
876
|
+
else {
|
|
877
|
+
total += len;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
if (watcher.fiber == Qnil) {
|
|
882
|
+
switchpoint_result = backend_snooze();
|
|
883
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
RB_GC_GUARD(watcher.fiber);
|
|
887
|
+
RB_GC_GUARD(switchpoint_result);
|
|
888
|
+
|
|
889
|
+
return INT2NUM(total);
|
|
890
|
+
error:
|
|
891
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
|
892
|
+
}
|
|
893
|
+
|
|
621
894
|
VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
|
|
622
895
|
Backend_t *backend;
|
|
623
896
|
rb_io_t *fptr;
|
|
@@ -685,26 +958,12 @@ noreturn VALUE Backend_timer_loop(VALUE self, VALUE interval) {
|
|
|
685
958
|
RB_GC_GUARD(switchpoint_result);
|
|
686
959
|
|
|
687
960
|
rb_yield(Qnil);
|
|
688
|
-
|
|
689
|
-
while (1) {
|
|
961
|
+
do {
|
|
690
962
|
next_time += interval_d;
|
|
691
|
-
|
|
692
|
-
}
|
|
963
|
+
} while (next_time <= now);
|
|
693
964
|
}
|
|
694
965
|
}
|
|
695
966
|
|
|
696
|
-
VALUE Backend_timeout_safe(VALUE arg) {
|
|
697
|
-
return rb_yield(arg);
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
VALUE Backend_timeout_rescue(VALUE arg, VALUE exception) {
|
|
701
|
-
return exception;
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
VALUE Backend_timeout_ensure_safe(VALUE arg) {
|
|
705
|
-
return rb_rescue2(Backend_timeout_safe, Qnil, Backend_timeout_rescue, Qnil, rb_eException, (VALUE)0);
|
|
706
|
-
}
|
|
707
|
-
|
|
708
967
|
struct libev_timeout {
|
|
709
968
|
struct ev_timer timer;
|
|
710
969
|
VALUE fiber;
|
|
@@ -759,13 +1018,39 @@ VALUE Backend_timeout(int argc,VALUE *argv, VALUE self) {
|
|
|
759
1018
|
return result;
|
|
760
1019
|
}
|
|
761
1020
|
|
|
1021
|
+
#ifdef POLYPHONY_USE_PIDFD_OPEN
|
|
1022
|
+
VALUE Backend_waitpid(VALUE self, VALUE pid) {
|
|
1023
|
+
int pid_int = NUM2INT(pid);
|
|
1024
|
+
int fd = pidfd_open(pid_int, 0);
|
|
1025
|
+
if (fd >= 0) {
|
|
1026
|
+
Backend_t *backend;
|
|
1027
|
+
GetBackend(self, backend);
|
|
1028
|
+
|
|
1029
|
+
VALUE resume_value = libev_wait_fd(backend, fd, EV_READ, 0);
|
|
1030
|
+
close(fd);
|
|
1031
|
+
RAISE_IF_EXCEPTION(resume_value);
|
|
1032
|
+
RB_GC_GUARD(resume_value);
|
|
1033
|
+
}
|
|
1034
|
+
else {
|
|
1035
|
+
int e = errno;
|
|
1036
|
+
rb_syserr_fail(e, strerror(e));
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
int status = 0;
|
|
1040
|
+
pid_t ret = waitpid(pid_int, &status, WNOHANG);
|
|
1041
|
+
if (ret < 0) {
|
|
1042
|
+
int e = errno;
|
|
1043
|
+
rb_syserr_fail(e, strerror(e));
|
|
1044
|
+
}
|
|
1045
|
+
return rb_ary_new_from_args(2, INT2NUM(ret), INT2NUM(WEXITSTATUS(status)));
|
|
1046
|
+
}
|
|
1047
|
+
#else
|
|
762
1048
|
struct libev_child {
|
|
763
1049
|
struct ev_child child;
|
|
764
1050
|
VALUE fiber;
|
|
765
1051
|
};
|
|
766
1052
|
|
|
767
|
-
void Backend_child_callback(EV_P_ ev_child *w, int revents)
|
|
768
|
-
{
|
|
1053
|
+
void Backend_child_callback(EV_P_ ev_child *w, int revents) {
|
|
769
1054
|
struct libev_child *watcher = (struct libev_child *)w;
|
|
770
1055
|
int exit_status = WEXITSTATUS(w->rstatus);
|
|
771
1056
|
VALUE status;
|
|
@@ -792,6 +1077,7 @@ VALUE Backend_waitpid(VALUE self, VALUE pid) {
|
|
|
792
1077
|
RB_GC_GUARD(switchpoint_result);
|
|
793
1078
|
return switchpoint_result;
|
|
794
1079
|
}
|
|
1080
|
+
#endif
|
|
795
1081
|
|
|
796
1082
|
void Backend_async_callback(EV_P_ ev_async *w, int revents) { }
|
|
797
1083
|
|
|
@@ -820,7 +1106,7 @@ VALUE Backend_kind(VALUE self) {
|
|
|
820
1106
|
void Init_Backend() {
|
|
821
1107
|
ev_set_allocator(xrealloc);
|
|
822
1108
|
|
|
823
|
-
VALUE cBackend = rb_define_class_under(mPolyphony, "Backend",
|
|
1109
|
+
VALUE cBackend = rb_define_class_under(mPolyphony, "Backend", rb_cObject);
|
|
824
1110
|
rb_define_alloc_func(cBackend, Backend_allocate);
|
|
825
1111
|
|
|
826
1112
|
rb_define_method(cBackend, "initialize", Backend_initialize, 0);
|
|
@@ -829,24 +1115,26 @@ void Init_Backend() {
|
|
|
829
1115
|
|
|
830
1116
|
rb_define_method(cBackend, "poll", Backend_poll, 3);
|
|
831
1117
|
rb_define_method(cBackend, "break", Backend_wakeup, 0);
|
|
1118
|
+
rb_define_method(cBackend, "kind", Backend_kind, 0);
|
|
832
1119
|
|
|
833
|
-
rb_define_method(cBackend, "read", Backend_read, 4);
|
|
834
|
-
rb_define_method(cBackend, "read_loop", Backend_read_loop, 1);
|
|
835
|
-
rb_define_method(cBackend, "write", Backend_write_m, -1);
|
|
836
1120
|
rb_define_method(cBackend, "accept", Backend_accept, 2);
|
|
837
1121
|
rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
|
|
838
1122
|
rb_define_method(cBackend, "connect", Backend_connect, 3);
|
|
1123
|
+
rb_define_method(cBackend, "feed_loop", Backend_feed_loop, 3);
|
|
1124
|
+
rb_define_method(cBackend, "read", Backend_read, 4);
|
|
1125
|
+
rb_define_method(cBackend, "read_loop", Backend_read_loop, 1);
|
|
839
1126
|
rb_define_method(cBackend, "recv", Backend_recv, 3);
|
|
840
1127
|
rb_define_method(cBackend, "recv_loop", Backend_read_loop, 1);
|
|
841
|
-
rb_define_method(cBackend, "
|
|
842
|
-
rb_define_method(cBackend, "
|
|
1128
|
+
rb_define_method(cBackend, "recv_feed_loop", Backend_feed_loop, 3);
|
|
1129
|
+
rb_define_method(cBackend, "send", Backend_send, 3);
|
|
1130
|
+
rb_define_method(cBackend, "sendv", Backend_sendv, 3);
|
|
843
1131
|
rb_define_method(cBackend, "sleep", Backend_sleep, 1);
|
|
844
|
-
rb_define_method(cBackend, "timer_loop", Backend_timer_loop, 1);
|
|
845
1132
|
rb_define_method(cBackend, "timeout", Backend_timeout, -1);
|
|
846
|
-
rb_define_method(cBackend, "
|
|
1133
|
+
rb_define_method(cBackend, "timer_loop", Backend_timer_loop, 1);
|
|
847
1134
|
rb_define_method(cBackend, "wait_event", Backend_wait_event, 1);
|
|
848
|
-
|
|
849
|
-
rb_define_method(cBackend, "
|
|
1135
|
+
rb_define_method(cBackend, "wait_io", Backend_wait_io, 2);
|
|
1136
|
+
rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
|
|
1137
|
+
rb_define_method(cBackend, "write", Backend_write_m, -1);
|
|
850
1138
|
|
|
851
1139
|
ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
|
|
852
1140
|
SYM_libev = ID2SYM(rb_intern("libev"));
|