polyphony 0.53.0 → 0.56.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,42 +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
- VALUE SYM_libev;
60
+ #include "../libev/ev.h"
61
+ #include "backend_common.h"
61
62
 
62
- ID ID_ivar_is_nonblocking;
63
-
64
- // Since we need to ensure that fd's are non-blocking before every I/O
65
- // operation, here we improve upon Ruby's rb_io_set_nonblock by caching the
66
- // "nonblock" state in an instance variable. Calling rb_ivar_get on every read
67
- // is still much cheaper than doing a fcntl syscall on every read! Preliminary
68
- // benchmarks (with a "hello world" HTTP server) show throughput is improved
69
- // by 10-13%.
70
- inline void io_set_nonblock(rb_io_t *fptr, VALUE io) {
71
- VALUE is_nonblocking = rb_ivar_get(io, ID_ivar_is_nonblocking);
72
- if (is_nonblocking == Qtrue) return;
73
-
74
- rb_ivar_set(io, ID_ivar_is_nonblocking, Qtrue);
75
-
76
- #ifdef _WIN32
77
- rb_w32_set_nonblock(fptr->fd);
78
- #elif defined(F_GETFL)
79
- int oflags = fcntl(fptr->fd, F_GETFL);
80
- if ((oflags == -1) && (oflags & O_NONBLOCK)) return;
81
- oflags |= O_NONBLOCK;
82
- fcntl(fptr->fd, F_SETFL, oflags);
83
- #endif
84
- }
63
+ VALUE SYM_libev;
64
+ VALUE SYM_send;
65
+ VALUE SYM_splice;
66
+ VALUE SYM_write;
85
67
 
86
68
  typedef struct Backend_t {
87
- // common fields
88
- unsigned int currently_polling;
89
- unsigned int pending_count;
90
- unsigned int poll_no_wait_count;
69
+ struct Backend_base base;
91
70
 
92
71
  // implementation-specific fields
93
72
  struct ev_loop *ev_loop;
@@ -140,9 +119,10 @@ static VALUE Backend_initialize(VALUE self) {
140
119
  // block when no other watcher is active
141
120
  ev_unref(backend->ev_loop);
142
121
 
143
- backend->currently_polling = 0;
144
- backend->pending_count = 0;
145
- 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;
146
126
 
147
127
  return Qnil;
148
128
  }
@@ -173,32 +153,21 @@ VALUE Backend_post_fork(VALUE self) {
173
153
  return self;
174
154
  }
175
155
 
176
- unsigned int Backend_pending_count(VALUE self) {
156
+ inline unsigned int Backend_pending_count(VALUE self) {
177
157
  Backend_t *backend;
178
158
  GetBackend(self, backend);
179
159
 
180
- return backend->pending_count;
160
+ return backend->base.pending_count;
181
161
  }
182
162
 
183
163
  VALUE Backend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE runqueue) {
184
- int is_nowait = nowait == Qtrue;
185
164
  Backend_t *backend;
186
165
  GetBackend(self, backend);
187
166
 
188
- if (is_nowait) {
189
- backend->poll_no_wait_count++;
190
- if (backend->poll_no_wait_count < 10) return self;
191
-
192
- long runnable_count = Runqueue_len(runqueue);
193
- if (backend->poll_no_wait_count < runnable_count) return self;
194
- }
195
-
196
- backend->poll_no_wait_count = 0;
197
-
198
167
  COND_TRACE(2, SYM_fiber_event_poll_enter, current_fiber);
199
- backend->currently_polling = 1;
200
- ev_run(backend->ev_loop, is_nowait ? EVRUN_NOWAIT : EVRUN_ONCE);
201
- 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;
202
171
  COND_TRACE(2, SYM_fiber_event_poll_leave, current_fiber);
203
172
 
204
173
  return self;
@@ -208,7 +177,7 @@ VALUE Backend_wakeup(VALUE self) {
208
177
  Backend_t *backend;
209
178
  GetBackend(self, backend);
210
179
 
211
- if (backend->currently_polling) {
180
+ if (backend->base.currently_polling) {
212
181
  // Since the loop will run until at least one event has occurred, we signal
213
182
  // the selector's associated async watcher, which will cause the ev loop to
214
183
  // return. In contrast to using `ev_break` to break out of the loop, which
@@ -221,10 +190,6 @@ VALUE Backend_wakeup(VALUE self) {
221
190
  return Qnil;
222
191
  }
223
192
 
224
- #include "../libev/ev.h"
225
-
226
- #include "backend_common.h"
227
-
228
193
  struct libev_io {
229
194
  struct ev_io io;
230
195
  VALUE fiber;
@@ -245,7 +210,7 @@ VALUE libev_wait_fd_with_watcher(Backend_t *backend, int fd, struct libev_io *wa
245
210
  }
246
211
  ev_io_start(backend->ev_loop, &watcher->io);
247
212
 
248
- switchpoint_result = backend_await(backend);
213
+ switchpoint_result = backend_await((struct Backend_base *)backend);
249
214
 
250
215
  ev_io_stop(backend->ev_loop, &watcher->io);
251
216
  RB_GC_GUARD(switchpoint_result);
@@ -281,7 +246,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
281
246
  if (underlying_io != Qnil) io = underlying_io;
282
247
  GetOpenFile(io, fptr);
283
248
  rb_io_check_byte_readable(fptr);
284
- io_set_nonblock(fptr, io);
249
+ io_verify_blocking_mode(fptr, io, Qfalse);
285
250
  rectify_io_file_pos(fptr);
286
251
  watcher.fiber = Qnil;
287
252
  OBJ_TAINT(str);
@@ -298,7 +263,6 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
298
263
  }
299
264
  else {
300
265
  switchpoint_result = backend_snooze();
301
-
302
266
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
303
267
 
304
268
  if (n == 0) break; // EOF
@@ -353,7 +317,7 @@ VALUE Backend_read_loop(VALUE self, VALUE io) {
353
317
  if (underlying_io != Qnil) io = underlying_io;
354
318
  GetOpenFile(io, fptr);
355
319
  rb_io_check_byte_readable(fptr);
356
- io_set_nonblock(fptr, io);
320
+ io_verify_blocking_mode(fptr, io, Qfalse);
357
321
  rectify_io_file_pos(fptr);
358
322
  watcher.fiber = Qnil;
359
323
 
@@ -405,7 +369,7 @@ VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
405
369
  if (underlying_io != Qnil) io = underlying_io;
406
370
  GetOpenFile(io, fptr);
407
371
  rb_io_check_byte_readable(fptr);
408
- io_set_nonblock(fptr, io);
372
+ io_verify_blocking_mode(fptr, io, Qfalse);
409
373
  rectify_io_file_pos(fptr);
410
374
  watcher.fiber = Qnil;
411
375
 
@@ -453,7 +417,7 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
453
417
  GetBackend(self, backend);
454
418
  io = rb_io_get_write_io(io);
455
419
  GetOpenFile(io, fptr);
456
- io_set_nonblock(fptr, io);
420
+ io_verify_blocking_mode(fptr, io, Qfalse);
457
421
  watcher.fiber = Qnil;
458
422
 
459
423
  while (left > 0) {
@@ -503,7 +467,7 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
503
467
  GetBackend(self, backend);
504
468
  io = rb_io_get_write_io(io);
505
469
  GetOpenFile(io, fptr);
506
- io_set_nonblock(fptr, io);
470
+ io_verify_blocking_mode(fptr, io, Qfalse);
507
471
  watcher.fiber = Qnil;
508
472
 
509
473
  iov = malloc(iov_count * sizeof(struct iovec));
@@ -584,7 +548,7 @@ VALUE Backend_accept(VALUE self, VALUE server_socket, VALUE socket_class) {
584
548
 
585
549
  GetBackend(self, backend);
586
550
  GetOpenFile(server_socket, fptr);
587
- io_set_nonblock(fptr, server_socket);
551
+ io_verify_blocking_mode(fptr, server_socket, Qfalse);
588
552
  watcher.fiber = Qnil;
589
553
  while (1) {
590
554
  fd = accept(fptr->fd, &addr, &len);
@@ -612,7 +576,7 @@ VALUE Backend_accept(VALUE self, VALUE server_socket, VALUE socket_class) {
612
576
  fp->fd = fd;
613
577
  fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
614
578
  rb_io_ascii8bit_binmode(socket);
615
- io_set_nonblock(fp, socket);
579
+ io_verify_blocking_mode(fp, socket, Qfalse);
616
580
  rb_io_synchronized(fp);
617
581
 
618
582
  // if (rsock_do_not_reverse_lookup) {
@@ -641,7 +605,7 @@ VALUE Backend_accept_loop(VALUE self, VALUE server_socket, VALUE socket_class) {
641
605
 
642
606
  GetBackend(self, backend);
643
607
  GetOpenFile(server_socket, fptr);
644
- io_set_nonblock(fptr, server_socket);
608
+ io_verify_blocking_mode(fptr, server_socket, Qfalse);
645
609
  watcher.fiber = Qnil;
646
610
 
647
611
  while (1) {
@@ -669,7 +633,7 @@ VALUE Backend_accept_loop(VALUE self, VALUE server_socket, VALUE socket_class) {
669
633
  fp->fd = fd;
670
634
  fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
671
635
  rb_io_ascii8bit_binmode(socket);
672
- io_set_nonblock(fp, socket);
636
+ io_verify_blocking_mode(fp, socket, Qfalse);
673
637
  rb_io_synchronized(fp);
674
638
 
675
639
  rb_yield(socket);
@@ -697,7 +661,7 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
697
661
 
698
662
  GetBackend(self, backend);
699
663
  GetOpenFile(sock, fptr);
700
- io_set_nonblock(fptr, sock);
664
+ io_verify_blocking_mode(fptr, sock, Qfalse);
701
665
  watcher.fiber = Qnil;
702
666
 
703
667
  addr.sin_family = AF_INET;
@@ -740,7 +704,7 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
740
704
  GetBackend(self, backend);
741
705
  io = rb_io_get_write_io(io);
742
706
  GetOpenFile(io, fptr);
743
- io_set_nonblock(fptr, io);
707
+ io_verify_blocking_mode(fptr, io, Qfalse);
744
708
  watcher.fiber = Qnil;
745
709
 
746
710
  while (left > 0) {
@@ -773,6 +737,7 @@ error:
773
737
  return RAISE_EXCEPTION(switchpoint_result);
774
738
  }
775
739
 
740
+ #ifdef POLYPHONY_LINUX
776
741
  VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
777
742
  Backend_t *backend;
778
743
  struct libev_io watcher;
@@ -782,22 +747,18 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
782
747
  rb_io_t *dest_fptr;
783
748
  int len;
784
749
 
785
- #ifndef POLYPHONY_LINUX
786
- rb_raise(rb_eRuntimeError, "splice not supported");
787
- #endif
788
-
789
750
  GetBackend(self, backend);
790
751
 
791
752
  underlying_io = rb_ivar_get(src, ID_ivar_io);
792
753
  if (underlying_io != Qnil) src = underlying_io;
793
754
  GetOpenFile(src, src_fptr);
794
- io_set_nonblock(src_fptr, src);
755
+ io_verify_blocking_mode(src_fptr, src, Qfalse);
795
756
 
796
757
  underlying_io = rb_ivar_get(dest, ID_ivar_io);
797
758
  if (underlying_io != Qnil) dest = underlying_io;
798
759
  dest = rb_io_get_write_io(dest);
799
760
  GetOpenFile(dest, dest_fptr);
800
- io_set_nonblock(dest_fptr, dest);
761
+ io_verify_blocking_mode(dest_fptr, dest, Qfalse);
801
762
 
802
763
  watcher.fiber = Qnil;
803
764
  while (1) {
@@ -840,22 +801,18 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
840
801
  int len;
841
802
  int total = 0;
842
803
 
843
- #ifndef POLYPHONY_LINUX
844
- rb_raise(rb_eRuntimeError, "splice not supported");
845
- #endif
846
-
847
804
  GetBackend(self, backend);
848
805
 
849
806
  underlying_io = rb_ivar_get(src, ID_ivar_io);
850
807
  if (underlying_io != Qnil) src = underlying_io;
851
808
  GetOpenFile(src, src_fptr);
852
- io_set_nonblock(src_fptr, src);
809
+ io_verify_blocking_mode(src_fptr, src, Qfalse);
853
810
 
854
811
  underlying_io = rb_ivar_get(dest, ID_ivar_io);
855
812
  if (underlying_io != Qnil) dest = underlying_io;
856
813
  dest = rb_io_get_write_io(dest);
857
814
  GetOpenFile(dest, dest_fptr);
858
- io_set_nonblock(dest_fptr, dest);
815
+ io_verify_blocking_mode(dest_fptr, dest, Qfalse);
859
816
 
860
817
  watcher.fiber = Qnil;
861
818
  while (1) {
@@ -890,6 +847,158 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
890
847
  error:
891
848
  return RAISE_EXCEPTION(switchpoint_result);
892
849
  }
850
+ #endif
851
+
852
+ VALUE Backend_fake_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
853
+ Backend_t *backend;
854
+ struct libev_io watcher;
855
+ VALUE switchpoint_result = Qnil;
856
+ VALUE underlying_io;
857
+ rb_io_t *src_fptr;
858
+ rb_io_t *dest_fptr;
859
+ int len = NUM2INT(maxlen);
860
+ VALUE str = rb_str_new(0, len);
861
+ char *buf = RSTRING_PTR(str);
862
+ int left = 0;
863
+ int total = 0;
864
+
865
+ GetBackend(self, backend);
866
+
867
+ underlying_io = rb_ivar_get(src, ID_ivar_io);
868
+ if (underlying_io != Qnil) src = underlying_io;
869
+ GetOpenFile(src, src_fptr);
870
+ io_verify_blocking_mode(src_fptr, src, Qfalse);
871
+
872
+ underlying_io = rb_ivar_get(dest, ID_ivar_io);
873
+ if (underlying_io != Qnil) dest = underlying_io;
874
+ dest = rb_io_get_write_io(dest);
875
+ GetOpenFile(dest, dest_fptr);
876
+ io_verify_blocking_mode(dest_fptr, dest, Qfalse);
877
+
878
+ watcher.fiber = Qnil;
879
+
880
+ while (1) {
881
+ ssize_t n = read(src_fptr->fd, buf, len);
882
+ if (n < 0) {
883
+ int e = errno;
884
+ if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
885
+
886
+ switchpoint_result = libev_wait_fd_with_watcher(backend, src_fptr->fd, &watcher, EV_READ);
887
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
888
+ }
889
+ else {
890
+ total = left = n;
891
+ break;
892
+ }
893
+ }
894
+
895
+ while (left > 0) {
896
+ ssize_t n = write(dest_fptr->fd, buf, left);
897
+ if (n < 0) {
898
+ int e = errno;
899
+ if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
900
+
901
+ switchpoint_result = libev_wait_fd_with_watcher(backend, dest_fptr->fd, &watcher, EV_WRITE);
902
+
903
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
904
+ }
905
+ else {
906
+ buf += n;
907
+ left -= n;
908
+ }
909
+ }
910
+
911
+ if (watcher.fiber == Qnil) {
912
+ switchpoint_result = backend_snooze();
913
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
914
+ }
915
+
916
+ RB_GC_GUARD(watcher.fiber);
917
+ RB_GC_GUARD(switchpoint_result);
918
+ RB_GC_GUARD(str);
919
+
920
+ return INT2NUM(total);
921
+ error:
922
+ return RAISE_EXCEPTION(switchpoint_result);
923
+ }
924
+
925
+ VALUE Backend_fake_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
926
+ Backend_t *backend;
927
+ struct libev_io watcher;
928
+ VALUE switchpoint_result = Qnil;
929
+ VALUE underlying_io;
930
+ rb_io_t *src_fptr;
931
+ rb_io_t *dest_fptr;
932
+ int len = NUM2INT(maxlen);
933
+ VALUE str = rb_str_new(0, len);
934
+ char *buf = RSTRING_PTR(str);
935
+ int left = 0;
936
+ int total = 0;
937
+
938
+ GetBackend(self, backend);
939
+
940
+ underlying_io = rb_ivar_get(src, ID_ivar_io);
941
+ if (underlying_io != Qnil) src = underlying_io;
942
+ GetOpenFile(src, src_fptr);
943
+ io_verify_blocking_mode(src_fptr, src, Qfalse);
944
+
945
+ underlying_io = rb_ivar_get(dest, ID_ivar_io);
946
+ if (underlying_io != Qnil) dest = underlying_io;
947
+ dest = rb_io_get_write_io(dest);
948
+ GetOpenFile(dest, dest_fptr);
949
+ io_verify_blocking_mode(dest_fptr, dest, Qfalse);
950
+
951
+ watcher.fiber = Qnil;
952
+
953
+ while (1) {
954
+ char *ptr = buf;
955
+ while (1) {
956
+ ssize_t n = read(src_fptr->fd, ptr, len);
957
+ if (n < 0) {
958
+ int e = errno;
959
+ if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
960
+
961
+ switchpoint_result = libev_wait_fd_with_watcher(backend, src_fptr->fd, &watcher, EV_READ);
962
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
963
+ }
964
+ else if (n == 0) goto done;
965
+ else {
966
+ total += n;
967
+ left = n;
968
+ break;
969
+ }
970
+ }
971
+
972
+ while (left > 0) {
973
+ ssize_t n = write(dest_fptr->fd, ptr, left);
974
+ if (n < 0) {
975
+ int e = errno;
976
+ if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
977
+
978
+ switchpoint_result = libev_wait_fd_with_watcher(backend, dest_fptr->fd, &watcher, EV_WRITE);
979
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
980
+ }
981
+ else {
982
+ ptr += n;
983
+ left -= n;
984
+ }
985
+ }
986
+ }
987
+
988
+ done:
989
+ if (watcher.fiber == Qnil) {
990
+ switchpoint_result = backend_snooze();
991
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
992
+ }
993
+
994
+ RB_GC_GUARD(watcher.fiber);
995
+ RB_GC_GUARD(switchpoint_result);
996
+ RB_GC_GUARD(str);
997
+
998
+ return INT2NUM(total);
999
+ error:
1000
+ return RAISE_EXCEPTION(switchpoint_result);
1001
+ }
893
1002
 
894
1003
  VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
895
1004
  Backend_t *backend;
@@ -924,7 +1033,7 @@ VALUE Backend_sleep(VALUE self, VALUE duration) {
924
1033
  ev_timer_init(&watcher.timer, Backend_timer_callback, NUM2DBL(duration), 0.);
925
1034
  ev_timer_start(backend->ev_loop, &watcher.timer);
926
1035
 
927
- switchpoint_result = backend_await(backend);
1036
+ switchpoint_result = backend_await((struct Backend_base *)backend);
928
1037
 
929
1038
  ev_timer_stop(backend->ev_loop, &watcher.timer);
930
1039
  RAISE_IF_EXCEPTION(switchpoint_result);
@@ -952,7 +1061,7 @@ noreturn VALUE Backend_timer_loop(VALUE self, VALUE interval) {
952
1061
  VALUE switchpoint_result = Qnil;
953
1062
  ev_timer_init(&watcher.timer, Backend_timer_callback, sleep_duration, 0.);
954
1063
  ev_timer_start(backend->ev_loop, &watcher.timer);
955
- switchpoint_result = backend_await(backend);
1064
+ switchpoint_result = backend_await((struct Backend_base *)backend);
956
1065
  ev_timer_stop(backend->ev_loop, &watcher.timer);
957
1066
  RAISE_IF_EXCEPTION(switchpoint_result);
958
1067
  RB_GC_GUARD(switchpoint_result);
@@ -1069,7 +1178,7 @@ VALUE Backend_waitpid(VALUE self, VALUE pid) {
1069
1178
  ev_child_init(&watcher.child, Backend_child_callback, NUM2INT(pid), 0);
1070
1179
  ev_child_start(backend->ev_loop, &watcher.child);
1071
1180
 
1072
- switchpoint_result = backend_await(backend);
1181
+ switchpoint_result = backend_await((struct Backend_base *)backend);
1073
1182
 
1074
1183
  ev_child_stop(backend->ev_loop, &watcher.child);
1075
1184
  RAISE_IF_EXCEPTION(switchpoint_result);
@@ -1091,7 +1200,7 @@ VALUE Backend_wait_event(VALUE self, VALUE raise) {
1091
1200
  ev_async_init(&async, Backend_async_callback);
1092
1201
  ev_async_start(backend->ev_loop, &async);
1093
1202
 
1094
- switchpoint_result = backend_await(backend);
1203
+ switchpoint_result = backend_await((struct Backend_base *)backend);
1095
1204
 
1096
1205
  ev_async_stop(backend->ev_loop, &async);
1097
1206
  if (RTEST(raise)) RAISE_IF_EXCEPTION(switchpoint_result);
@@ -1103,6 +1212,46 @@ VALUE Backend_kind(VALUE self) {
1103
1212
  return SYM_libev;
1104
1213
  }
1105
1214
 
1215
+ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1216
+ VALUE result = Qnil;
1217
+ if (argc == 0) return result;
1218
+
1219
+ for (int i = 0; i < argc; i++) {
1220
+ VALUE op = argv[i];
1221
+ VALUE op_type = RARRAY_AREF(op, 0);
1222
+ VALUE op_len = RARRAY_LEN(op);
1223
+
1224
+ if (op_type == SYM_write && op_len == 3)
1225
+ result = Backend_write(self, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2));
1226
+ else if (op_type == SYM_send && op_len == 4)
1227
+ result = Backend_send(self, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2), RARRAY_AREF(op, 3));
1228
+ #ifdef POLYPHONY_LINUX
1229
+ else if (op_type == SYM_splice && op_len == 4)
1230
+ result = Backend_splice(self, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2), RARRAY_AREF(op, 3));
1231
+ #endif
1232
+ else
1233
+ rb_raise(rb_eRuntimeError, "Invalid op specified or bad op arity");
1234
+ }
1235
+
1236
+ RB_GC_GUARD(result);
1237
+ return result;
1238
+ }
1239
+
1240
+ VALUE Backend_idle_gc_period_set(VALUE self, VALUE period) {
1241
+ Backend_t *backend;
1242
+ GetBackend(self, backend);
1243
+ backend->base.idle_gc_period = NUM2DBL(period);
1244
+ backend->base.idle_gc_last_time = current_time();
1245
+ return self;
1246
+ }
1247
+
1248
+ inline VALUE Backend_run_idle_tasks(VALUE self) {
1249
+ Backend_t *backend;
1250
+ GetBackend(self, backend);
1251
+ backend_run_idle_tasks(&backend->base);
1252
+ return self;
1253
+ }
1254
+
1106
1255
  void Init_Backend() {
1107
1256
  ev_set_allocator(xrealloc);
1108
1257
 
@@ -1116,6 +1265,8 @@ void Init_Backend() {
1116
1265
  rb_define_method(cBackend, "poll", Backend_poll, 3);
1117
1266
  rb_define_method(cBackend, "break", Backend_wakeup, 0);
1118
1267
  rb_define_method(cBackend, "kind", Backend_kind, 0);
1268
+ rb_define_method(cBackend, "chain", Backend_chain, -1);
1269
+ rb_define_method(cBackend, "idle_gc_period=", Backend_idle_gc_period_set, 1);
1119
1270
 
1120
1271
  rb_define_method(cBackend, "accept", Backend_accept, 2);
1121
1272
  rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
@@ -1129,6 +1280,15 @@ void Init_Backend() {
1129
1280
  rb_define_method(cBackend, "send", Backend_send, 3);
1130
1281
  rb_define_method(cBackend, "sendv", Backend_sendv, 3);
1131
1282
  rb_define_method(cBackend, "sleep", Backend_sleep, 1);
1283
+
1284
+ #ifdef POLYPHONY_LINUX
1285
+ rb_define_method(cBackend, "splice", Backend_splice, 3);
1286
+ rb_define_method(cBackend, "splice_to_eof", Backend_splice_to_eof, 3);
1287
+ #else
1288
+ rb_define_method(cBackend, "splice", Backend_fake_splice, 3);
1289
+ rb_define_method(cBackend, "splice_to_eof", Backend_fake_splice_to_eof, 3);
1290
+ #endif
1291
+
1132
1292
  rb_define_method(cBackend, "timeout", Backend_timeout, -1);
1133
1293
  rb_define_method(cBackend, "timer_loop", Backend_timer_loop, 1);
1134
1294
  rb_define_method(cBackend, "wait_event", Backend_wait_event, 1);
@@ -1136,8 +1296,11 @@ void Init_Backend() {
1136
1296
  rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
1137
1297
  rb_define_method(cBackend, "write", Backend_write_m, -1);
1138
1298
 
1139
- ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
1140
1299
  SYM_libev = ID2SYM(rb_intern("libev"));
1300
+
1301
+ SYM_send = ID2SYM(rb_intern("send"));
1302
+ SYM_splice = ID2SYM(rb_intern("splice"));
1303
+ SYM_write = ID2SYM(rb_intern("write"));
1141
1304
  }
1142
1305
 
1143
1306
  #endif // POLYPHONY_BACKEND_LIBEV