polyphony 0.49.0 → 0.51.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/CHANGELOG.md +31 -0
- data/Gemfile.lock +3 -1
- data/TODO.md +6 -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/performance/thread-vs-fiber/polyphony_server.rb +2 -4
- data/ext/polyphony/backend_common.h +29 -2
- data/ext/polyphony/backend_io_uring.c +111 -32
- data/ext/polyphony/backend_libev.c +79 -46
- data/ext/polyphony/fiber.c +2 -1
- data/ext/polyphony/polyphony.h +1 -0
- data/ext/polyphony/runqueue.c +6 -0
- data/ext/polyphony/runqueue_ring_buffer.c +9 -0
- data/ext/polyphony/runqueue_ring_buffer.h +1 -0
- data/ext/polyphony/thread.c +14 -0
- data/lib/polyphony.rb +1 -1
- data/lib/polyphony/adapters/process.rb +2 -0
- data/lib/polyphony/core/exceptions.rb +1 -0
- data/lib/polyphony/core/global_api.rb +1 -1
- data/lib/polyphony/core/timer.rb +63 -20
- data/lib/polyphony/extensions/core.rb +4 -4
- data/lib/polyphony/extensions/fiber.rb +11 -8
- data/lib/polyphony/extensions/io.rb +4 -0
- data/lib/polyphony/extensions/socket.rb +12 -0
- data/lib/polyphony/extensions/thread.rb +1 -2
- data/lib/polyphony/net.rb +3 -6
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +1 -0
- data/test/helper.rb +1 -2
- data/test/test_backend.rb +25 -0
- data/test/test_fiber.rb +31 -0
- data/test/test_io.rb +53 -1
- data/test/test_signal.rb +1 -2
- data/test/test_socket.rb +34 -0
- data/test/test_timer.rb +46 -11
- metadata +19 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 72f11d867863c51f1fc1ab3ec85fe575cca9ee53a5047f4d3f7b76cea3b650b7
|
4
|
+
data.tar.gz: c29354b2de2f207185fc06fd86421b53d328aaf67d611109a6f95e9130ac5967
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 93351a3c4007145ff317257f82b764708b445e94b848d31f783f4d72071b097a8309322c4bedfdb7f321b0613ef436a314ad4e650928d0855143d3a5b98d9c13
|
7
|
+
data.tar.gz: 9119039548264867fa012bcbb90999f4ad82170c28539d43b0145687a761d508630da538ac87b75f9bf32f6b33cb0e13c22f50143248b85cc90289d5d3451d3f
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,34 @@
|
|
1
|
+
## 0.51.0
|
2
|
+
|
3
|
+
- Implement `IO#feed_loop`, `Socket#feed_loop`
|
4
|
+
- Fix error handling in `Process.kill_and_await`
|
5
|
+
|
6
|
+
## 0.50.1
|
7
|
+
|
8
|
+
- Set `IOSQE_ASYNC` flag in io_uring backend
|
9
|
+
- Fix error handling in `Backend#waitpid`
|
10
|
+
- Reimplement libev backend's `#waitpid` by using pidfd_open (in similar manner
|
11
|
+
to the io_uring backend)
|
12
|
+
|
13
|
+
## 0.50.0
|
14
|
+
|
15
|
+
- Use `Process::CLOCK_MONOTONIC` in Timer
|
16
|
+
- Add `Timer#sleep`, `Timer#after`, `Timer#every`
|
17
|
+
- Prevent fiber from being resumed after terminating
|
18
|
+
- Add `Thread#fiber_index_of` method
|
19
|
+
- Use `Backend#wait_event` in `Fiber#await`
|
20
|
+
|
21
|
+
## 0.49.2
|
22
|
+
|
23
|
+
- Fix hang with 100s or more child fibers when terminating
|
24
|
+
- Fix double pending_count increment in io_uring backend
|
25
|
+
|
26
|
+
## 0.49.1
|
27
|
+
|
28
|
+
- Use `TCPSocket` instead of `Socket` in `Net.tcp_connect`
|
29
|
+
- Catch `Errno::ERSCH` in `Process.kill_and_await`
|
30
|
+
- Set io_uring queue size to 2048
|
31
|
+
|
1
32
|
## 0.49.0
|
2
33
|
|
3
34
|
- Implement `Polyphony::Timer` for performant timeouts
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
polyphony (0.
|
4
|
+
polyphony (0.51.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -72,6 +72,7 @@ GEM
|
|
72
72
|
builder
|
73
73
|
minitest (>= 5.0)
|
74
74
|
ruby-progressbar
|
75
|
+
msgpack (1.4.2)
|
75
76
|
multi_xml (0.6.0)
|
76
77
|
mysql2 (0.5.3)
|
77
78
|
parallel (1.19.1)
|
@@ -136,6 +137,7 @@ DEPENDENCIES
|
|
136
137
|
just-the-docs (~> 0.3.0)
|
137
138
|
minitest (= 5.13.0)
|
138
139
|
minitest-reporters (= 1.4.2)
|
140
|
+
msgpack (= 1.4.2)
|
139
141
|
mysql2 (= 0.5.3)
|
140
142
|
pg (= 1.1.4)
|
141
143
|
polyphony!
|
data/TODO.md
CHANGED
@@ -1,6 +1,10 @@
|
|
1
|
-
- Check segfault when
|
1
|
+
- Check segfault when resetting a `cancel_after` timeout lots of times at very high rate
|
2
|
+
- Check why `throttled_loop` inside of `move_on_after` fails to stop
|
2
3
|
|
3
|
-
-
|
4
|
+
- Commented out `io_uring_sqe_set_flags(sqe, IOSQE_ASYNC);` in `io_uring_backend_defer_submit_and_await`:
|
5
|
+
- This flag should be set for I/O ops, not for other stuff
|
6
|
+
|
7
|
+
- Override stock `::SizedQueue` impl with Queue with capacity
|
4
8
|
|
5
9
|
- Add support for `break` and `StopIteration` in all loops (with tests)
|
6
10
|
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
|
6
|
+
def process
|
7
|
+
p :b_start
|
8
|
+
sleep 1
|
9
|
+
p :b_stop
|
10
|
+
end
|
11
|
+
|
12
|
+
spin do
|
13
|
+
p :a_start
|
14
|
+
spin { process }
|
15
|
+
sleep 60
|
16
|
+
p :a_stop
|
17
|
+
end
|
18
|
+
|
19
|
+
p :main_start
|
20
|
+
sleep 120
|
21
|
+
p :main_stop
|
@@ -26,15 +26,13 @@ def write_response(socket)
|
|
26
26
|
socket.write "HTTP/1.1 #{status_code}\r\n#{headers}\r\n#{data}"
|
27
27
|
end
|
28
28
|
|
29
|
-
server = TCPServer.open('0.0.0.0',
|
30
|
-
puts "pid #{Process.pid} Polyphony (#{Thread.current.backend.kind}) listening on port
|
29
|
+
server = TCPServer.open('0.0.0.0', 4411)
|
30
|
+
puts "pid #{Process.pid} Polyphony (#{Thread.current.backend.kind}) listening on port 4411"
|
31
31
|
|
32
32
|
spin_loop(interval: 10) do
|
33
33
|
p Thread.current.fiber_scheduling_stats
|
34
34
|
end
|
35
35
|
|
36
|
-
GC.disable
|
37
|
-
|
38
36
|
server.accept_loop do |c|
|
39
37
|
spin { handle_client(c) }
|
40
38
|
end
|
@@ -3,6 +3,14 @@
|
|
3
3
|
#include "ruby.h"
|
4
4
|
#include "ruby/io.h"
|
5
5
|
|
6
|
+
#ifndef __NR_pidfd_open
|
7
|
+
#define __NR_pidfd_open 434 /* System call # on most architectures */
|
8
|
+
#endif
|
9
|
+
|
10
|
+
static int pidfd_open(pid_t pid, unsigned int flags) {
|
11
|
+
return syscall(__NR_pidfd_open, pid, flags);
|
12
|
+
}
|
13
|
+
|
6
14
|
//////////////////////////////////////////////////////////////////////
|
7
15
|
//////////////////////////////////////////////////////////////////////
|
8
16
|
// the following is copied verbatim from the Ruby source code (io.c)
|
@@ -99,6 +107,13 @@ inline VALUE backend_snooze() {
|
|
99
107
|
READ_LOOP_PREPARE_STR(); \
|
100
108
|
}
|
101
109
|
|
110
|
+
#define READ_LOOP_PASS_STR_TO_RECEIVER(receiver, method_id) { \
|
111
|
+
io_set_read_length(str, total, shrinkable); \
|
112
|
+
io_enc_str(str, fptr); \
|
113
|
+
rb_funcall_passing_block(receiver, method_id, 1, &str); \
|
114
|
+
READ_LOOP_PREPARE_STR(); \
|
115
|
+
}
|
116
|
+
|
102
117
|
inline void rectify_io_file_pos(rb_io_t *fptr) {
|
103
118
|
// Apparently after reopening a closed file, the file position is not reset,
|
104
119
|
// which causes the read to fail. Fortunately we can use fptr->rbuf.len to
|
@@ -114,7 +129,7 @@ inline double current_time() {
|
|
114
129
|
struct timespec ts;
|
115
130
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
116
131
|
long long ns = ts.tv_sec;
|
117
|
-
ns = ns *
|
132
|
+
ns = ns * 1e9 + ts.tv_nsec;
|
118
133
|
double t = ns;
|
119
134
|
return t / 1e9;
|
120
135
|
}
|
@@ -126,4 +141,16 @@ inline VALUE backend_timeout_exception(VALUE exception) {
|
|
126
141
|
return rb_funcall(exception, ID_new, 0);
|
127
142
|
else
|
128
143
|
return rb_funcall(rb_eRuntimeError, ID_new, 1, exception);
|
129
|
-
}
|
144
|
+
}
|
145
|
+
|
146
|
+
VALUE Backend_timeout_safe(VALUE arg) {
|
147
|
+
return rb_yield(arg);
|
148
|
+
}
|
149
|
+
|
150
|
+
VALUE Backend_timeout_rescue(VALUE arg, VALUE exception) {
|
151
|
+
return exception;
|
152
|
+
}
|
153
|
+
|
154
|
+
VALUE Backend_timeout_ensure_safe(VALUE arg) {
|
155
|
+
return rb_rescue2(Backend_timeout_safe, Qnil, Backend_timeout_rescue, Qnil, rb_eException, (VALUE)0);
|
156
|
+
}
|
@@ -19,14 +19,6 @@
|
|
19
19
|
#include "ruby/thread.h"
|
20
20
|
#include "backend_io_uring_context.h"
|
21
21
|
|
22
|
-
#ifndef __NR_pidfd_open
|
23
|
-
#define __NR_pidfd_open 434 /* System call # on most architectures */
|
24
|
-
#endif
|
25
|
-
|
26
|
-
static int pidfd_open(pid_t pid, unsigned int flags) {
|
27
|
-
return syscall(__NR_pidfd_open, pid, flags);
|
28
|
-
}
|
29
|
-
|
30
22
|
VALUE SYM_io_uring;
|
31
23
|
|
32
24
|
typedef struct Backend_t {
|
@@ -72,7 +64,7 @@ static VALUE Backend_initialize(VALUE self) {
|
|
72
64
|
backend->pending_count = 0;
|
73
65
|
backend->poll_no_wait_count = 0;
|
74
66
|
backend->pending_sqes = 0;
|
75
|
-
backend->prepared_limit =
|
67
|
+
backend->prepared_limit = 2048;
|
76
68
|
|
77
69
|
context_store_initialize(&backend->store);
|
78
70
|
io_uring_queue_init(backend->prepared_limit, &backend->ring, 0);
|
@@ -260,12 +252,10 @@ int io_uring_backend_defer_submit_and_await(
|
|
260
252
|
VALUE switchpoint_result = Qnil;
|
261
253
|
|
262
254
|
io_uring_sqe_set_data(sqe, ctx);
|
263
|
-
|
255
|
+
io_uring_sqe_set_flags(sqe, IOSQE_ASYNC);
|
264
256
|
io_uring_backend_defer_submit(backend);
|
265
257
|
|
266
|
-
backend->pending_count++;
|
267
258
|
switchpoint_result = backend_await(backend);
|
268
|
-
backend->pending_count--;
|
269
259
|
|
270
260
|
if (!ctx->completed) {
|
271
261
|
ctx->result = -ECANCELED;
|
@@ -399,6 +389,52 @@ VALUE Backend_read_loop(VALUE self, VALUE io) {
|
|
399
389
|
return io;
|
400
390
|
}
|
401
391
|
|
392
|
+
VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
|
393
|
+
Backend_t *backend;
|
394
|
+
rb_io_t *fptr;
|
395
|
+
VALUE str;
|
396
|
+
long total;
|
397
|
+
long len = 8192;
|
398
|
+
int shrinkable;
|
399
|
+
char *buf;
|
400
|
+
VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
|
401
|
+
ID method_id = SYM2ID(method);
|
402
|
+
|
403
|
+
READ_LOOP_PREPARE_STR();
|
404
|
+
|
405
|
+
GetBackend(self, backend);
|
406
|
+
if (underlying_io != Qnil) io = underlying_io;
|
407
|
+
GetOpenFile(io, fptr);
|
408
|
+
rb_io_check_byte_readable(fptr);
|
409
|
+
rectify_io_file_pos(fptr);
|
410
|
+
|
411
|
+
while (1) {
|
412
|
+
VALUE resume_value = Qnil;
|
413
|
+
op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_READ);
|
414
|
+
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
415
|
+
io_uring_prep_read(sqe, fptr->fd, buf, len, -1);
|
416
|
+
|
417
|
+
ssize_t result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
418
|
+
OP_CONTEXT_RELEASE(&backend->store, ctx);
|
419
|
+
RAISE_IF_EXCEPTION(resume_value);
|
420
|
+
if (!ctx->completed) return resume_value;
|
421
|
+
RB_GC_GUARD(resume_value);
|
422
|
+
|
423
|
+
if (result < 0)
|
424
|
+
rb_syserr_fail(-result, strerror(-result));
|
425
|
+
else if (!result)
|
426
|
+
break; // EOF
|
427
|
+
else {
|
428
|
+
total = result;
|
429
|
+
READ_LOOP_PASS_STR_TO_RECEIVER(receiver, method_id);
|
430
|
+
}
|
431
|
+
}
|
432
|
+
|
433
|
+
RB_GC_GUARD(str);
|
434
|
+
|
435
|
+
return io;
|
436
|
+
}
|
437
|
+
|
402
438
|
VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
|
403
439
|
Backend_t *backend;
|
404
440
|
rb_io_t *fptr;
|
@@ -606,6 +642,51 @@ VALUE Backend_recv_loop(VALUE self, VALUE io) {
|
|
606
642
|
return io;
|
607
643
|
}
|
608
644
|
|
645
|
+
VALUE Backend_recv_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
|
646
|
+
Backend_t *backend;
|
647
|
+
rb_io_t *fptr;
|
648
|
+
VALUE str;
|
649
|
+
long total;
|
650
|
+
long len = 8192;
|
651
|
+
int shrinkable;
|
652
|
+
char *buf;
|
653
|
+
VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
|
654
|
+
ID method_id = SYM2ID(method);
|
655
|
+
|
656
|
+
READ_LOOP_PREPARE_STR();
|
657
|
+
|
658
|
+
GetBackend(self, backend);
|
659
|
+
if (underlying_io != Qnil) io = underlying_io;
|
660
|
+
GetOpenFile(io, fptr);
|
661
|
+
rb_io_check_byte_readable(fptr);
|
662
|
+
rectify_io_file_pos(fptr);
|
663
|
+
|
664
|
+
while (1) {
|
665
|
+
VALUE resume_value = Qnil;
|
666
|
+
op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_RECV);
|
667
|
+
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
668
|
+
io_uring_prep_recv(sqe, fptr->fd, buf, len, 0);
|
669
|
+
|
670
|
+
int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
671
|
+
OP_CONTEXT_RELEASE(&backend->store, ctx);
|
672
|
+
RAISE_IF_EXCEPTION(resume_value);
|
673
|
+
if (!ctx->completed) return resume_value;
|
674
|
+
RB_GC_GUARD(resume_value);
|
675
|
+
|
676
|
+
if (result < 0)
|
677
|
+
rb_syserr_fail(-result, strerror(-result));
|
678
|
+
else if (!result)
|
679
|
+
break; // EOF
|
680
|
+
else {
|
681
|
+
total = result;
|
682
|
+
READ_LOOP_PASS_STR_TO_RECEIVER(receiver, method_id);
|
683
|
+
}
|
684
|
+
}
|
685
|
+
|
686
|
+
RB_GC_GUARD(str);
|
687
|
+
return io;
|
688
|
+
}
|
689
|
+
|
609
690
|
VALUE Backend_send(VALUE self, VALUE io, VALUE str) {
|
610
691
|
Backend_t *backend;
|
611
692
|
rb_io_t *fptr;
|
@@ -813,18 +894,6 @@ VALUE Backend_timer_loop(VALUE self, VALUE interval) {
|
|
813
894
|
}
|
814
895
|
}
|
815
896
|
|
816
|
-
VALUE Backend_timeout_safe(VALUE arg) {
|
817
|
-
return rb_yield(arg);
|
818
|
-
}
|
819
|
-
|
820
|
-
VALUE Backend_timeout_rescue(VALUE arg, VALUE exception) {
|
821
|
-
return exception;
|
822
|
-
}
|
823
|
-
|
824
|
-
VALUE Backend_timeout_ensure_safe(VALUE arg) {
|
825
|
-
return rb_rescue2(Backend_timeout_safe, Qnil, Backend_timeout_rescue, Qnil, rb_eException, (VALUE)0);
|
826
|
-
}
|
827
|
-
|
828
897
|
struct Backend_timeout_ctx {
|
829
898
|
Backend_t *backend;
|
830
899
|
op_context_t *ctx;
|
@@ -863,7 +932,6 @@ VALUE Backend_timeout(int argc, VALUE *argv, VALUE self) {
|
|
863
932
|
ctx->resume_value = timeout;
|
864
933
|
io_uring_prep_timeout(sqe, &ts, 0, 0);
|
865
934
|
io_uring_sqe_set_data(sqe, ctx);
|
866
|
-
io_uring_sqe_set_flags(sqe, IOSQE_ASYNC);
|
867
935
|
io_uring_backend_defer_submit(backend);
|
868
936
|
|
869
937
|
struct Backend_timeout_ctx timeout_ctx = {backend, ctx};
|
@@ -881,19 +949,28 @@ VALUE Backend_timeout(int argc, VALUE *argv, VALUE self) {
|
|
881
949
|
}
|
882
950
|
|
883
951
|
VALUE Backend_waitpid(VALUE self, VALUE pid) {
|
884
|
-
Backend_t *backend;
|
885
952
|
int pid_int = NUM2INT(pid);
|
886
953
|
int fd = pidfd_open(pid_int, 0);
|
887
|
-
GetBackend(self, backend);
|
888
|
-
|
889
|
-
VALUE resume_value = io_uring_backend_wait_fd(backend, fd, 0);
|
890
|
-
close(fd);
|
891
954
|
|
892
|
-
|
893
|
-
|
955
|
+
if (fd >= 0) {
|
956
|
+
Backend_t *backend;
|
957
|
+
GetBackend(self, backend);
|
894
958
|
|
959
|
+
VALUE resume_value = io_uring_backend_wait_fd(backend, fd, 0);
|
960
|
+
close(fd);
|
961
|
+
RAISE_IF_EXCEPTION(resume_value);
|
962
|
+
RB_GC_GUARD(resume_value);
|
963
|
+
}
|
964
|
+
|
895
965
|
int status;
|
896
966
|
pid_t ret = waitpid(pid_int, &status, WNOHANG);
|
967
|
+
if (ret < 0) {
|
968
|
+
int e = errno;
|
969
|
+
if (e == ECHILD)
|
970
|
+
ret = pid_int;
|
971
|
+
else
|
972
|
+
rb_syserr_fail(e, strerror(e));
|
973
|
+
}
|
897
974
|
return rb_ary_new_from_args(2, INT2NUM(ret), INT2NUM(WEXITSTATUS(status)));
|
898
975
|
}
|
899
976
|
|
@@ -932,9 +1009,11 @@ void Init_Backend() {
|
|
932
1009
|
|
933
1010
|
rb_define_method(cBackend, "read", Backend_read, 4);
|
934
1011
|
rb_define_method(cBackend, "read_loop", Backend_read_loop, 1);
|
1012
|
+
rb_define_method(cBackend, "feed_loop", Backend_feed_loop, 3);
|
935
1013
|
rb_define_method(cBackend, "write", Backend_write_m, -1);
|
936
1014
|
rb_define_method(cBackend, "recv", Backend_recv, 3);
|
937
1015
|
rb_define_method(cBackend, "recv_loop", Backend_recv_loop, 1);
|
1016
|
+
rb_define_method(cBackend, "recv_feed_loop", Backend_recv_feed_loop, 3);
|
938
1017
|
rb_define_method(cBackend, "send", Backend_send, 2);
|
939
1018
|
rb_define_method(cBackend, "accept", Backend_accept, 2);
|
940
1019
|
rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
|
@@ -8,6 +8,8 @@
|
|
8
8
|
#include <netinet/in.h>
|
9
9
|
#include <arpa/inet.h>
|
10
10
|
#include <stdnoreturn.h>
|
11
|
+
#include <sys/types.h>
|
12
|
+
#include <sys/wait.h>
|
11
13
|
|
12
14
|
#include "polyphony.h"
|
13
15
|
#include "../libev/ev.h"
|
@@ -332,6 +334,58 @@ error:
|
|
332
334
|
return RAISE_EXCEPTION(switchpoint_result);
|
333
335
|
}
|
334
336
|
|
337
|
+
VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
|
338
|
+
Backend_t *backend;
|
339
|
+
struct libev_io watcher;
|
340
|
+
rb_io_t *fptr;
|
341
|
+
VALUE str;
|
342
|
+
long total;
|
343
|
+
long len = 8192;
|
344
|
+
int shrinkable;
|
345
|
+
char *buf;
|
346
|
+
VALUE switchpoint_result = Qnil;
|
347
|
+
VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
|
348
|
+
ID method_id = SYM2ID(method);
|
349
|
+
|
350
|
+
READ_LOOP_PREPARE_STR();
|
351
|
+
|
352
|
+
GetBackend(self, backend);
|
353
|
+
if (underlying_io != Qnil) io = underlying_io;
|
354
|
+
GetOpenFile(io, fptr);
|
355
|
+
rb_io_check_byte_readable(fptr);
|
356
|
+
io_set_nonblock(fptr, io);
|
357
|
+
rectify_io_file_pos(fptr);
|
358
|
+
watcher.fiber = Qnil;
|
359
|
+
|
360
|
+
while (1) {
|
361
|
+
ssize_t n = read(fptr->fd, buf, len);
|
362
|
+
if (n < 0) {
|
363
|
+
int e = errno;
|
364
|
+
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
365
|
+
|
366
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
|
367
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
368
|
+
}
|
369
|
+
else {
|
370
|
+
switchpoint_result = backend_snooze();
|
371
|
+
|
372
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
373
|
+
|
374
|
+
if (n == 0) break; // EOF
|
375
|
+
total = n;
|
376
|
+
READ_LOOP_PASS_STR_TO_RECEIVER(receiver, method_id);
|
377
|
+
}
|
378
|
+
}
|
379
|
+
|
380
|
+
RB_GC_GUARD(str);
|
381
|
+
RB_GC_GUARD(watcher.fiber);
|
382
|
+
RB_GC_GUARD(switchpoint_result);
|
383
|
+
|
384
|
+
return io;
|
385
|
+
error:
|
386
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
387
|
+
}
|
388
|
+
|
335
389
|
VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
|
336
390
|
Backend_t *backend;
|
337
391
|
struct libev_io watcher;
|
@@ -685,26 +739,12 @@ noreturn VALUE Backend_timer_loop(VALUE self, VALUE interval) {
|
|
685
739
|
RB_GC_GUARD(switchpoint_result);
|
686
740
|
|
687
741
|
rb_yield(Qnil);
|
688
|
-
|
689
|
-
while (1) {
|
742
|
+
do {
|
690
743
|
next_time += interval_d;
|
691
|
-
|
692
|
-
}
|
744
|
+
} while (next_time <= now);
|
693
745
|
}
|
694
746
|
}
|
695
747
|
|
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
748
|
struct libev_timeout {
|
709
749
|
struct ev_timer timer;
|
710
750
|
VALUE fiber;
|
@@ -759,38 +799,29 @@ VALUE Backend_timeout(int argc,VALUE *argv, VALUE self) {
|
|
759
799
|
return result;
|
760
800
|
}
|
761
801
|
|
762
|
-
struct libev_child {
|
763
|
-
struct ev_child child;
|
764
|
-
VALUE fiber;
|
765
|
-
};
|
766
|
-
|
767
|
-
void Backend_child_callback(EV_P_ ev_child *w, int revents)
|
768
|
-
{
|
769
|
-
struct libev_child *watcher = (struct libev_child *)w;
|
770
|
-
int exit_status = WEXITSTATUS(w->rstatus);
|
771
|
-
VALUE status;
|
772
|
-
|
773
|
-
status = rb_ary_new_from_args(2, INT2NUM(w->rpid), INT2NUM(exit_status));
|
774
|
-
Fiber_make_runnable(watcher->fiber, status);
|
775
|
-
}
|
776
|
-
|
777
802
|
VALUE Backend_waitpid(VALUE self, VALUE pid) {
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
803
|
+
int pid_int = NUM2INT(pid);
|
804
|
+
int fd = pidfd_open(pid_int, 0);
|
805
|
+
if (fd >= 0) {
|
806
|
+
Backend_t *backend;
|
807
|
+
GetBackend(self, backend);
|
808
|
+
|
809
|
+
VALUE resume_value = libev_wait_fd(backend, fd, EV_READ, 0);
|
810
|
+
close(fd);
|
811
|
+
RAISE_IF_EXCEPTION(resume_value);
|
812
|
+
RB_GC_GUARD(resume_value);
|
813
|
+
}
|
788
814
|
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
815
|
+
int status = 0;
|
816
|
+
pid_t ret = waitpid(pid_int, &status, WNOHANG);
|
817
|
+
if (ret < 0) {
|
818
|
+
int e = errno;
|
819
|
+
if (e == ECHILD)
|
820
|
+
ret = pid_int;
|
821
|
+
else
|
822
|
+
rb_syserr_fail(e, strerror(e));
|
823
|
+
}
|
824
|
+
return rb_ary_new_from_args(2, INT2NUM(ret), INT2NUM(WEXITSTATUS(status)));
|
794
825
|
}
|
795
826
|
|
796
827
|
void Backend_async_callback(EV_P_ ev_async *w, int revents) { }
|
@@ -832,12 +863,14 @@ void Init_Backend() {
|
|
832
863
|
|
833
864
|
rb_define_method(cBackend, "read", Backend_read, 4);
|
834
865
|
rb_define_method(cBackend, "read_loop", Backend_read_loop, 1);
|
866
|
+
rb_define_method(cBackend, "feed_loop", Backend_feed_loop, 3);
|
835
867
|
rb_define_method(cBackend, "write", Backend_write_m, -1);
|
836
868
|
rb_define_method(cBackend, "accept", Backend_accept, 2);
|
837
869
|
rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
|
838
870
|
rb_define_method(cBackend, "connect", Backend_connect, 3);
|
839
871
|
rb_define_method(cBackend, "recv", Backend_recv, 3);
|
840
872
|
rb_define_method(cBackend, "recv_loop", Backend_read_loop, 1);
|
873
|
+
rb_define_method(cBackend, "recv_feed_loop", Backend_feed_loop, 3);
|
841
874
|
rb_define_method(cBackend, "send", Backend_write, 2);
|
842
875
|
rb_define_method(cBackend, "wait_io", Backend_wait_io, 2);
|
843
876
|
rb_define_method(cBackend, "sleep", Backend_sleep, 1);
|