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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 444ffbf8eb00cbd5e6948d6e3347e244fb8ea548aa1ad302d266e2f16c1a91b9
4
- data.tar.gz: 2ac2f6ba1b51e71f1f138ad8eae665cc12e5458e836a0ef4f2df30d9ab95b679
3
+ metadata.gz: 72f11d867863c51f1fc1ab3ec85fe575cca9ee53a5047f4d3f7b76cea3b650b7
4
+ data.tar.gz: c29354b2de2f207185fc06fd86421b53d328aaf67d611109a6f95e9130ac5967
5
5
  SHA512:
6
- metadata.gz: 65924d13d191fc7c6d7b9fc2f7df997218396cf7ff9933564296ee50100441279387b01dc046797378973514dc45b25afd7eda92e19aac9dde3d7f2a13a9901e
7
- data.tar.gz: a419d45a69c4a12a468277068ed792cc82d0384b2d620497c4c132fa27b9155ce05ce6662c062766ae5204c8c09a490bb1e95164c7a4bf0baa9f3166ac57d348
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.49.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 doing lots of `cancel_after` calls
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
- - Override stock `SizedQueue` impl with Queue with capacity
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
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ main = Fiber.current
7
+ spin do
8
+ sleep 0.1
9
+ main.schedule(:foo)
10
+ end
11
+
12
+ v = suspend
13
+ puts "v => #{v.inspect}"
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ main = Fiber.current
7
+ spin do
8
+ sleep 0.1
9
+ main.terminate
10
+ end
11
+
12
+ sleep
@@ -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', 1234)
30
- puts "pid #{Process.pid} Polyphony (#{Thread.current.backend.kind}) listening on port 1234"
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 * 1000000000 + ts.tv_nsec;
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 = 256;
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
- // io_uring_sqe_set_flags(sqe, IOSQE_ASYNC);
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
- RAISE_IF_EXCEPTION(resume_value);
893
- RB_GC_GUARD(resume_value);
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
- if (next_time > now) break;
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
- Backend_t *backend;
779
- struct libev_child watcher;
780
- VALUE switchpoint_result = Qnil;
781
- GetBackend(self, backend);
782
-
783
- watcher.fiber = rb_fiber_current();
784
- ev_child_init(&watcher.child, Backend_child_callback, NUM2INT(pid), 0);
785
- ev_child_start(backend->ev_loop, &watcher.child);
786
-
787
- switchpoint_result = backend_await(backend);
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
- ev_child_stop(backend->ev_loop, &watcher.child);
790
- RAISE_IF_EXCEPTION(switchpoint_result);
791
- RB_GC_GUARD(watcher.fiber);
792
- RB_GC_GUARD(switchpoint_result);
793
- return switchpoint_result;
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);