polyphony 0.53.2 → 0.58

Sign up to get free protection for your applications and to get access to all the features.
@@ -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"));