polyphony 0.53.1 → 0.57.0

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,6 +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);
40
+ // printf("acquire %d (%s)\n", ctx->id, op_type_to_str(type));
38
41
 
39
42
  ctx->prev = NULL;
40
43
  ctx->next = store->taken;
@@ -44,13 +47,21 @@ inline op_context_t *context_store_acquire(op_context_store_t *store, enum op_ty
44
47
  ctx->type = type;
45
48
  ctx->fiber = rb_fiber_current();
46
49
  ctx->resume_value = Qnil;
47
- ctx->completed = 0;
50
+ ctx->ref_count = 2;
48
51
  ctx->result = 0;
49
52
 
50
53
  return ctx;
51
54
  }
52
55
 
53
- inline void context_store_release(op_context_store_t *store, op_context_t *ctx) {
56
+ // returns true if ctx was released
57
+ inline int context_store_release(op_context_store_t *store, op_context_t *ctx) {
58
+ // printf("release %d (%s, ref_count: %d)\n", ctx->id, op_type_to_str(ctx->type), ctx->ref_count);
59
+
60
+ assert(ctx->ref_count);
61
+
62
+ ctx->ref_count--;
63
+ if (ctx->ref_count) return 0;
64
+
54
65
  if (ctx->next) ctx->next->prev = ctx->prev;
55
66
  if (ctx->prev) ctx->prev->next = ctx->next;
56
67
  if (store->taken == ctx) store->taken = ctx->next;
@@ -59,6 +70,7 @@ inline void context_store_release(op_context_store_t *store, op_context_t *ctx)
59
70
  ctx->next = store->available;
60
71
  if (ctx->next) ctx->next->prev = ctx;
61
72
  store->available = ctx;
73
+ return 1;
62
74
  }
63
75
 
64
76
  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,45 +51,22 @@ 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;
@@ -143,9 +119,10 @@ static VALUE Backend_initialize(VALUE self) {
143
119
  // block when no other watcher is active
144
120
  ev_unref(backend->ev_loop);
145
121
 
146
- backend->currently_polling = 0;
147
- backend->pending_count = 0;
148
- backend->poll_no_wait_count = 0;
122
+ backend->base.currently_polling = 0;
123
+ backend->base.pending_count = 0;
124
+ backend->base.idle_gc_period = 0;
125
+ backend->base.idle_gc_last_time = 0;
149
126
 
150
127
  return Qnil;
151
128
  }
@@ -176,32 +153,21 @@ VALUE Backend_post_fork(VALUE self) {
176
153
  return self;
177
154
  }
178
155
 
179
- unsigned int Backend_pending_count(VALUE self) {
156
+ inline unsigned int Backend_pending_count(VALUE self) {
180
157
  Backend_t *backend;
181
158
  GetBackend(self, backend);
182
159
 
183
- return backend->pending_count;
160
+ return backend->base.pending_count;
184
161
  }
185
162
 
186
163
  VALUE Backend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE runqueue) {
187
- int is_nowait = nowait == Qtrue;
188
164
  Backend_t *backend;
189
165
  GetBackend(self, backend);
190
166
 
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
167
  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;
168
+ backend->base.currently_polling = 1;
169
+ ev_run(backend->ev_loop, nowait == Qtrue ? EVRUN_NOWAIT : EVRUN_ONCE);
170
+ backend->base.currently_polling = 0;
205
171
  COND_TRACE(2, SYM_fiber_event_poll_leave, current_fiber);
206
172
 
207
173
  return self;
@@ -211,7 +177,7 @@ VALUE Backend_wakeup(VALUE self) {
211
177
  Backend_t *backend;
212
178
  GetBackend(self, backend);
213
179
 
214
- if (backend->currently_polling) {
180
+ if (backend->base.currently_polling) {
215
181
  // Since the loop will run until at least one event has occurred, we signal
216
182
  // the selector's associated async watcher, which will cause the ev loop to
217
183
  // return. In contrast to using `ev_break` to break out of the loop, which
@@ -224,10 +190,6 @@ VALUE Backend_wakeup(VALUE self) {
224
190
  return Qnil;
225
191
  }
226
192
 
227
- #include "../libev/ev.h"
228
-
229
- #include "backend_common.h"
230
-
231
193
  struct libev_io {
232
194
  struct ev_io io;
233
195
  VALUE fiber;
@@ -248,7 +210,7 @@ VALUE libev_wait_fd_with_watcher(Backend_t *backend, int fd, struct libev_io *wa
248
210
  }
249
211
  ev_io_start(backend->ev_loop, &watcher->io);
250
212
 
251
- switchpoint_result = backend_await(backend);
213
+ switchpoint_result = backend_await((struct Backend_base *)backend);
252
214
 
253
215
  ev_io_stop(backend->ev_loop, &watcher->io);
254
216
  RB_GC_GUARD(switchpoint_result);
@@ -284,7 +246,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
284
246
  if (underlying_io != Qnil) io = underlying_io;
285
247
  GetOpenFile(io, fptr);
286
248
  rb_io_check_byte_readable(fptr);
287
- io_set_nonblock(fptr, io);
249
+ io_verify_blocking_mode(fptr, io, Qfalse);
288
250
  rectify_io_file_pos(fptr);
289
251
  watcher.fiber = Qnil;
290
252
  OBJ_TAINT(str);
@@ -301,7 +263,6 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
301
263
  }
302
264
  else {
303
265
  switchpoint_result = backend_snooze();
304
-
305
266
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
306
267
 
307
268
  if (n == 0) break; // EOF
@@ -356,7 +317,7 @@ VALUE Backend_read_loop(VALUE self, VALUE io) {
356
317
  if (underlying_io != Qnil) io = underlying_io;
357
318
  GetOpenFile(io, fptr);
358
319
  rb_io_check_byte_readable(fptr);
359
- io_set_nonblock(fptr, io);
320
+ io_verify_blocking_mode(fptr, io, Qfalse);
360
321
  rectify_io_file_pos(fptr);
361
322
  watcher.fiber = Qnil;
362
323
 
@@ -408,7 +369,7 @@ VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
408
369
  if (underlying_io != Qnil) io = underlying_io;
409
370
  GetOpenFile(io, fptr);
410
371
  rb_io_check_byte_readable(fptr);
411
- io_set_nonblock(fptr, io);
372
+ io_verify_blocking_mode(fptr, io, Qfalse);
412
373
  rectify_io_file_pos(fptr);
413
374
  watcher.fiber = Qnil;
414
375
 
@@ -456,7 +417,7 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
456
417
  GetBackend(self, backend);
457
418
  io = rb_io_get_write_io(io);
458
419
  GetOpenFile(io, fptr);
459
- io_set_nonblock(fptr, io);
420
+ io_verify_blocking_mode(fptr, io, Qfalse);
460
421
  watcher.fiber = Qnil;
461
422
 
462
423
  while (left > 0) {
@@ -506,7 +467,7 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
506
467
  GetBackend(self, backend);
507
468
  io = rb_io_get_write_io(io);
508
469
  GetOpenFile(io, fptr);
509
- io_set_nonblock(fptr, io);
470
+ io_verify_blocking_mode(fptr, io, Qfalse);
510
471
  watcher.fiber = Qnil;
511
472
 
512
473
  iov = malloc(iov_count * sizeof(struct iovec));
@@ -587,7 +548,7 @@ VALUE Backend_accept(VALUE self, VALUE server_socket, VALUE socket_class) {
587
548
 
588
549
  GetBackend(self, backend);
589
550
  GetOpenFile(server_socket, fptr);
590
- io_set_nonblock(fptr, server_socket);
551
+ io_verify_blocking_mode(fptr, server_socket, Qfalse);
591
552
  watcher.fiber = Qnil;
592
553
  while (1) {
593
554
  fd = accept(fptr->fd, &addr, &len);
@@ -615,7 +576,7 @@ VALUE Backend_accept(VALUE self, VALUE server_socket, VALUE socket_class) {
615
576
  fp->fd = fd;
616
577
  fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
617
578
  rb_io_ascii8bit_binmode(socket);
618
- io_set_nonblock(fp, socket);
579
+ io_verify_blocking_mode(fp, socket, Qfalse);
619
580
  rb_io_synchronized(fp);
620
581
 
621
582
  // if (rsock_do_not_reverse_lookup) {
@@ -644,7 +605,7 @@ VALUE Backend_accept_loop(VALUE self, VALUE server_socket, VALUE socket_class) {
644
605
 
645
606
  GetBackend(self, backend);
646
607
  GetOpenFile(server_socket, fptr);
647
- io_set_nonblock(fptr, server_socket);
608
+ io_verify_blocking_mode(fptr, server_socket, Qfalse);
648
609
  watcher.fiber = Qnil;
649
610
 
650
611
  while (1) {
@@ -672,7 +633,7 @@ VALUE Backend_accept_loop(VALUE self, VALUE server_socket, VALUE socket_class) {
672
633
  fp->fd = fd;
673
634
  fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
674
635
  rb_io_ascii8bit_binmode(socket);
675
- io_set_nonblock(fp, socket);
636
+ io_verify_blocking_mode(fp, socket, Qfalse);
676
637
  rb_io_synchronized(fp);
677
638
 
678
639
  rb_yield(socket);
@@ -700,7 +661,7 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
700
661
 
701
662
  GetBackend(self, backend);
702
663
  GetOpenFile(sock, fptr);
703
- io_set_nonblock(fptr, sock);
664
+ io_verify_blocking_mode(fptr, sock, Qfalse);
704
665
  watcher.fiber = Qnil;
705
666
 
706
667
  addr.sin_family = AF_INET;
@@ -743,7 +704,7 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
743
704
  GetBackend(self, backend);
744
705
  io = rb_io_get_write_io(io);
745
706
  GetOpenFile(io, fptr);
746
- io_set_nonblock(fptr, io);
707
+ io_verify_blocking_mode(fptr, io, Qfalse);
747
708
  watcher.fiber = Qnil;
748
709
 
749
710
  while (left > 0) {
@@ -776,10 +737,63 @@ error:
776
737
  return RAISE_EXCEPTION(switchpoint_result);
777
738
  }
778
739
 
779
- #ifndef POLYPHONY_LINUX
740
+ struct libev_rw_ctx {
741
+ int ref_count;
742
+ VALUE fiber;
743
+ };
744
+
745
+ struct libev_ref_count_io {
746
+ struct ev_io io;
747
+ struct libev_rw_ctx *ctx;
748
+ };
749
+
750
+ struct libev_rw_io {
751
+ struct libev_ref_count_io r;
752
+ struct libev_ref_count_io w;
753
+ struct libev_rw_ctx ctx;
754
+ };
755
+
756
+ void Backend_rw_io_callback(EV_P_ ev_io *w, int revents)
757
+ {
758
+ struct libev_ref_count_io *watcher = (struct libev_ref_count_io *)w;
759
+ int ref_count = watcher->ctx->ref_count--;
760
+ if (!ref_count)
761
+ Fiber_make_runnable(watcher->ctx->fiber, Qnil);
762
+ }
763
+
764
+ VALUE libev_wait_rw_fd_with_watcher(Backend_t *backend, int r_fd, int w_fd, struct libev_rw_io *watcher) {
765
+ VALUE switchpoint_result = Qnil;
766
+
767
+ if (watcher->ctx.fiber == Qnil) watcher->ctx.fiber = rb_fiber_current();
768
+ watcher->ctx.ref_count = 0;
769
+ if (r_fd != -1) {
770
+ ev_io_init(&watcher->r.io, Backend_rw_io_callback, r_fd, EV_READ);
771
+ ev_io_start(backend->ev_loop, &watcher->r.io);
772
+ watcher->r.ctx = &watcher->ctx;
773
+ watcher->ctx.ref_count++;
774
+ }
775
+ if (w_fd != -1) {
776
+ ev_io_init(&watcher->w.io, Backend_rw_io_callback, w_fd, EV_WRITE);
777
+ ev_io_start(backend->ev_loop, &watcher->w.io);
778
+ watcher->w.ctx = &watcher->ctx;
779
+ watcher->ctx.ref_count++;
780
+ }
781
+
782
+ switchpoint_result = backend_await((struct Backend_base *)backend);
783
+
784
+ if (r_fd != -1) ev_io_stop(backend->ev_loop, &watcher->r.io);
785
+ if (w_fd != -1) ev_io_stop(backend->ev_loop, &watcher->w.io);
786
+ RB_GC_GUARD(switchpoint_result);
787
+ return switchpoint_result;
788
+ }
789
+
790
+
791
+
792
+
793
+ #ifdef POLYPHONY_LINUX
780
794
  VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
781
795
  Backend_t *backend;
782
- struct libev_io watcher;
796
+ struct libev_rw_io watcher;
783
797
  VALUE switchpoint_result = Qnil;
784
798
  VALUE underlying_io;
785
799
  rb_io_t *src_fptr;
@@ -791,25 +805,22 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
791
805
  underlying_io = rb_ivar_get(src, ID_ivar_io);
792
806
  if (underlying_io != Qnil) src = underlying_io;
793
807
  GetOpenFile(src, src_fptr);
794
- io_set_nonblock(src_fptr, src);
808
+ io_verify_blocking_mode(src_fptr, src, Qfalse);
795
809
 
796
810
  underlying_io = rb_ivar_get(dest, ID_ivar_io);
797
811
  if (underlying_io != Qnil) dest = underlying_io;
798
812
  dest = rb_io_get_write_io(dest);
799
813
  GetOpenFile(dest, dest_fptr);
800
- io_set_nonblock(dest_fptr, dest);
814
+ io_verify_blocking_mode(dest_fptr, dest, Qfalse);
801
815
 
802
- watcher.fiber = Qnil;
816
+ watcher.ctx.fiber = Qnil;
803
817
  while (1) {
804
818
  len = splice(src_fptr->fd, 0, dest_fptr->fd, 0, NUM2INT(maxlen), 0);
805
819
  if (len < 0) {
806
820
  int e = errno;
807
821
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
808
822
 
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);
823
+ switchpoint_result = libev_wait_rw_fd_with_watcher(backend, src_fptr->fd, dest_fptr->fd, &watcher);
813
824
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
814
825
  }
815
826
  else {
@@ -817,12 +828,12 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
817
828
  }
818
829
  }
819
830
 
820
- if (watcher.fiber == Qnil) {
831
+ if (watcher.ctx.fiber == Qnil) {
821
832
  switchpoint_result = backend_snooze();
822
833
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
823
834
  }
824
835
 
825
- RB_GC_GUARD(watcher.fiber);
836
+ RB_GC_GUARD(watcher.ctx.fiber);
826
837
  RB_GC_GUARD(switchpoint_result);
827
838
 
828
839
  return INT2NUM(len);
@@ -832,7 +843,7 @@ error:
832
843
 
833
844
  VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
834
845
  Backend_t *backend;
835
- struct libev_io watcher;
846
+ struct libev_rw_io watcher;
836
847
  VALUE switchpoint_result = Qnil;
837
848
  VALUE underlying_io;
838
849
  rb_io_t *src_fptr;
@@ -845,25 +856,22 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
845
856
  underlying_io = rb_ivar_get(src, ID_ivar_io);
846
857
  if (underlying_io != Qnil) src = underlying_io;
847
858
  GetOpenFile(src, src_fptr);
848
- io_set_nonblock(src_fptr, src);
859
+ io_verify_blocking_mode(src_fptr, src, Qfalse);
849
860
 
850
861
  underlying_io = rb_ivar_get(dest, ID_ivar_io);
851
862
  if (underlying_io != Qnil) dest = underlying_io;
852
863
  dest = rb_io_get_write_io(dest);
853
864
  GetOpenFile(dest, dest_fptr);
854
- io_set_nonblock(dest_fptr, dest);
865
+ io_verify_blocking_mode(dest_fptr, dest, Qfalse);
855
866
 
856
- watcher.fiber = Qnil;
867
+ watcher.ctx.fiber = Qnil;
857
868
  while (1) {
858
869
  len = splice(src_fptr->fd, 0, dest_fptr->fd, 0, NUM2INT(maxlen), 0);
859
870
  if (len < 0) {
860
871
  int e = errno;
861
872
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
862
873
 
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);
874
+ switchpoint_result = libev_wait_rw_fd_with_watcher(backend, src_fptr->fd, dest_fptr->fd, &watcher);
867
875
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
868
876
  }
869
877
  else if (len == 0) {
@@ -874,6 +882,79 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
874
882
  }
875
883
  }
876
884
 
885
+ if (watcher.ctx.fiber == Qnil) {
886
+ switchpoint_result = backend_snooze();
887
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
888
+ }
889
+
890
+ RB_GC_GUARD(watcher.ctx.fiber);
891
+ RB_GC_GUARD(switchpoint_result);
892
+
893
+ return INT2NUM(total);
894
+ error:
895
+ return RAISE_EXCEPTION(switchpoint_result);
896
+ }
897
+ #endif
898
+
899
+ VALUE Backend_fake_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
900
+ Backend_t *backend;
901
+ struct libev_io watcher;
902
+ VALUE switchpoint_result = Qnil;
903
+ VALUE underlying_io;
904
+ rb_io_t *src_fptr;
905
+ rb_io_t *dest_fptr;
906
+ int len = NUM2INT(maxlen);
907
+ VALUE str = rb_str_new(0, len);
908
+ char *buf = RSTRING_PTR(str);
909
+ int left = 0;
910
+ int total = 0;
911
+
912
+ GetBackend(self, backend);
913
+
914
+ underlying_io = rb_ivar_get(src, ID_ivar_io);
915
+ if (underlying_io != Qnil) src = underlying_io;
916
+ GetOpenFile(src, src_fptr);
917
+ io_verify_blocking_mode(src_fptr, src, Qfalse);
918
+
919
+ underlying_io = rb_ivar_get(dest, ID_ivar_io);
920
+ if (underlying_io != Qnil) dest = underlying_io;
921
+ dest = rb_io_get_write_io(dest);
922
+ GetOpenFile(dest, dest_fptr);
923
+ io_verify_blocking_mode(dest_fptr, dest, Qfalse);
924
+
925
+ watcher.fiber = Qnil;
926
+
927
+ while (1) {
928
+ ssize_t n = read(src_fptr->fd, buf, len);
929
+ if (n < 0) {
930
+ int e = errno;
931
+ if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
932
+
933
+ switchpoint_result = libev_wait_fd_with_watcher(backend, src_fptr->fd, &watcher, EV_READ);
934
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
935
+ }
936
+ else {
937
+ total = left = n;
938
+ break;
939
+ }
940
+ }
941
+
942
+ while (left > 0) {
943
+ ssize_t n = write(dest_fptr->fd, buf, left);
944
+ if (n < 0) {
945
+ int e = errno;
946
+ if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
947
+
948
+ switchpoint_result = libev_wait_fd_with_watcher(backend, dest_fptr->fd, &watcher, EV_WRITE);
949
+
950
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
951
+ }
952
+ else {
953
+ buf += n;
954
+ left -= n;
955
+ }
956
+ }
957
+
877
958
  if (watcher.fiber == Qnil) {
878
959
  switchpoint_result = backend_snooze();
879
960
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
@@ -881,12 +962,90 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
881
962
 
882
963
  RB_GC_GUARD(watcher.fiber);
883
964
  RB_GC_GUARD(switchpoint_result);
965
+ RB_GC_GUARD(str);
966
+
967
+ return INT2NUM(total);
968
+ error:
969
+ return RAISE_EXCEPTION(switchpoint_result);
970
+ }
971
+
972
+ VALUE Backend_fake_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
973
+ Backend_t *backend;
974
+ struct libev_io watcher;
975
+ VALUE switchpoint_result = Qnil;
976
+ VALUE underlying_io;
977
+ rb_io_t *src_fptr;
978
+ rb_io_t *dest_fptr;
979
+ int len = NUM2INT(maxlen);
980
+ VALUE str = rb_str_new(0, len);
981
+ char *buf = RSTRING_PTR(str);
982
+ int left = 0;
983
+ int total = 0;
984
+
985
+ GetBackend(self, backend);
986
+
987
+ underlying_io = rb_ivar_get(src, ID_ivar_io);
988
+ if (underlying_io != Qnil) src = underlying_io;
989
+ GetOpenFile(src, src_fptr);
990
+ io_verify_blocking_mode(src_fptr, src, Qfalse);
991
+
992
+ underlying_io = rb_ivar_get(dest, ID_ivar_io);
993
+ if (underlying_io != Qnil) dest = underlying_io;
994
+ dest = rb_io_get_write_io(dest);
995
+ GetOpenFile(dest, dest_fptr);
996
+ io_verify_blocking_mode(dest_fptr, dest, Qfalse);
997
+
998
+ watcher.fiber = Qnil;
999
+
1000
+ while (1) {
1001
+ char *ptr = buf;
1002
+ while (1) {
1003
+ ssize_t n = read(src_fptr->fd, ptr, len);
1004
+ if (n < 0) {
1005
+ int e = errno;
1006
+ if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
1007
+
1008
+ switchpoint_result = libev_wait_fd_with_watcher(backend, src_fptr->fd, &watcher, EV_READ);
1009
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
1010
+ }
1011
+ else if (n == 0) goto done;
1012
+ else {
1013
+ total += n;
1014
+ left = n;
1015
+ break;
1016
+ }
1017
+ }
1018
+
1019
+ while (left > 0) {
1020
+ ssize_t n = write(dest_fptr->fd, ptr, left);
1021
+ if (n < 0) {
1022
+ int e = errno;
1023
+ if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
1024
+
1025
+ switchpoint_result = libev_wait_fd_with_watcher(backend, dest_fptr->fd, &watcher, EV_WRITE);
1026
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
1027
+ }
1028
+ else {
1029
+ ptr += n;
1030
+ left -= n;
1031
+ }
1032
+ }
1033
+ }
1034
+
1035
+ done:
1036
+ if (watcher.fiber == Qnil) {
1037
+ switchpoint_result = backend_snooze();
1038
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
1039
+ }
1040
+
1041
+ RB_GC_GUARD(watcher.fiber);
1042
+ RB_GC_GUARD(switchpoint_result);
1043
+ RB_GC_GUARD(str);
884
1044
 
885
1045
  return INT2NUM(total);
886
1046
  error:
887
1047
  return RAISE_EXCEPTION(switchpoint_result);
888
1048
  }
889
- #endif
890
1049
 
891
1050
  VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
892
1051
  Backend_t *backend;
@@ -921,7 +1080,7 @@ VALUE Backend_sleep(VALUE self, VALUE duration) {
921
1080
  ev_timer_init(&watcher.timer, Backend_timer_callback, NUM2DBL(duration), 0.);
922
1081
  ev_timer_start(backend->ev_loop, &watcher.timer);
923
1082
 
924
- switchpoint_result = backend_await(backend);
1083
+ switchpoint_result = backend_await((struct Backend_base *)backend);
925
1084
 
926
1085
  ev_timer_stop(backend->ev_loop, &watcher.timer);
927
1086
  RAISE_IF_EXCEPTION(switchpoint_result);
@@ -949,7 +1108,7 @@ noreturn VALUE Backend_timer_loop(VALUE self, VALUE interval) {
949
1108
  VALUE switchpoint_result = Qnil;
950
1109
  ev_timer_init(&watcher.timer, Backend_timer_callback, sleep_duration, 0.);
951
1110
  ev_timer_start(backend->ev_loop, &watcher.timer);
952
- switchpoint_result = backend_await(backend);
1111
+ switchpoint_result = backend_await((struct Backend_base *)backend);
953
1112
  ev_timer_stop(backend->ev_loop, &watcher.timer);
954
1113
  RAISE_IF_EXCEPTION(switchpoint_result);
955
1114
  RB_GC_GUARD(switchpoint_result);
@@ -1066,7 +1225,7 @@ VALUE Backend_waitpid(VALUE self, VALUE pid) {
1066
1225
  ev_child_init(&watcher.child, Backend_child_callback, NUM2INT(pid), 0);
1067
1226
  ev_child_start(backend->ev_loop, &watcher.child);
1068
1227
 
1069
- switchpoint_result = backend_await(backend);
1228
+ switchpoint_result = backend_await((struct Backend_base *)backend);
1070
1229
 
1071
1230
  ev_child_stop(backend->ev_loop, &watcher.child);
1072
1231
  RAISE_IF_EXCEPTION(switchpoint_result);
@@ -1088,7 +1247,7 @@ VALUE Backend_wait_event(VALUE self, VALUE raise) {
1088
1247
  ev_async_init(&async, Backend_async_callback);
1089
1248
  ev_async_start(backend->ev_loop, &async);
1090
1249
 
1091
- switchpoint_result = backend_await(backend);
1250
+ switchpoint_result = backend_await((struct Backend_base *)backend);
1092
1251
 
1093
1252
  ev_async_stop(backend->ev_loop, &async);
1094
1253
  if (RTEST(raise)) RAISE_IF_EXCEPTION(switchpoint_result);
@@ -1113,10 +1272,8 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1113
1272
  result = Backend_write(self, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2));
1114
1273
  else if (op_type == SYM_send && op_len == 4)
1115
1274
  result = Backend_send(self, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2), RARRAY_AREF(op, 3));
1116
- #ifndef POLYPHONY_LINUX
1117
1275
  else if (op_type == SYM_splice && op_len == 4)
1118
1276
  result = Backend_splice(self, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2), RARRAY_AREF(op, 3));
1119
- #endif
1120
1277
  else
1121
1278
  rb_raise(rb_eRuntimeError, "Invalid op specified or bad op arity");
1122
1279
  }
@@ -1125,6 +1282,157 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1125
1282
  return result;
1126
1283
  }
1127
1284
 
1285
+ VALUE Backend_idle_gc_period_set(VALUE self, VALUE period) {
1286
+ Backend_t *backend;
1287
+ GetBackend(self, backend);
1288
+ backend->base.idle_gc_period = NUM2DBL(period);
1289
+ backend->base.idle_gc_last_time = current_time();
1290
+ return self;
1291
+ }
1292
+
1293
+ inline VALUE Backend_run_idle_tasks(VALUE self) {
1294
+ Backend_t *backend;
1295
+ GetBackend(self, backend);
1296
+ backend_run_idle_tasks(&backend->base);
1297
+ return self;
1298
+ }
1299
+
1300
+ inline int splice_chunks_write(Backend_t *backend, int fd, VALUE str, struct libev_rw_io *watcher, VALUE *result) {
1301
+ char *buf = RSTRING_PTR(str);
1302
+ int len = RSTRING_LEN(str);
1303
+ int left = len;
1304
+ while (left > 0) {
1305
+ ssize_t n = write(fd, buf, left);
1306
+ if (n < 0) {
1307
+ int err = errno;
1308
+ if ((err != EWOULDBLOCK && err != EAGAIN)) return err;
1309
+
1310
+ *result = libev_wait_rw_fd_with_watcher(backend, -1, fd, watcher);
1311
+ if (TEST_EXCEPTION(*result)) return -1;
1312
+ }
1313
+ else {
1314
+ buf += n;
1315
+ left -= n;
1316
+ }
1317
+ }
1318
+ return 0;
1319
+ }
1320
+
1321
+ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VALUE postfix, VALUE chunk_prefix, VALUE chunk_postfix, VALUE chunk_size) {
1322
+ Backend_t *backend;
1323
+ GetBackend(self, backend);
1324
+ int total = 0;
1325
+ int err = 0;
1326
+ VALUE result = Qnil;
1327
+
1328
+ rb_io_t *src_fptr;
1329
+ rb_io_t *dest_fptr;
1330
+
1331
+ VALUE underlying_io = rb_ivar_get(src, ID_ivar_io);
1332
+ if (underlying_io != Qnil) src = underlying_io;
1333
+ GetOpenFile(src, src_fptr);
1334
+ io_verify_blocking_mode(src_fptr, src, Qfalse);
1335
+
1336
+ underlying_io = rb_ivar_get(dest, ID_ivar_io);
1337
+ if (underlying_io != Qnil) dest = underlying_io;
1338
+ dest = rb_io_get_write_io(dest);
1339
+ GetOpenFile(dest, dest_fptr);
1340
+ io_verify_blocking_mode(dest_fptr, dest, Qfalse);
1341
+
1342
+ struct libev_rw_io watcher;
1343
+ watcher.ctx.fiber = Qnil;
1344
+ int maxlen = NUM2INT(chunk_size);
1345
+ VALUE str = Qnil;
1346
+ VALUE chunk_len_value = Qnil;
1347
+
1348
+ int pipefd[2] = { -1, -1 };
1349
+ if (pipe(pipefd) == -1) {
1350
+ err = errno;
1351
+ goto syscallerror;
1352
+ }
1353
+
1354
+ fcntl(pipefd[0], F_SETFL, O_NONBLOCK);
1355
+ fcntl(pipefd[1], F_SETFL, O_NONBLOCK);
1356
+
1357
+ if (prefix != Qnil) {
1358
+ int err = splice_chunks_write(backend, dest_fptr->fd, prefix, &watcher, &result);
1359
+ if (err == -1) goto error; else if (err) goto syscallerror;
1360
+ }
1361
+ while (1) {
1362
+ int chunk_len;
1363
+ // splice to pipe
1364
+ while (1) {
1365
+ chunk_len = splice(src_fptr->fd, 0, pipefd[1], 0, maxlen, 0);
1366
+ if (chunk_len < 0) {
1367
+ err = errno;
1368
+ if (err != EWOULDBLOCK && err != EAGAIN) goto syscallerror;
1369
+
1370
+ result = libev_wait_rw_fd_with_watcher(backend, src_fptr->fd, pipefd[1], &watcher);
1371
+ if (TEST_EXCEPTION(result)) goto error;
1372
+ }
1373
+ else {
1374
+ break;
1375
+ }
1376
+ }
1377
+ if (chunk_len == 0) break;
1378
+
1379
+ total += chunk_len;
1380
+ chunk_len_value = INT2NUM(chunk_len);
1381
+
1382
+ if (chunk_prefix != Qnil) {
1383
+ VALUE str = (TYPE(chunk_prefix) == T_STRING) ? chunk_prefix : rb_funcall(chunk_prefix, ID_call, 1, chunk_len_value);
1384
+ int err = splice_chunks_write(backend, dest_fptr->fd, str, &watcher, &result);
1385
+ if (err == -1) goto error; else if (err) goto syscallerror;
1386
+ }
1387
+
1388
+ int left = chunk_len;
1389
+ while (1) {
1390
+ int n = splice(pipefd[0], 0, dest_fptr->fd, 0, left, 0);
1391
+ if (n < 0) {
1392
+ err = errno;
1393
+ if (err != EWOULDBLOCK && err != EAGAIN) goto syscallerror;
1394
+
1395
+ result = libev_wait_rw_fd_with_watcher(backend, pipefd[0], dest_fptr->fd, &watcher);
1396
+ if (TEST_EXCEPTION(result)) goto error;
1397
+ }
1398
+ else {
1399
+ left -= n;
1400
+ if (left == 0) break;
1401
+ }
1402
+ }
1403
+
1404
+ if (chunk_postfix != Qnil) {
1405
+ VALUE str = (TYPE(chunk_postfix) == T_STRING) ? chunk_postfix : rb_funcall(chunk_postfix, ID_call, 1, chunk_len_value);
1406
+ int err = splice_chunks_write(backend, dest_fptr->fd, str, &watcher, &result);
1407
+ if (err == -1) goto error; else if (err) goto syscallerror;
1408
+ }
1409
+ }
1410
+
1411
+ if (postfix != Qnil) {
1412
+ int err = splice_chunks_write(backend, dest_fptr->fd, postfix, &watcher, &result);
1413
+ if (err == -1) goto error; else if (err) goto syscallerror;
1414
+ }
1415
+
1416
+ if (watcher.ctx.fiber == Qnil) {
1417
+ result = backend_snooze();
1418
+ if (TEST_EXCEPTION(result)) goto error;
1419
+ }
1420
+ RB_GC_GUARD(str);
1421
+ RB_GC_GUARD(chunk_len_value);
1422
+ RB_GC_GUARD(result);
1423
+ if (pipefd[0] != -1) close(pipefd[0]);
1424
+ if (pipefd[1] != -1) close(pipefd[1]);
1425
+ return INT2NUM(total);
1426
+ syscallerror:
1427
+ if (pipefd[0] != -1) close(pipefd[0]);
1428
+ if (pipefd[1] != -1) close(pipefd[1]);
1429
+ rb_syserr_fail(err, strerror(err));
1430
+ error:
1431
+ if (pipefd[0] != -1) close(pipefd[0]);
1432
+ if (pipefd[1] != -1) close(pipefd[1]);
1433
+ return RAISE_EXCEPTION(result);
1434
+ }
1435
+
1128
1436
  void Init_Backend() {
1129
1437
  ev_set_allocator(xrealloc);
1130
1438
 
@@ -1139,6 +1447,8 @@ void Init_Backend() {
1139
1447
  rb_define_method(cBackend, "break", Backend_wakeup, 0);
1140
1448
  rb_define_method(cBackend, "kind", Backend_kind, 0);
1141
1449
  rb_define_method(cBackend, "chain", Backend_chain, -1);
1450
+ rb_define_method(cBackend, "idle_gc_period=", Backend_idle_gc_period_set, 1);
1451
+ rb_define_method(cBackend, "splice_chunks", Backend_splice_chunks, 7);
1142
1452
 
1143
1453
  rb_define_method(cBackend, "accept", Backend_accept, 2);
1144
1454
  rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
@@ -1153,9 +1463,12 @@ void Init_Backend() {
1153
1463
  rb_define_method(cBackend, "sendv", Backend_sendv, 3);
1154
1464
  rb_define_method(cBackend, "sleep", Backend_sleep, 1);
1155
1465
 
1156
- #ifndef POLYPHONY_LINUX
1466
+ #ifdef POLYPHONY_LINUX
1157
1467
  rb_define_method(cBackend, "splice", Backend_splice, 3);
1158
1468
  rb_define_method(cBackend, "splice_to_eof", Backend_splice_to_eof, 3);
1469
+ #else
1470
+ rb_define_method(cBackend, "splice", Backend_fake_splice, 3);
1471
+ rb_define_method(cBackend, "splice_to_eof", Backend_fake_splice_to_eof, 3);
1159
1472
  #endif
1160
1473
 
1161
1474
  rb_define_method(cBackend, "timeout", Backend_timeout, -1);
@@ -1165,7 +1478,6 @@ void Init_Backend() {
1165
1478
  rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
1166
1479
  rb_define_method(cBackend, "write", Backend_write_m, -1);
1167
1480
 
1168
- ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
1169
1481
  SYM_libev = ID2SYM(rb_intern("libev"));
1170
1482
 
1171
1483
  SYM_send = ID2SYM(rb_intern("send"));