polyphony 0.53.2 → 0.58

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.
@@ -1,4 +1,5 @@
1
1
  #include <stdlib.h>
2
+ #include <assert.h>
2
3
  #include "ruby.h"
3
4
  #include "polyphony.h"
4
5
  #include "backend_io_uring_context.h"
@@ -15,6 +16,7 @@ const char *op_type_to_str(enum op_type type) {
15
16
  case OP_POLL: return "POLL";
16
17
  case OP_ACCEPT: return "ACCEPT";
17
18
  case OP_CONNECT: return "CONNECT";
19
+ case OP_CHAIN: return "CHAIN";
18
20
  default: return "";
19
21
  };
20
22
  }
@@ -35,7 +37,7 @@ inline op_context_t *context_store_acquire(op_context_store_t *store, enum op_ty
35
37
  ctx = malloc(sizeof(op_context_t));
36
38
  }
37
39
  ctx->id = (++store->last_id);
38
-
40
+ // printf("acquire %p %d (%s)\n", ctx, ctx->id, op_type_to_str(type));
39
41
  ctx->prev = NULL;
40
42
  ctx->next = store->taken;
41
43
  if (store->taken) store->taken->prev = ctx;
@@ -44,13 +46,21 @@ inline op_context_t *context_store_acquire(op_context_store_t *store, enum op_ty
44
46
  ctx->type = type;
45
47
  ctx->fiber = rb_fiber_current();
46
48
  ctx->resume_value = Qnil;
47
- ctx->completed = 0;
49
+ ctx->ref_count = 2;
48
50
  ctx->result = 0;
49
51
 
50
52
  return ctx;
51
53
  }
52
54
 
53
- inline void context_store_release(op_context_store_t *store, op_context_t *ctx) {
55
+ // returns true if ctx was released
56
+ inline int context_store_release(op_context_store_t *store, op_context_t *ctx) {
57
+ // printf("release %p %d (%s, ref_count: %d)\n", ctx, ctx->id, op_type_to_str(ctx->type), ctx->ref_count);
58
+
59
+ assert(ctx->ref_count);
60
+
61
+ ctx->ref_count--;
62
+ if (ctx->ref_count) return 0;
63
+
54
64
  if (ctx->next) ctx->next->prev = ctx->prev;
55
65
  if (ctx->prev) ctx->prev->next = ctx->next;
56
66
  if (store->taken == ctx) store->taken = ctx->next;
@@ -59,6 +69,7 @@ inline void context_store_release(op_context_store_t *store, op_context_t *ctx)
59
69
  ctx->next = store->available;
60
70
  if (ctx->next) ctx->next->prev = ctx;
61
71
  store->available = ctx;
72
+ return 1;
62
73
  }
63
74
 
64
75
  void context_store_free(op_context_store_t *store) {
@@ -14,14 +14,15 @@ enum op_type {
14
14
  OP_TIMEOUT,
15
15
  OP_POLL,
16
16
  OP_ACCEPT,
17
- OP_CONNECT
17
+ OP_CONNECT,
18
+ OP_CHAIN
18
19
  };
19
20
 
20
21
  typedef struct op_context {
21
22
  struct op_context *prev;
22
23
  struct op_context *next;
23
24
  enum op_type type: 16;
24
- int completed : 16;
25
+ unsigned int ref_count : 16;
25
26
  int id;
26
27
  int result;
27
28
  VALUE fiber;
@@ -38,17 +39,16 @@ const char *op_type_to_str(enum op_type type);
38
39
 
39
40
  void context_store_initialize(op_context_store_t *store);
40
41
  op_context_t *context_store_acquire(op_context_store_t *store, enum op_type type);
41
- void context_store_release(op_context_store_t *store, op_context_t *ctx);
42
+ int context_store_release(op_context_store_t *store, op_context_t *ctx);
42
43
  void context_store_free(op_context_store_t *store);
43
44
 
44
- #define OP_CONTEXT_ACQUIRE(store, op_type) context_store_acquire(store, op_type)
45
- #define OP_CONTEXT_RELEASE(store, ctx) { \
46
- if (ctx->completed) {\
47
- context_store_release(store, ctx); \
48
- } \
49
- else { \
50
- ctx->completed = 1; \
51
- } \
45
+ inline unsigned int OP_CONTEXT_RELEASE(op_context_store_t *store, op_context_t *ctx) {
46
+ int completed = !ctx->ref_count;
47
+ if (ctx->ref_count)
48
+ ctx->ref_count -= 1;
49
+ else
50
+ context_store_release(store, ctx);
51
+ return completed;
52
52
  }
53
53
 
54
54
  #endif /* BACKEND_IO_URING_CONTEXT_H */
@@ -42,7 +42,6 @@ thread.
42
42
  #define _GNU_SOURCE 1
43
43
  #endif
44
44
 
45
- #include <fcntl.h>
46
45
  #include <netdb.h>
47
46
  #include <sys/socket.h>
48
47
  #include <sys/uio.h>
@@ -52,58 +51,41 @@ thread.
52
51
  #include <stdnoreturn.h>
53
52
  #include <sys/types.h>
54
53
  #include <sys/wait.h>
54
+ #include <fcntl.h>
55
55
 
56
56
  #include "polyphony.h"
57
57
  #include "../libev/ev.h"
58
58
  #include "ruby/io.h"
59
59
 
60
+ #include "../libev/ev.h"
61
+ #include "backend_common.h"
62
+
60
63
  VALUE SYM_libev;
61
64
  VALUE SYM_send;
62
65
  VALUE SYM_splice;
63
66
  VALUE SYM_write;
64
67
 
65
- ID ID_ivar_is_nonblocking;
66
-
67
- // Since we need to ensure that fd's are non-blocking before every I/O
68
- // operation, here we improve upon Ruby's rb_io_set_nonblock by caching the
69
- // "nonblock" state in an instance variable. Calling rb_ivar_get on every read
70
- // is still much cheaper than doing a fcntl syscall on every read! Preliminary
71
- // benchmarks (with a "hello world" HTTP server) show throughput is improved
72
- // by 10-13%.
73
- inline void io_set_nonblock(rb_io_t *fptr, VALUE io) {
74
- VALUE is_nonblocking = rb_ivar_get(io, ID_ivar_is_nonblocking);
75
- if (is_nonblocking == Qtrue) return;
76
-
77
- rb_ivar_set(io, ID_ivar_is_nonblocking, Qtrue);
78
-
79
- #ifdef _WIN32
80
- rb_w32_set_nonblock(fptr->fd);
81
- #elif defined(F_GETFL)
82
- int oflags = fcntl(fptr->fd, F_GETFL);
83
- if ((oflags == -1) && (oflags & O_NONBLOCK)) return;
84
- oflags |= O_NONBLOCK;
85
- fcntl(fptr->fd, F_SETFL, oflags);
86
- #endif
87
- }
88
-
89
68
  typedef struct Backend_t {
90
- // common fields
91
- unsigned int currently_polling;
92
- unsigned int pending_count;
93
- unsigned int poll_no_wait_count;
69
+ struct Backend_base base;
94
70
 
95
71
  // implementation-specific fields
96
72
  struct ev_loop *ev_loop;
97
73
  struct ev_async break_async;
98
74
  } Backend_t;
99
75
 
76
+ static void Backend_mark(void *ptr) {
77
+ Backend_t *backend = ptr;
78
+ if (backend->base.idle_block != Qnil)
79
+ rb_gc_mark(backend->base.idle_block);
80
+ }
81
+
100
82
  static size_t Backend_size(const void *ptr) {
101
83
  return sizeof(Backend_t);
102
84
  }
103
85
 
104
86
  static const rb_data_type_t Backend_type = {
105
87
  "LibevBackend",
106
- {0, 0, Backend_size,},
88
+ {Backend_mark, 0, Backend_size,},
107
89
  0, 0, RUBY_TYPED_FREE_IMMEDIATELY
108
90
  };
109
91
 
@@ -134,6 +116,8 @@ static VALUE Backend_initialize(VALUE self) {
134
116
  Backend_t *backend;
135
117
 
136
118
  GetBackend(self, backend);
119
+
120
+ initialize_backend_base(&backend->base);
137
121
  backend->ev_loop = libev_new_loop();
138
122
 
139
123
  // start async watcher used for breaking a poll op (from another thread)
@@ -143,10 +127,6 @@ static VALUE Backend_initialize(VALUE self) {
143
127
  // block when no other watcher is active
144
128
  ev_unref(backend->ev_loop);
145
129
 
146
- backend->currently_polling = 0;
147
- backend->pending_count = 0;
148
- backend->poll_no_wait_count = 0;
149
-
150
130
  return Qnil;
151
131
  }
152
132
 
@@ -176,32 +156,21 @@ VALUE Backend_post_fork(VALUE self) {
176
156
  return self;
177
157
  }
178
158
 
179
- unsigned int Backend_pending_count(VALUE self) {
159
+ inline unsigned int Backend_pending_count(VALUE self) {
180
160
  Backend_t *backend;
181
161
  GetBackend(self, backend);
182
162
 
183
- return backend->pending_count;
163
+ return backend->base.pending_count;
184
164
  }
185
165
 
186
166
  VALUE Backend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE runqueue) {
187
- int is_nowait = nowait == Qtrue;
188
167
  Backend_t *backend;
189
168
  GetBackend(self, backend);
190
169
 
191
- if (is_nowait) {
192
- backend->poll_no_wait_count++;
193
- if (backend->poll_no_wait_count < 10) return self;
194
-
195
- long runnable_count = Runqueue_len(runqueue);
196
- if (backend->poll_no_wait_count < runnable_count) return self;
197
- }
198
-
199
- backend->poll_no_wait_count = 0;
200
-
201
170
  COND_TRACE(2, SYM_fiber_event_poll_enter, current_fiber);
202
- backend->currently_polling = 1;
203
- ev_run(backend->ev_loop, is_nowait ? EVRUN_NOWAIT : EVRUN_ONCE);
204
- backend->currently_polling = 0;
171
+ backend->base.currently_polling = 1;
172
+ ev_run(backend->ev_loop, nowait == Qtrue ? EVRUN_NOWAIT : EVRUN_ONCE);
173
+ backend->base.currently_polling = 0;
205
174
  COND_TRACE(2, SYM_fiber_event_poll_leave, current_fiber);
206
175
 
207
176
  return self;
@@ -211,7 +180,7 @@ VALUE Backend_wakeup(VALUE self) {
211
180
  Backend_t *backend;
212
181
  GetBackend(self, backend);
213
182
 
214
- if (backend->currently_polling) {
183
+ if (backend->base.currently_polling) {
215
184
  // Since the loop will run until at least one event has occurred, we signal
216
185
  // the selector's associated async watcher, which will cause the ev loop to
217
186
  // return. In contrast to using `ev_break` to break out of the loop, which
@@ -224,10 +193,6 @@ VALUE Backend_wakeup(VALUE self) {
224
193
  return Qnil;
225
194
  }
226
195
 
227
- #include "../libev/ev.h"
228
-
229
- #include "backend_common.h"
230
-
231
196
  struct libev_io {
232
197
  struct ev_io io;
233
198
  VALUE fiber;
@@ -248,7 +213,7 @@ VALUE libev_wait_fd_with_watcher(Backend_t *backend, int fd, struct libev_io *wa
248
213
  }
249
214
  ev_io_start(backend->ev_loop, &watcher->io);
250
215
 
251
- switchpoint_result = backend_await(backend);
216
+ switchpoint_result = backend_await((struct Backend_base *)backend);
252
217
 
253
218
  ev_io_stop(backend->ev_loop, &watcher->io);
254
219
  RB_GC_GUARD(switchpoint_result);
@@ -284,7 +249,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
284
249
  if (underlying_io != Qnil) io = underlying_io;
285
250
  GetOpenFile(io, fptr);
286
251
  rb_io_check_byte_readable(fptr);
287
- io_set_nonblock(fptr, io);
252
+ io_verify_blocking_mode(fptr, io, Qfalse);
288
253
  rectify_io_file_pos(fptr);
289
254
  watcher.fiber = Qnil;
290
255
  OBJ_TAINT(str);
@@ -301,7 +266,6 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
301
266
  }
302
267
  else {
303
268
  switchpoint_result = backend_snooze();
304
-
305
269
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
306
270
 
307
271
  if (n == 0) break; // EOF
@@ -356,7 +320,7 @@ VALUE Backend_read_loop(VALUE self, VALUE io) {
356
320
  if (underlying_io != Qnil) io = underlying_io;
357
321
  GetOpenFile(io, fptr);
358
322
  rb_io_check_byte_readable(fptr);
359
- io_set_nonblock(fptr, io);
323
+ io_verify_blocking_mode(fptr, io, Qfalse);
360
324
  rectify_io_file_pos(fptr);
361
325
  watcher.fiber = Qnil;
362
326
 
@@ -408,7 +372,7 @@ VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
408
372
  if (underlying_io != Qnil) io = underlying_io;
409
373
  GetOpenFile(io, fptr);
410
374
  rb_io_check_byte_readable(fptr);
411
- io_set_nonblock(fptr, io);
375
+ io_verify_blocking_mode(fptr, io, Qfalse);
412
376
  rectify_io_file_pos(fptr);
413
377
  watcher.fiber = Qnil;
414
378
 
@@ -456,7 +420,7 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
456
420
  GetBackend(self, backend);
457
421
  io = rb_io_get_write_io(io);
458
422
  GetOpenFile(io, fptr);
459
- io_set_nonblock(fptr, io);
423
+ io_verify_blocking_mode(fptr, io, Qfalse);
460
424
  watcher.fiber = Qnil;
461
425
 
462
426
  while (left > 0) {
@@ -506,7 +470,7 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
506
470
  GetBackend(self, backend);
507
471
  io = rb_io_get_write_io(io);
508
472
  GetOpenFile(io, fptr);
509
- io_set_nonblock(fptr, io);
473
+ io_verify_blocking_mode(fptr, io, Qfalse);
510
474
  watcher.fiber = Qnil;
511
475
 
512
476
  iov = malloc(iov_count * sizeof(struct iovec));
@@ -587,7 +551,7 @@ VALUE Backend_accept(VALUE self, VALUE server_socket, VALUE socket_class) {
587
551
 
588
552
  GetBackend(self, backend);
589
553
  GetOpenFile(server_socket, fptr);
590
- io_set_nonblock(fptr, server_socket);
554
+ io_verify_blocking_mode(fptr, server_socket, Qfalse);
591
555
  watcher.fiber = Qnil;
592
556
  while (1) {
593
557
  fd = accept(fptr->fd, &addr, &len);
@@ -615,7 +579,7 @@ VALUE Backend_accept(VALUE self, VALUE server_socket, VALUE socket_class) {
615
579
  fp->fd = fd;
616
580
  fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
617
581
  rb_io_ascii8bit_binmode(socket);
618
- io_set_nonblock(fp, socket);
582
+ io_verify_blocking_mode(fp, socket, Qfalse);
619
583
  rb_io_synchronized(fp);
620
584
 
621
585
  // if (rsock_do_not_reverse_lookup) {
@@ -644,7 +608,7 @@ VALUE Backend_accept_loop(VALUE self, VALUE server_socket, VALUE socket_class) {
644
608
 
645
609
  GetBackend(self, backend);
646
610
  GetOpenFile(server_socket, fptr);
647
- io_set_nonblock(fptr, server_socket);
611
+ io_verify_blocking_mode(fptr, server_socket, Qfalse);
648
612
  watcher.fiber = Qnil;
649
613
 
650
614
  while (1) {
@@ -672,7 +636,7 @@ VALUE Backend_accept_loop(VALUE self, VALUE server_socket, VALUE socket_class) {
672
636
  fp->fd = fd;
673
637
  fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
674
638
  rb_io_ascii8bit_binmode(socket);
675
- io_set_nonblock(fp, socket);
639
+ io_verify_blocking_mode(fp, socket, Qfalse);
676
640
  rb_io_synchronized(fp);
677
641
 
678
642
  rb_yield(socket);
@@ -700,7 +664,7 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
700
664
 
701
665
  GetBackend(self, backend);
702
666
  GetOpenFile(sock, fptr);
703
- io_set_nonblock(fptr, sock);
667
+ io_verify_blocking_mode(fptr, sock, Qfalse);
704
668
  watcher.fiber = Qnil;
705
669
 
706
670
  addr.sin_family = AF_INET;
@@ -743,7 +707,7 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
743
707
  GetBackend(self, backend);
744
708
  io = rb_io_get_write_io(io);
745
709
  GetOpenFile(io, fptr);
746
- io_set_nonblock(fptr, io);
710
+ io_verify_blocking_mode(fptr, io, Qfalse);
747
711
  watcher.fiber = Qnil;
748
712
 
749
713
  while (left > 0) {
@@ -776,10 +740,63 @@ error:
776
740
  return RAISE_EXCEPTION(switchpoint_result);
777
741
  }
778
742
 
743
+ struct libev_rw_ctx {
744
+ int ref_count;
745
+ VALUE fiber;
746
+ };
747
+
748
+ struct libev_ref_count_io {
749
+ struct ev_io io;
750
+ struct libev_rw_ctx *ctx;
751
+ };
752
+
753
+ struct libev_rw_io {
754
+ struct libev_ref_count_io r;
755
+ struct libev_ref_count_io w;
756
+ struct libev_rw_ctx ctx;
757
+ };
758
+
759
+ void Backend_rw_io_callback(EV_P_ ev_io *w, int revents)
760
+ {
761
+ struct libev_ref_count_io *watcher = (struct libev_ref_count_io *)w;
762
+ int ref_count = watcher->ctx->ref_count--;
763
+ if (!ref_count)
764
+ Fiber_make_runnable(watcher->ctx->fiber, Qnil);
765
+ }
766
+
767
+ VALUE libev_wait_rw_fd_with_watcher(Backend_t *backend, int r_fd, int w_fd, struct libev_rw_io *watcher) {
768
+ VALUE switchpoint_result = Qnil;
769
+
770
+ if (watcher->ctx.fiber == Qnil) watcher->ctx.fiber = rb_fiber_current();
771
+ watcher->ctx.ref_count = 0;
772
+ if (r_fd != -1) {
773
+ ev_io_init(&watcher->r.io, Backend_rw_io_callback, r_fd, EV_READ);
774
+ ev_io_start(backend->ev_loop, &watcher->r.io);
775
+ watcher->r.ctx = &watcher->ctx;
776
+ watcher->ctx.ref_count++;
777
+ }
778
+ if (w_fd != -1) {
779
+ ev_io_init(&watcher->w.io, Backend_rw_io_callback, w_fd, EV_WRITE);
780
+ ev_io_start(backend->ev_loop, &watcher->w.io);
781
+ watcher->w.ctx = &watcher->ctx;
782
+ watcher->ctx.ref_count++;
783
+ }
784
+
785
+ switchpoint_result = backend_await((struct Backend_base *)backend);
786
+
787
+ if (r_fd != -1) ev_io_stop(backend->ev_loop, &watcher->r.io);
788
+ if (w_fd != -1) ev_io_stop(backend->ev_loop, &watcher->w.io);
789
+ RB_GC_GUARD(switchpoint_result);
790
+ return switchpoint_result;
791
+ }
792
+
793
+
794
+
795
+
779
796
  #ifdef POLYPHONY_LINUX
780
797
  VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
781
798
  Backend_t *backend;
782
- struct libev_io watcher;
799
+ struct libev_rw_io watcher;
783
800
  VALUE switchpoint_result = Qnil;
784
801
  VALUE underlying_io;
785
802
  rb_io_t *src_fptr;
@@ -791,25 +808,22 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
791
808
  underlying_io = rb_ivar_get(src, ID_ivar_io);
792
809
  if (underlying_io != Qnil) src = underlying_io;
793
810
  GetOpenFile(src, src_fptr);
794
- io_set_nonblock(src_fptr, src);
811
+ io_verify_blocking_mode(src_fptr, src, Qfalse);
795
812
 
796
813
  underlying_io = rb_ivar_get(dest, ID_ivar_io);
797
814
  if (underlying_io != Qnil) dest = underlying_io;
798
815
  dest = rb_io_get_write_io(dest);
799
816
  GetOpenFile(dest, dest_fptr);
800
- io_set_nonblock(dest_fptr, dest);
817
+ io_verify_blocking_mode(dest_fptr, dest, Qfalse);
801
818
 
802
- watcher.fiber = Qnil;
819
+ watcher.ctx.fiber = Qnil;
803
820
  while (1) {
804
821
  len = splice(src_fptr->fd, 0, dest_fptr->fd, 0, NUM2INT(maxlen), 0);
805
822
  if (len < 0) {
806
823
  int e = errno;
807
824
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
808
825
 
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);
826
+ switchpoint_result = libev_wait_rw_fd_with_watcher(backend, src_fptr->fd, dest_fptr->fd, &watcher);
813
827
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
814
828
  }
815
829
  else {
@@ -817,12 +831,12 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
817
831
  }
818
832
  }
819
833
 
820
- if (watcher.fiber == Qnil) {
834
+ if (watcher.ctx.fiber == Qnil) {
821
835
  switchpoint_result = backend_snooze();
822
836
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
823
837
  }
824
838
 
825
- RB_GC_GUARD(watcher.fiber);
839
+ RB_GC_GUARD(watcher.ctx.fiber);
826
840
  RB_GC_GUARD(switchpoint_result);
827
841
 
828
842
  return INT2NUM(len);
@@ -832,7 +846,7 @@ error:
832
846
 
833
847
  VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
834
848
  Backend_t *backend;
835
- struct libev_io watcher;
849
+ struct libev_rw_io watcher;
836
850
  VALUE switchpoint_result = Qnil;
837
851
  VALUE underlying_io;
838
852
  rb_io_t *src_fptr;
@@ -845,25 +859,22 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
845
859
  underlying_io = rb_ivar_get(src, ID_ivar_io);
846
860
  if (underlying_io != Qnil) src = underlying_io;
847
861
  GetOpenFile(src, src_fptr);
848
- io_set_nonblock(src_fptr, src);
862
+ io_verify_blocking_mode(src_fptr, src, Qfalse);
849
863
 
850
864
  underlying_io = rb_ivar_get(dest, ID_ivar_io);
851
865
  if (underlying_io != Qnil) dest = underlying_io;
852
866
  dest = rb_io_get_write_io(dest);
853
867
  GetOpenFile(dest, dest_fptr);
854
- io_set_nonblock(dest_fptr, dest);
868
+ io_verify_blocking_mode(dest_fptr, dest, Qfalse);
855
869
 
856
- watcher.fiber = Qnil;
870
+ watcher.ctx.fiber = Qnil;
857
871
  while (1) {
858
872
  len = splice(src_fptr->fd, 0, dest_fptr->fd, 0, NUM2INT(maxlen), 0);
859
873
  if (len < 0) {
860
874
  int e = errno;
861
875
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
862
876
 
863
- switchpoint_result = libev_wait_fd_with_watcher(backend, src_fptr->fd, &watcher, EV_READ);
864
- if (TEST_EXCEPTION(switchpoint_result)) goto error;
865
-
866
- switchpoint_result = libev_wait_fd_with_watcher(backend, dest_fptr->fd, &watcher, EV_WRITE);
877
+ switchpoint_result = libev_wait_rw_fd_with_watcher(backend, src_fptr->fd, dest_fptr->fd, &watcher);
867
878
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
868
879
  }
869
880
  else if (len == 0) {
@@ -874,6 +885,79 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
874
885
  }
875
886
  }
876
887
 
888
+ if (watcher.ctx.fiber == Qnil) {
889
+ switchpoint_result = backend_snooze();
890
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
891
+ }
892
+
893
+ RB_GC_GUARD(watcher.ctx.fiber);
894
+ RB_GC_GUARD(switchpoint_result);
895
+
896
+ return INT2NUM(total);
897
+ error:
898
+ return RAISE_EXCEPTION(switchpoint_result);
899
+ }
900
+ #endif
901
+
902
+ VALUE Backend_fake_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
903
+ Backend_t *backend;
904
+ struct libev_io watcher;
905
+ VALUE switchpoint_result = Qnil;
906
+ VALUE underlying_io;
907
+ rb_io_t *src_fptr;
908
+ rb_io_t *dest_fptr;
909
+ int len = NUM2INT(maxlen);
910
+ VALUE str = rb_str_new(0, len);
911
+ char *buf = RSTRING_PTR(str);
912
+ int left = 0;
913
+ int total = 0;
914
+
915
+ GetBackend(self, backend);
916
+
917
+ underlying_io = rb_ivar_get(src, ID_ivar_io);
918
+ if (underlying_io != Qnil) src = underlying_io;
919
+ GetOpenFile(src, src_fptr);
920
+ io_verify_blocking_mode(src_fptr, src, Qfalse);
921
+
922
+ underlying_io = rb_ivar_get(dest, ID_ivar_io);
923
+ if (underlying_io != Qnil) dest = underlying_io;
924
+ dest = rb_io_get_write_io(dest);
925
+ GetOpenFile(dest, dest_fptr);
926
+ io_verify_blocking_mode(dest_fptr, dest, Qfalse);
927
+
928
+ watcher.fiber = Qnil;
929
+
930
+ while (1) {
931
+ ssize_t n = read(src_fptr->fd, buf, len);
932
+ if (n < 0) {
933
+ int e = errno;
934
+ if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
935
+
936
+ switchpoint_result = libev_wait_fd_with_watcher(backend, src_fptr->fd, &watcher, EV_READ);
937
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
938
+ }
939
+ else {
940
+ total = left = n;
941
+ break;
942
+ }
943
+ }
944
+
945
+ while (left > 0) {
946
+ ssize_t n = write(dest_fptr->fd, buf, left);
947
+ if (n < 0) {
948
+ int e = errno;
949
+ if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
950
+
951
+ switchpoint_result = libev_wait_fd_with_watcher(backend, dest_fptr->fd, &watcher, EV_WRITE);
952
+
953
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
954
+ }
955
+ else {
956
+ buf += n;
957
+ left -= n;
958
+ }
959
+ }
960
+
877
961
  if (watcher.fiber == Qnil) {
878
962
  switchpoint_result = backend_snooze();
879
963
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
@@ -881,12 +965,90 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
881
965
 
882
966
  RB_GC_GUARD(watcher.fiber);
883
967
  RB_GC_GUARD(switchpoint_result);
968
+ RB_GC_GUARD(str);
969
+
970
+ return INT2NUM(total);
971
+ error:
972
+ return RAISE_EXCEPTION(switchpoint_result);
973
+ }
974
+
975
+ VALUE Backend_fake_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
976
+ Backend_t *backend;
977
+ struct libev_io watcher;
978
+ VALUE switchpoint_result = Qnil;
979
+ VALUE underlying_io;
980
+ rb_io_t *src_fptr;
981
+ rb_io_t *dest_fptr;
982
+ int len = NUM2INT(maxlen);
983
+ VALUE str = rb_str_new(0, len);
984
+ char *buf = RSTRING_PTR(str);
985
+ int left = 0;
986
+ int total = 0;
987
+
988
+ GetBackend(self, backend);
989
+
990
+ underlying_io = rb_ivar_get(src, ID_ivar_io);
991
+ if (underlying_io != Qnil) src = underlying_io;
992
+ GetOpenFile(src, src_fptr);
993
+ io_verify_blocking_mode(src_fptr, src, Qfalse);
994
+
995
+ underlying_io = rb_ivar_get(dest, ID_ivar_io);
996
+ if (underlying_io != Qnil) dest = underlying_io;
997
+ dest = rb_io_get_write_io(dest);
998
+ GetOpenFile(dest, dest_fptr);
999
+ io_verify_blocking_mode(dest_fptr, dest, Qfalse);
1000
+
1001
+ watcher.fiber = Qnil;
1002
+
1003
+ while (1) {
1004
+ char *ptr = buf;
1005
+ while (1) {
1006
+ ssize_t n = read(src_fptr->fd, ptr, len);
1007
+ if (n < 0) {
1008
+ int e = errno;
1009
+ if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
1010
+
1011
+ switchpoint_result = libev_wait_fd_with_watcher(backend, src_fptr->fd, &watcher, EV_READ);
1012
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
1013
+ }
1014
+ else if (n == 0) goto done;
1015
+ else {
1016
+ total += n;
1017
+ left = n;
1018
+ break;
1019
+ }
1020
+ }
1021
+
1022
+ while (left > 0) {
1023
+ ssize_t n = write(dest_fptr->fd, ptr, left);
1024
+ if (n < 0) {
1025
+ int e = errno;
1026
+ if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
1027
+
1028
+ switchpoint_result = libev_wait_fd_with_watcher(backend, dest_fptr->fd, &watcher, EV_WRITE);
1029
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
1030
+ }
1031
+ else {
1032
+ ptr += n;
1033
+ left -= n;
1034
+ }
1035
+ }
1036
+ }
1037
+
1038
+ done:
1039
+ if (watcher.fiber == Qnil) {
1040
+ switchpoint_result = backend_snooze();
1041
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
1042
+ }
1043
+
1044
+ RB_GC_GUARD(watcher.fiber);
1045
+ RB_GC_GUARD(switchpoint_result);
1046
+ RB_GC_GUARD(str);
884
1047
 
885
1048
  return INT2NUM(total);
886
1049
  error:
887
1050
  return RAISE_EXCEPTION(switchpoint_result);
888
1051
  }
889
- #endif
890
1052
 
891
1053
  VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
892
1054
  Backend_t *backend;
@@ -921,7 +1083,7 @@ VALUE Backend_sleep(VALUE self, VALUE duration) {
921
1083
  ev_timer_init(&watcher.timer, Backend_timer_callback, NUM2DBL(duration), 0.);
922
1084
  ev_timer_start(backend->ev_loop, &watcher.timer);
923
1085
 
924
- switchpoint_result = backend_await(backend);
1086
+ switchpoint_result = backend_await((struct Backend_base *)backend);
925
1087
 
926
1088
  ev_timer_stop(backend->ev_loop, &watcher.timer);
927
1089
  RAISE_IF_EXCEPTION(switchpoint_result);
@@ -949,7 +1111,7 @@ noreturn VALUE Backend_timer_loop(VALUE self, VALUE interval) {
949
1111
  VALUE switchpoint_result = Qnil;
950
1112
  ev_timer_init(&watcher.timer, Backend_timer_callback, sleep_duration, 0.);
951
1113
  ev_timer_start(backend->ev_loop, &watcher.timer);
952
- switchpoint_result = backend_await(backend);
1114
+ switchpoint_result = backend_await((struct Backend_base *)backend);
953
1115
  ev_timer_stop(backend->ev_loop, &watcher.timer);
954
1116
  RAISE_IF_EXCEPTION(switchpoint_result);
955
1117
  RB_GC_GUARD(switchpoint_result);
@@ -1066,7 +1228,7 @@ VALUE Backend_waitpid(VALUE self, VALUE pid) {
1066
1228
  ev_child_init(&watcher.child, Backend_child_callback, NUM2INT(pid), 0);
1067
1229
  ev_child_start(backend->ev_loop, &watcher.child);
1068
1230
 
1069
- switchpoint_result = backend_await(backend);
1231
+ switchpoint_result = backend_await((struct Backend_base *)backend);
1070
1232
 
1071
1233
  ev_child_stop(backend->ev_loop, &watcher.child);
1072
1234
  RAISE_IF_EXCEPTION(switchpoint_result);
@@ -1088,7 +1250,7 @@ VALUE Backend_wait_event(VALUE self, VALUE raise) {
1088
1250
  ev_async_init(&async, Backend_async_callback);
1089
1251
  ev_async_start(backend->ev_loop, &async);
1090
1252
 
1091
- switchpoint_result = backend_await(backend);
1253
+ switchpoint_result = backend_await((struct Backend_base *)backend);
1092
1254
 
1093
1255
  ev_async_stop(backend->ev_loop, &async);
1094
1256
  if (RTEST(raise)) RAISE_IF_EXCEPTION(switchpoint_result);
@@ -1113,10 +1275,8 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1113
1275
  result = Backend_write(self, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2));
1114
1276
  else if (op_type == SYM_send && op_len == 4)
1115
1277
  result = Backend_send(self, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2), RARRAY_AREF(op, 3));
1116
- #ifdef POLYPHONY_LINUX
1117
1278
  else if (op_type == SYM_splice && op_len == 4)
1118
1279
  result = Backend_splice(self, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2), RARRAY_AREF(op, 3));
1119
- #endif
1120
1280
  else
1121
1281
  rb_raise(rb_eRuntimeError, "Invalid op specified or bad op arity");
1122
1282
  }
@@ -1125,6 +1285,164 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1125
1285
  return result;
1126
1286
  }
1127
1287
 
1288
+ VALUE Backend_idle_gc_period_set(VALUE self, VALUE period) {
1289
+ Backend_t *backend;
1290
+ GetBackend(self, backend);
1291
+ backend->base.idle_gc_period = NUM2DBL(period);
1292
+ backend->base.idle_gc_last_time = current_time();
1293
+ return self;
1294
+ }
1295
+
1296
+ VALUE Backend_idle_block_set(VALUE self, VALUE block) {
1297
+ Backend_t *backend;
1298
+ GetBackend(self, backend);
1299
+ backend->base.idle_block = block;
1300
+ return self;
1301
+ }
1302
+
1303
+ inline VALUE Backend_run_idle_tasks(VALUE self) {
1304
+ Backend_t *backend;
1305
+ GetBackend(self, backend);
1306
+ backend_run_idle_tasks(&backend->base);
1307
+ return self;
1308
+ }
1309
+
1310
+ inline int splice_chunks_write(Backend_t *backend, int fd, VALUE str, struct libev_rw_io *watcher, VALUE *result) {
1311
+ char *buf = RSTRING_PTR(str);
1312
+ int len = RSTRING_LEN(str);
1313
+ int left = len;
1314
+ while (left > 0) {
1315
+ ssize_t n = write(fd, buf, left);
1316
+ if (n < 0) {
1317
+ int err = errno;
1318
+ if ((err != EWOULDBLOCK && err != EAGAIN)) return err;
1319
+
1320
+ *result = libev_wait_rw_fd_with_watcher(backend, -1, fd, watcher);
1321
+ if (TEST_EXCEPTION(*result)) return -1;
1322
+ }
1323
+ else {
1324
+ buf += n;
1325
+ left -= n;
1326
+ }
1327
+ }
1328
+ return 0;
1329
+ }
1330
+
1331
+ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VALUE postfix, VALUE chunk_prefix, VALUE chunk_postfix, VALUE chunk_size) {
1332
+ Backend_t *backend;
1333
+ GetBackend(self, backend);
1334
+ int total = 0;
1335
+ int err = 0;
1336
+ VALUE result = Qnil;
1337
+
1338
+ rb_io_t *src_fptr;
1339
+ rb_io_t *dest_fptr;
1340
+
1341
+ VALUE underlying_io = rb_ivar_get(src, ID_ivar_io);
1342
+ if (underlying_io != Qnil) src = underlying_io;
1343
+ GetOpenFile(src, src_fptr);
1344
+ io_verify_blocking_mode(src_fptr, src, Qfalse);
1345
+
1346
+ underlying_io = rb_ivar_get(dest, ID_ivar_io);
1347
+ if (underlying_io != Qnil) dest = underlying_io;
1348
+ dest = rb_io_get_write_io(dest);
1349
+ GetOpenFile(dest, dest_fptr);
1350
+ io_verify_blocking_mode(dest_fptr, dest, Qfalse);
1351
+
1352
+ struct libev_rw_io watcher;
1353
+ watcher.ctx.fiber = Qnil;
1354
+ int maxlen = NUM2INT(chunk_size);
1355
+ VALUE str = Qnil;
1356
+ VALUE chunk_len_value = Qnil;
1357
+
1358
+ int pipefd[2] = { -1, -1 };
1359
+ if (pipe(pipefd) == -1) {
1360
+ err = errno;
1361
+ goto syscallerror;
1362
+ }
1363
+
1364
+ fcntl(pipefd[0], F_SETFL, O_NONBLOCK);
1365
+ fcntl(pipefd[1], F_SETFL, O_NONBLOCK);
1366
+
1367
+ if (prefix != Qnil) {
1368
+ int err = splice_chunks_write(backend, dest_fptr->fd, prefix, &watcher, &result);
1369
+ if (err == -1) goto error; else if (err) goto syscallerror;
1370
+ }
1371
+ while (1) {
1372
+ int chunk_len;
1373
+ // splice to pipe
1374
+ while (1) {
1375
+ chunk_len = splice(src_fptr->fd, 0, pipefd[1], 0, maxlen, 0);
1376
+ if (chunk_len < 0) {
1377
+ err = errno;
1378
+ if (err != EWOULDBLOCK && err != EAGAIN) goto syscallerror;
1379
+
1380
+ result = libev_wait_rw_fd_with_watcher(backend, src_fptr->fd, pipefd[1], &watcher);
1381
+ if (TEST_EXCEPTION(result)) goto error;
1382
+ }
1383
+ else {
1384
+ break;
1385
+ }
1386
+ }
1387
+ if (chunk_len == 0) break;
1388
+
1389
+ total += chunk_len;
1390
+ chunk_len_value = INT2NUM(chunk_len);
1391
+
1392
+ if (chunk_prefix != Qnil) {
1393
+ VALUE str = (TYPE(chunk_prefix) == T_STRING) ? chunk_prefix : rb_funcall(chunk_prefix, ID_call, 1, chunk_len_value);
1394
+ int err = splice_chunks_write(backend, dest_fptr->fd, str, &watcher, &result);
1395
+ if (err == -1) goto error; else if (err) goto syscallerror;
1396
+ }
1397
+
1398
+ int left = chunk_len;
1399
+ while (1) {
1400
+ int n = splice(pipefd[0], 0, dest_fptr->fd, 0, left, 0);
1401
+ if (n < 0) {
1402
+ err = errno;
1403
+ if (err != EWOULDBLOCK && err != EAGAIN) goto syscallerror;
1404
+
1405
+ result = libev_wait_rw_fd_with_watcher(backend, pipefd[0], dest_fptr->fd, &watcher);
1406
+ if (TEST_EXCEPTION(result)) goto error;
1407
+ }
1408
+ else {
1409
+ left -= n;
1410
+ if (left == 0) break;
1411
+ }
1412
+ }
1413
+
1414
+ if (chunk_postfix != Qnil) {
1415
+ VALUE str = (TYPE(chunk_postfix) == T_STRING) ? chunk_postfix : rb_funcall(chunk_postfix, ID_call, 1, chunk_len_value);
1416
+ int err = splice_chunks_write(backend, dest_fptr->fd, str, &watcher, &result);
1417
+ if (err == -1) goto error; else if (err) goto syscallerror;
1418
+ }
1419
+ }
1420
+
1421
+ if (postfix != Qnil) {
1422
+ int err = splice_chunks_write(backend, dest_fptr->fd, postfix, &watcher, &result);
1423
+ if (err == -1) goto error; else if (err) goto syscallerror;
1424
+ }
1425
+
1426
+ if (watcher.ctx.fiber == Qnil) {
1427
+ result = backend_snooze();
1428
+ if (TEST_EXCEPTION(result)) goto error;
1429
+ }
1430
+ RB_GC_GUARD(str);
1431
+ RB_GC_GUARD(chunk_len_value);
1432
+ RB_GC_GUARD(result);
1433
+ if (pipefd[0] != -1) close(pipefd[0]);
1434
+ if (pipefd[1] != -1) close(pipefd[1]);
1435
+ return INT2NUM(total);
1436
+ syscallerror:
1437
+ if (pipefd[0] != -1) close(pipefd[0]);
1438
+ if (pipefd[1] != -1) close(pipefd[1]);
1439
+ rb_syserr_fail(err, strerror(err));
1440
+ error:
1441
+ if (pipefd[0] != -1) close(pipefd[0]);
1442
+ if (pipefd[1] != -1) close(pipefd[1]);
1443
+ return RAISE_EXCEPTION(result);
1444
+ }
1445
+
1128
1446
  void Init_Backend() {
1129
1447
  ev_set_allocator(xrealloc);
1130
1448
 
@@ -1139,6 +1457,9 @@ void Init_Backend() {
1139
1457
  rb_define_method(cBackend, "break", Backend_wakeup, 0);
1140
1458
  rb_define_method(cBackend, "kind", Backend_kind, 0);
1141
1459
  rb_define_method(cBackend, "chain", Backend_chain, -1);
1460
+ rb_define_method(cBackend, "idle_gc_period=", Backend_idle_gc_period_set, 1);
1461
+ rb_define_method(cBackend, "idle_block=", Backend_idle_block_set, 1);
1462
+ rb_define_method(cBackend, "splice_chunks", Backend_splice_chunks, 7);
1142
1463
 
1143
1464
  rb_define_method(cBackend, "accept", Backend_accept, 2);
1144
1465
  rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
@@ -1156,6 +1477,9 @@ void Init_Backend() {
1156
1477
  #ifdef POLYPHONY_LINUX
1157
1478
  rb_define_method(cBackend, "splice", Backend_splice, 3);
1158
1479
  rb_define_method(cBackend, "splice_to_eof", Backend_splice_to_eof, 3);
1480
+ #else
1481
+ rb_define_method(cBackend, "splice", Backend_fake_splice, 3);
1482
+ rb_define_method(cBackend, "splice_to_eof", Backend_fake_splice_to_eof, 3);
1159
1483
  #endif
1160
1484
 
1161
1485
  rb_define_method(cBackend, "timeout", Backend_timeout, -1);
@@ -1165,7 +1489,6 @@ void Init_Backend() {
1165
1489
  rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
1166
1490
  rb_define_method(cBackend, "write", Backend_write_m, -1);
1167
1491
 
1168
- ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
1169
1492
  SYM_libev = ID2SYM(rb_intern("libev"));
1170
1493
 
1171
1494
  SYM_send = ID2SYM(rb_intern("send"));