polyphony 0.53.1 → 0.57.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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"));