polyphony 0.54.0 → 0.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,5 @@
1
1
  #include <stdlib.h>
2
+ #include <assert.h>
2
3
  #include "ruby.h"
3
4
  #include "polyphony.h"
4
5
  #include "backend_io_uring_context.h"
@@ -15,6 +16,7 @@ const char *op_type_to_str(enum op_type type) {
15
16
  case OP_POLL: return "POLL";
16
17
  case OP_ACCEPT: return "ACCEPT";
17
18
  case OP_CONNECT: return "CONNECT";
19
+ case OP_CHAIN: return "CHAIN";
18
20
  default: return "";
19
21
  };
20
22
  }
@@ -35,7 +37,7 @@ inline op_context_t *context_store_acquire(op_context_store_t *store, enum op_ty
35
37
  ctx = malloc(sizeof(op_context_t));
36
38
  }
37
39
  ctx->id = (++store->last_id);
38
-
40
+ // printf("acquire %p %d (%s)\n", ctx, ctx->id, op_type_to_str(type));
39
41
  ctx->prev = NULL;
40
42
  ctx->next = store->taken;
41
43
  if (store->taken) store->taken->prev = ctx;
@@ -44,13 +46,21 @@ inline op_context_t *context_store_acquire(op_context_store_t *store, enum op_ty
44
46
  ctx->type = type;
45
47
  ctx->fiber = rb_fiber_current();
46
48
  ctx->resume_value = Qnil;
47
- ctx->completed = 0;
49
+ ctx->ref_count = 2;
48
50
  ctx->result = 0;
49
51
 
50
52
  return ctx;
51
53
  }
52
54
 
53
- inline void context_store_release(op_context_store_t *store, op_context_t *ctx) {
55
+ // returns true if ctx was released
56
+ inline int context_store_release(op_context_store_t *store, op_context_t *ctx) {
57
+ // printf("release %p %d (%s, ref_count: %d)\n", ctx, ctx->id, op_type_to_str(ctx->type), ctx->ref_count);
58
+
59
+ assert(ctx->ref_count);
60
+
61
+ ctx->ref_count--;
62
+ if (ctx->ref_count) return 0;
63
+
54
64
  if (ctx->next) ctx->next->prev = ctx->prev;
55
65
  if (ctx->prev) ctx->prev->next = ctx->next;
56
66
  if (store->taken == ctx) store->taken = ctx->next;
@@ -59,6 +69,7 @@ inline void context_store_release(op_context_store_t *store, op_context_t *ctx)
59
69
  ctx->next = store->available;
60
70
  if (ctx->next) ctx->next->prev = ctx;
61
71
  store->available = ctx;
72
+ return 1;
62
73
  }
63
74
 
64
75
  void context_store_free(op_context_store_t *store) {
@@ -14,14 +14,15 @@ enum op_type {
14
14
  OP_TIMEOUT,
15
15
  OP_POLL,
16
16
  OP_ACCEPT,
17
- OP_CONNECT
17
+ OP_CONNECT,
18
+ OP_CHAIN
18
19
  };
19
20
 
20
21
  typedef struct op_context {
21
22
  struct op_context *prev;
22
23
  struct op_context *next;
23
24
  enum op_type type: 16;
24
- int completed : 16;
25
+ unsigned int ref_count : 16;
25
26
  int id;
26
27
  int result;
27
28
  VALUE fiber;
@@ -38,17 +39,16 @@ const char *op_type_to_str(enum op_type type);
38
39
 
39
40
  void context_store_initialize(op_context_store_t *store);
40
41
  op_context_t *context_store_acquire(op_context_store_t *store, enum op_type type);
41
- void context_store_release(op_context_store_t *store, op_context_t *ctx);
42
+ int context_store_release(op_context_store_t *store, op_context_t *ctx);
42
43
  void context_store_free(op_context_store_t *store);
43
44
 
44
- #define OP_CONTEXT_ACQUIRE(store, op_type) context_store_acquire(store, op_type)
45
- #define OP_CONTEXT_RELEASE(store, ctx) { \
46
- if (ctx->completed) {\
47
- context_store_release(store, ctx); \
48
- } \
49
- else { \
50
- ctx->completed = 1; \
51
- } \
45
+ inline unsigned int OP_CONTEXT_RELEASE(op_context_store_t *store, op_context_t *ctx) {
46
+ int completed = !ctx->ref_count;
47
+ if (ctx->ref_count)
48
+ ctx->ref_count -= 1;
49
+ else
50
+ context_store_release(store, ctx);
51
+ return completed;
52
52
  }
53
53
 
54
54
  #endif /* BACKEND_IO_URING_CONTEXT_H */
@@ -42,7 +42,6 @@ thread.
42
42
  #define _GNU_SOURCE 1
43
43
  #endif
44
44
 
45
- #include <fcntl.h>
46
45
  #include <netdb.h>
47
46
  #include <sys/socket.h>
48
47
  #include <sys/uio.h>
@@ -52,58 +51,45 @@ thread.
52
51
  #include <stdnoreturn.h>
53
52
  #include <sys/types.h>
54
53
  #include <sys/wait.h>
54
+ #include <fcntl.h>
55
55
 
56
56
  #include "polyphony.h"
57
57
  #include "../libev/ev.h"
58
58
  #include "ruby/io.h"
59
59
 
60
+ #include "../libev/ev.h"
61
+ #include "backend_common.h"
62
+
60
63
  VALUE SYM_libev;
61
64
  VALUE SYM_send;
62
65
  VALUE SYM_splice;
63
66
  VALUE SYM_write;
64
67
 
65
- ID ID_ivar_is_nonblocking;
66
-
67
- // Since we need to ensure that fd's are non-blocking before every I/O
68
- // operation, here we improve upon Ruby's rb_io_set_nonblock by caching the
69
- // "nonblock" state in an instance variable. Calling rb_ivar_get on every read
70
- // is still much cheaper than doing a fcntl syscall on every read! Preliminary
71
- // benchmarks (with a "hello world" HTTP server) show throughput is improved
72
- // by 10-13%.
73
- inline void io_set_nonblock(rb_io_t *fptr, VALUE io) {
74
- VALUE is_nonblocking = rb_ivar_get(io, ID_ivar_is_nonblocking);
75
- if (is_nonblocking == Qtrue) return;
76
-
77
- rb_ivar_set(io, ID_ivar_is_nonblocking, Qtrue);
78
-
79
- #ifdef _WIN32
80
- rb_w32_set_nonblock(fptr->fd);
81
- #elif defined(F_GETFL)
82
- int oflags = fcntl(fptr->fd, F_GETFL);
83
- if ((oflags == -1) && (oflags & O_NONBLOCK)) return;
84
- oflags |= O_NONBLOCK;
85
- fcntl(fptr->fd, F_SETFL, oflags);
86
- #endif
87
- }
88
-
89
68
  typedef struct Backend_t {
90
- // common fields
91
- unsigned int currently_polling;
92
- unsigned int pending_count;
93
- unsigned int poll_no_wait_count;
69
+ struct Backend_base base;
94
70
 
95
71
  // implementation-specific fields
96
72
  struct ev_loop *ev_loop;
97
73
  struct ev_async break_async;
98
74
  } Backend_t;
99
75
 
76
+ static void Backend_mark(void *ptr) {
77
+ Backend_t *backend = ptr;
78
+ backend_base_mark(&backend->base);
79
+ }
80
+
81
+ static void Backend_free(void *ptr) {
82
+ Backend_t *backend = ptr;
83
+ backend_base_finalize(&backend->base);
84
+ }
85
+
100
86
  static size_t Backend_size(const void *ptr) {
101
87
  return sizeof(Backend_t);
102
88
  }
103
89
 
104
90
  static const rb_data_type_t Backend_type = {
105
91
  "LibevBackend",
106
- {0, 0, Backend_size,},
92
+ {Backend_mark, Backend_free, Backend_size,},
107
93
  0, 0, RUBY_TYPED_FREE_IMMEDIATELY
108
94
  };
109
95
 
@@ -134,6 +120,8 @@ static VALUE Backend_initialize(VALUE self) {
134
120
  Backend_t *backend;
135
121
 
136
122
  GetBackend(self, backend);
123
+
124
+ backend_base_initialize(&backend->base);
137
125
  backend->ev_loop = libev_new_loop();
138
126
 
139
127
  // start async watcher used for breaking a poll op (from another thread)
@@ -143,10 +131,6 @@ static VALUE Backend_initialize(VALUE self) {
143
131
  // block when no other watcher is active
144
132
  ev_unref(backend->ev_loop);
145
133
 
146
- backend->currently_polling = 0;
147
- backend->pending_count = 0;
148
- backend->poll_no_wait_count = 0;
149
-
150
134
  return Qnil;
151
135
  }
152
136
 
@@ -176,42 +160,45 @@ VALUE Backend_post_fork(VALUE self) {
176
160
  return self;
177
161
  }
178
162
 
179
- unsigned int Backend_pending_count(VALUE self) {
163
+ inline VALUE Backend_poll(VALUE self, VALUE blocking) {
180
164
  Backend_t *backend;
181
165
  GetBackend(self, backend);
182
166
 
183
- return backend->pending_count;
167
+ COND_TRACE(&backend->base, 2, SYM_fiber_event_poll_enter, rb_fiber_current());
168
+ backend->base.currently_polling = 1;
169
+ ev_run(backend->ev_loop, blocking == Qtrue ? EVRUN_ONCE : EVRUN_NOWAIT);
170
+ backend->base.currently_polling = 0;
171
+ COND_TRACE(&backend->base, 2, SYM_fiber_event_poll_leave, rb_fiber_current());
172
+
173
+ return self;
184
174
  }
185
175
 
186
- VALUE Backend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE runqueue) {
187
- int is_nowait = nowait == Qtrue;
176
+ inline void Backend_schedule_fiber(VALUE thread, VALUE self, VALUE fiber, VALUE value, int prioritize) {
188
177
  Backend_t *backend;
189
178
  GetBackend(self, backend);
190
179
 
191
- if (is_nowait) {
192
- backend->poll_no_wait_count++;
193
- if (backend->poll_no_wait_count < 10) return self;
180
+ backend_base_schedule_fiber(thread, self, &backend->base, fiber, value, prioritize);
181
+ }
194
182
 
195
- long runnable_count = Runqueue_len(runqueue);
196
- if (backend->poll_no_wait_count < runnable_count) return self;
197
- }
183
+ inline void Backend_unschedule_fiber(VALUE self, VALUE fiber) {
184
+ Backend_t *backend;
185
+ GetBackend(self, backend);
198
186
 
199
- backend->poll_no_wait_count = 0;
187
+ runqueue_delete(&backend->base.runqueue, fiber);
188
+ }
200
189
 
201
- 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;
205
- COND_TRACE(2, SYM_fiber_event_poll_leave, current_fiber);
190
+ inline VALUE Backend_switch_fiber(VALUE self) {
191
+ Backend_t *backend;
192
+ GetBackend(self, backend);
206
193
 
207
- return self;
194
+ return backend_base_switch_fiber(self, &backend->base);
208
195
  }
209
196
 
210
197
  VALUE Backend_wakeup(VALUE self) {
211
198
  Backend_t *backend;
212
199
  GetBackend(self, backend);
213
200
 
214
- if (backend->currently_polling) {
201
+ if (backend->base.currently_polling) {
215
202
  // Since the loop will run until at least one event has occurred, we signal
216
203
  // the selector's associated async watcher, which will cause the ev loop to
217
204
  // return. In contrast to using `ev_break` to break out of the loop, which
@@ -224,9 +211,16 @@ VALUE Backend_wakeup(VALUE self) {
224
211
  return Qnil;
225
212
  }
226
213
 
227
- #include "../libev/ev.h"
214
+ inline struct backend_stats Backend_stats(VALUE self) {
215
+ Backend_t *backend;
216
+ GetBackend(self, backend);
228
217
 
229
- #include "backend_common.h"
218
+ return (struct backend_stats){
219
+ .scheduled_fibers = runqueue_len(&backend->base.runqueue),
220
+ .waiting_fibers = 0,
221
+ .pending_ops = backend->base.pending_count
222
+ };
223
+ }
230
224
 
231
225
  struct libev_io {
232
226
  struct ev_io io;
@@ -248,7 +242,7 @@ VALUE libev_wait_fd_with_watcher(Backend_t *backend, int fd, struct libev_io *wa
248
242
  }
249
243
  ev_io_start(backend->ev_loop, &watcher->io);
250
244
 
251
- switchpoint_result = backend_await(backend);
245
+ switchpoint_result = backend_await((struct Backend_base *)backend);
252
246
 
253
247
  ev_io_stop(backend->ev_loop, &watcher->io);
254
248
  RB_GC_GUARD(switchpoint_result);
@@ -284,7 +278,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
284
278
  if (underlying_io != Qnil) io = underlying_io;
285
279
  GetOpenFile(io, fptr);
286
280
  rb_io_check_byte_readable(fptr);
287
- io_set_nonblock(fptr, io);
281
+ io_verify_blocking_mode(fptr, io, Qfalse);
288
282
  rectify_io_file_pos(fptr);
289
283
  watcher.fiber = Qnil;
290
284
  OBJ_TAINT(str);
@@ -301,7 +295,6 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
301
295
  }
302
296
  else {
303
297
  switchpoint_result = backend_snooze();
304
-
305
298
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
306
299
 
307
300
  if (n == 0) break; // EOF
@@ -356,7 +349,7 @@ VALUE Backend_read_loop(VALUE self, VALUE io) {
356
349
  if (underlying_io != Qnil) io = underlying_io;
357
350
  GetOpenFile(io, fptr);
358
351
  rb_io_check_byte_readable(fptr);
359
- io_set_nonblock(fptr, io);
352
+ io_verify_blocking_mode(fptr, io, Qfalse);
360
353
  rectify_io_file_pos(fptr);
361
354
  watcher.fiber = Qnil;
362
355
 
@@ -408,7 +401,7 @@ VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
408
401
  if (underlying_io != Qnil) io = underlying_io;
409
402
  GetOpenFile(io, fptr);
410
403
  rb_io_check_byte_readable(fptr);
411
- io_set_nonblock(fptr, io);
404
+ io_verify_blocking_mode(fptr, io, Qfalse);
412
405
  rectify_io_file_pos(fptr);
413
406
  watcher.fiber = Qnil;
414
407
 
@@ -456,7 +449,7 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
456
449
  GetBackend(self, backend);
457
450
  io = rb_io_get_write_io(io);
458
451
  GetOpenFile(io, fptr);
459
- io_set_nonblock(fptr, io);
452
+ io_verify_blocking_mode(fptr, io, Qfalse);
460
453
  watcher.fiber = Qnil;
461
454
 
462
455
  while (left > 0) {
@@ -506,7 +499,7 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
506
499
  GetBackend(self, backend);
507
500
  io = rb_io_get_write_io(io);
508
501
  GetOpenFile(io, fptr);
509
- io_set_nonblock(fptr, io);
502
+ io_verify_blocking_mode(fptr, io, Qfalse);
510
503
  watcher.fiber = Qnil;
511
504
 
512
505
  iov = malloc(iov_count * sizeof(struct iovec));
@@ -587,7 +580,7 @@ VALUE Backend_accept(VALUE self, VALUE server_socket, VALUE socket_class) {
587
580
 
588
581
  GetBackend(self, backend);
589
582
  GetOpenFile(server_socket, fptr);
590
- io_set_nonblock(fptr, server_socket);
583
+ io_verify_blocking_mode(fptr, server_socket, Qfalse);
591
584
  watcher.fiber = Qnil;
592
585
  while (1) {
593
586
  fd = accept(fptr->fd, &addr, &len);
@@ -615,7 +608,7 @@ VALUE Backend_accept(VALUE self, VALUE server_socket, VALUE socket_class) {
615
608
  fp->fd = fd;
616
609
  fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
617
610
  rb_io_ascii8bit_binmode(socket);
618
- io_set_nonblock(fp, socket);
611
+ io_verify_blocking_mode(fp, socket, Qfalse);
619
612
  rb_io_synchronized(fp);
620
613
 
621
614
  // if (rsock_do_not_reverse_lookup) {
@@ -644,7 +637,7 @@ VALUE Backend_accept_loop(VALUE self, VALUE server_socket, VALUE socket_class) {
644
637
 
645
638
  GetBackend(self, backend);
646
639
  GetOpenFile(server_socket, fptr);
647
- io_set_nonblock(fptr, server_socket);
640
+ io_verify_blocking_mode(fptr, server_socket, Qfalse);
648
641
  watcher.fiber = Qnil;
649
642
 
650
643
  while (1) {
@@ -672,7 +665,7 @@ VALUE Backend_accept_loop(VALUE self, VALUE server_socket, VALUE socket_class) {
672
665
  fp->fd = fd;
673
666
  fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
674
667
  rb_io_ascii8bit_binmode(socket);
675
- io_set_nonblock(fp, socket);
668
+ io_verify_blocking_mode(fp, socket, Qfalse);
676
669
  rb_io_synchronized(fp);
677
670
 
678
671
  rb_yield(socket);
@@ -700,7 +693,7 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
700
693
 
701
694
  GetBackend(self, backend);
702
695
  GetOpenFile(sock, fptr);
703
- io_set_nonblock(fptr, sock);
696
+ io_verify_blocking_mode(fptr, sock, Qfalse);
704
697
  watcher.fiber = Qnil;
705
698
 
706
699
  addr.sin_family = AF_INET;
@@ -743,7 +736,7 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
743
736
  GetBackend(self, backend);
744
737
  io = rb_io_get_write_io(io);
745
738
  GetOpenFile(io, fptr);
746
- io_set_nonblock(fptr, io);
739
+ io_verify_blocking_mode(fptr, io, Qfalse);
747
740
  watcher.fiber = Qnil;
748
741
 
749
742
  while (left > 0) {
@@ -776,10 +769,63 @@ error:
776
769
  return RAISE_EXCEPTION(switchpoint_result);
777
770
  }
778
771
 
772
+ struct libev_rw_ctx {
773
+ int ref_count;
774
+ VALUE fiber;
775
+ };
776
+
777
+ struct libev_ref_count_io {
778
+ struct ev_io io;
779
+ struct libev_rw_ctx *ctx;
780
+ };
781
+
782
+ struct libev_rw_io {
783
+ struct libev_ref_count_io r;
784
+ struct libev_ref_count_io w;
785
+ struct libev_rw_ctx ctx;
786
+ };
787
+
788
+ void Backend_rw_io_callback(EV_P_ ev_io *w, int revents)
789
+ {
790
+ struct libev_ref_count_io *watcher = (struct libev_ref_count_io *)w;
791
+ int ref_count = watcher->ctx->ref_count--;
792
+ if (!ref_count)
793
+ Fiber_make_runnable(watcher->ctx->fiber, Qnil);
794
+ }
795
+
796
+ VALUE libev_wait_rw_fd_with_watcher(Backend_t *backend, int r_fd, int w_fd, struct libev_rw_io *watcher) {
797
+ VALUE switchpoint_result = Qnil;
798
+
799
+ if (watcher->ctx.fiber == Qnil) watcher->ctx.fiber = rb_fiber_current();
800
+ watcher->ctx.ref_count = 0;
801
+ if (r_fd != -1) {
802
+ ev_io_init(&watcher->r.io, Backend_rw_io_callback, r_fd, EV_READ);
803
+ ev_io_start(backend->ev_loop, &watcher->r.io);
804
+ watcher->r.ctx = &watcher->ctx;
805
+ watcher->ctx.ref_count++;
806
+ }
807
+ if (w_fd != -1) {
808
+ ev_io_init(&watcher->w.io, Backend_rw_io_callback, w_fd, EV_WRITE);
809
+ ev_io_start(backend->ev_loop, &watcher->w.io);
810
+ watcher->w.ctx = &watcher->ctx;
811
+ watcher->ctx.ref_count++;
812
+ }
813
+
814
+ switchpoint_result = backend_await((struct Backend_base *)backend);
815
+
816
+ if (r_fd != -1) ev_io_stop(backend->ev_loop, &watcher->r.io);
817
+ if (w_fd != -1) ev_io_stop(backend->ev_loop, &watcher->w.io);
818
+ RB_GC_GUARD(switchpoint_result);
819
+ return switchpoint_result;
820
+ }
821
+
822
+
823
+
824
+
779
825
  #ifdef POLYPHONY_LINUX
780
826
  VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
781
827
  Backend_t *backend;
782
- struct libev_io watcher;
828
+ struct libev_rw_io watcher;
783
829
  VALUE switchpoint_result = Qnil;
784
830
  VALUE underlying_io;
785
831
  rb_io_t *src_fptr;
@@ -791,25 +837,22 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
791
837
  underlying_io = rb_ivar_get(src, ID_ivar_io);
792
838
  if (underlying_io != Qnil) src = underlying_io;
793
839
  GetOpenFile(src, src_fptr);
794
- io_set_nonblock(src_fptr, src);
840
+ io_verify_blocking_mode(src_fptr, src, Qfalse);
795
841
 
796
842
  underlying_io = rb_ivar_get(dest, ID_ivar_io);
797
843
  if (underlying_io != Qnil) dest = underlying_io;
798
844
  dest = rb_io_get_write_io(dest);
799
845
  GetOpenFile(dest, dest_fptr);
800
- io_set_nonblock(dest_fptr, dest);
846
+ io_verify_blocking_mode(dest_fptr, dest, Qfalse);
801
847
 
802
- watcher.fiber = Qnil;
848
+ watcher.ctx.fiber = Qnil;
803
849
  while (1) {
804
850
  len = splice(src_fptr->fd, 0, dest_fptr->fd, 0, NUM2INT(maxlen), 0);
805
851
  if (len < 0) {
806
852
  int e = errno;
807
853
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
808
854
 
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);
855
+ switchpoint_result = libev_wait_rw_fd_with_watcher(backend, src_fptr->fd, dest_fptr->fd, &watcher);
813
856
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
814
857
  }
815
858
  else {
@@ -817,12 +860,12 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
817
860
  }
818
861
  }
819
862
 
820
- if (watcher.fiber == Qnil) {
863
+ if (watcher.ctx.fiber == Qnil) {
821
864
  switchpoint_result = backend_snooze();
822
865
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
823
866
  }
824
867
 
825
- RB_GC_GUARD(watcher.fiber);
868
+ RB_GC_GUARD(watcher.ctx.fiber);
826
869
  RB_GC_GUARD(switchpoint_result);
827
870
 
828
871
  return INT2NUM(len);
@@ -832,7 +875,7 @@ error:
832
875
 
833
876
  VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
834
877
  Backend_t *backend;
835
- struct libev_io watcher;
878
+ struct libev_rw_io watcher;
836
879
  VALUE switchpoint_result = Qnil;
837
880
  VALUE underlying_io;
838
881
  rb_io_t *src_fptr;
@@ -845,25 +888,22 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
845
888
  underlying_io = rb_ivar_get(src, ID_ivar_io);
846
889
  if (underlying_io != Qnil) src = underlying_io;
847
890
  GetOpenFile(src, src_fptr);
848
- io_set_nonblock(src_fptr, src);
891
+ io_verify_blocking_mode(src_fptr, src, Qfalse);
849
892
 
850
893
  underlying_io = rb_ivar_get(dest, ID_ivar_io);
851
894
  if (underlying_io != Qnil) dest = underlying_io;
852
895
  dest = rb_io_get_write_io(dest);
853
896
  GetOpenFile(dest, dest_fptr);
854
- io_set_nonblock(dest_fptr, dest);
897
+ io_verify_blocking_mode(dest_fptr, dest, Qfalse);
855
898
 
856
- watcher.fiber = Qnil;
899
+ watcher.ctx.fiber = Qnil;
857
900
  while (1) {
858
901
  len = splice(src_fptr->fd, 0, dest_fptr->fd, 0, NUM2INT(maxlen), 0);
859
902
  if (len < 0) {
860
903
  int e = errno;
861
904
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
862
905
 
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);
906
+ switchpoint_result = libev_wait_rw_fd_with_watcher(backend, src_fptr->fd, dest_fptr->fd, &watcher);
867
907
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
868
908
  }
869
909
  else if (len == 0) {
@@ -874,6 +914,157 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
874
914
  }
875
915
  }
876
916
 
917
+ if (watcher.ctx.fiber == Qnil) {
918
+ switchpoint_result = backend_snooze();
919
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
920
+ }
921
+
922
+ RB_GC_GUARD(watcher.ctx.fiber);
923
+ RB_GC_GUARD(switchpoint_result);
924
+
925
+ return INT2NUM(total);
926
+ error:
927
+ return RAISE_EXCEPTION(switchpoint_result);
928
+ }
929
+ #endif
930
+
931
+ VALUE Backend_fake_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
932
+ Backend_t *backend;
933
+ struct libev_io watcher;
934
+ VALUE switchpoint_result = Qnil;
935
+ VALUE underlying_io;
936
+ rb_io_t *src_fptr;
937
+ rb_io_t *dest_fptr;
938
+ int len = NUM2INT(maxlen);
939
+ VALUE str = rb_str_new(0, len);
940
+ char *buf = RSTRING_PTR(str);
941
+ int left = 0;
942
+ int total = 0;
943
+
944
+ GetBackend(self, backend);
945
+
946
+ underlying_io = rb_ivar_get(src, ID_ivar_io);
947
+ if (underlying_io != Qnil) src = underlying_io;
948
+ GetOpenFile(src, src_fptr);
949
+ io_verify_blocking_mode(src_fptr, src, Qfalse);
950
+
951
+ underlying_io = rb_ivar_get(dest, ID_ivar_io);
952
+ if (underlying_io != Qnil) dest = underlying_io;
953
+ dest = rb_io_get_write_io(dest);
954
+ GetOpenFile(dest, dest_fptr);
955
+ io_verify_blocking_mode(dest_fptr, dest, Qfalse);
956
+
957
+ watcher.fiber = Qnil;
958
+
959
+ while (1) {
960
+ ssize_t n = read(src_fptr->fd, buf, len);
961
+ if (n < 0) {
962
+ int e = errno;
963
+ if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
964
+
965
+ switchpoint_result = libev_wait_fd_with_watcher(backend, src_fptr->fd, &watcher, EV_READ);
966
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
967
+ }
968
+ else {
969
+ total = left = n;
970
+ break;
971
+ }
972
+ }
973
+
974
+ while (left > 0) {
975
+ ssize_t n = write(dest_fptr->fd, buf, left);
976
+ if (n < 0) {
977
+ int e = errno;
978
+ if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
979
+
980
+ switchpoint_result = libev_wait_fd_with_watcher(backend, dest_fptr->fd, &watcher, EV_WRITE);
981
+
982
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
983
+ }
984
+ else {
985
+ buf += n;
986
+ left -= n;
987
+ }
988
+ }
989
+
990
+ if (watcher.fiber == Qnil) {
991
+ switchpoint_result = backend_snooze();
992
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
993
+ }
994
+
995
+ RB_GC_GUARD(watcher.fiber);
996
+ RB_GC_GUARD(switchpoint_result);
997
+ RB_GC_GUARD(str);
998
+
999
+ return INT2NUM(total);
1000
+ error:
1001
+ return RAISE_EXCEPTION(switchpoint_result);
1002
+ }
1003
+
1004
+ VALUE Backend_fake_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
1005
+ Backend_t *backend;
1006
+ struct libev_io watcher;
1007
+ VALUE switchpoint_result = Qnil;
1008
+ VALUE underlying_io;
1009
+ rb_io_t *src_fptr;
1010
+ rb_io_t *dest_fptr;
1011
+ int len = NUM2INT(maxlen);
1012
+ VALUE str = rb_str_new(0, len);
1013
+ char *buf = RSTRING_PTR(str);
1014
+ int left = 0;
1015
+ int total = 0;
1016
+
1017
+ GetBackend(self, backend);
1018
+
1019
+ underlying_io = rb_ivar_get(src, ID_ivar_io);
1020
+ if (underlying_io != Qnil) src = underlying_io;
1021
+ GetOpenFile(src, src_fptr);
1022
+ io_verify_blocking_mode(src_fptr, src, Qfalse);
1023
+
1024
+ underlying_io = rb_ivar_get(dest, ID_ivar_io);
1025
+ if (underlying_io != Qnil) dest = underlying_io;
1026
+ dest = rb_io_get_write_io(dest);
1027
+ GetOpenFile(dest, dest_fptr);
1028
+ io_verify_blocking_mode(dest_fptr, dest, Qfalse);
1029
+
1030
+ watcher.fiber = Qnil;
1031
+
1032
+ while (1) {
1033
+ char *ptr = buf;
1034
+ while (1) {
1035
+ ssize_t n = read(src_fptr->fd, ptr, len);
1036
+ if (n < 0) {
1037
+ int e = errno;
1038
+ if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
1039
+
1040
+ switchpoint_result = libev_wait_fd_with_watcher(backend, src_fptr->fd, &watcher, EV_READ);
1041
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
1042
+ }
1043
+ else if (n == 0) goto done;
1044
+ else {
1045
+ total += n;
1046
+ left = n;
1047
+ break;
1048
+ }
1049
+ }
1050
+
1051
+ while (left > 0) {
1052
+ ssize_t n = write(dest_fptr->fd, ptr, left);
1053
+ if (n < 0) {
1054
+ int e = errno;
1055
+ if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
1056
+
1057
+ switchpoint_result = libev_wait_fd_with_watcher(backend, dest_fptr->fd, &watcher, EV_WRITE);
1058
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
1059
+ }
1060
+ else {
1061
+ ptr += n;
1062
+ left -= n;
1063
+ }
1064
+ }
1065
+ }
1066
+
1067
+ done:
877
1068
  if (watcher.fiber == Qnil) {
878
1069
  switchpoint_result = backend_snooze();
879
1070
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
@@ -881,12 +1072,12 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
881
1072
 
882
1073
  RB_GC_GUARD(watcher.fiber);
883
1074
  RB_GC_GUARD(switchpoint_result);
1075
+ RB_GC_GUARD(str);
884
1076
 
885
1077
  return INT2NUM(total);
886
1078
  error:
887
1079
  return RAISE_EXCEPTION(switchpoint_result);
888
1080
  }
889
- #endif
890
1081
 
891
1082
  VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
892
1083
  Backend_t *backend;
@@ -921,7 +1112,7 @@ VALUE Backend_sleep(VALUE self, VALUE duration) {
921
1112
  ev_timer_init(&watcher.timer, Backend_timer_callback, NUM2DBL(duration), 0.);
922
1113
  ev_timer_start(backend->ev_loop, &watcher.timer);
923
1114
 
924
- switchpoint_result = backend_await(backend);
1115
+ switchpoint_result = backend_await((struct Backend_base *)backend);
925
1116
 
926
1117
  ev_timer_stop(backend->ev_loop, &watcher.timer);
927
1118
  RAISE_IF_EXCEPTION(switchpoint_result);
@@ -949,7 +1140,7 @@ noreturn VALUE Backend_timer_loop(VALUE self, VALUE interval) {
949
1140
  VALUE switchpoint_result = Qnil;
950
1141
  ev_timer_init(&watcher.timer, Backend_timer_callback, sleep_duration, 0.);
951
1142
  ev_timer_start(backend->ev_loop, &watcher.timer);
952
- switchpoint_result = backend_await(backend);
1143
+ switchpoint_result = backend_await((struct Backend_base *)backend);
953
1144
  ev_timer_stop(backend->ev_loop, &watcher.timer);
954
1145
  RAISE_IF_EXCEPTION(switchpoint_result);
955
1146
  RB_GC_GUARD(switchpoint_result);
@@ -1066,7 +1257,7 @@ VALUE Backend_waitpid(VALUE self, VALUE pid) {
1066
1257
  ev_child_init(&watcher.child, Backend_child_callback, NUM2INT(pid), 0);
1067
1258
  ev_child_start(backend->ev_loop, &watcher.child);
1068
1259
 
1069
- switchpoint_result = backend_await(backend);
1260
+ switchpoint_result = backend_await((struct Backend_base *)backend);
1070
1261
 
1071
1262
  ev_child_stop(backend->ev_loop, &watcher.child);
1072
1263
  RAISE_IF_EXCEPTION(switchpoint_result);
@@ -1088,7 +1279,7 @@ VALUE Backend_wait_event(VALUE self, VALUE raise) {
1088
1279
  ev_async_init(&async, Backend_async_callback);
1089
1280
  ev_async_start(backend->ev_loop, &async);
1090
1281
 
1091
- switchpoint_result = backend_await(backend);
1282
+ switchpoint_result = backend_await((struct Backend_base *)backend);
1092
1283
 
1093
1284
  ev_async_stop(backend->ev_loop, &async);
1094
1285
  if (RTEST(raise)) RAISE_IF_EXCEPTION(switchpoint_result);
@@ -1113,10 +1304,8 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1113
1304
  result = Backend_write(self, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2));
1114
1305
  else if (op_type == SYM_send && op_len == 4)
1115
1306
  result = Backend_send(self, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2), RARRAY_AREF(op, 3));
1116
- #ifdef POLYPHONY_LINUX
1117
1307
  else if (op_type == SYM_splice && op_len == 4)
1118
1308
  result = Backend_splice(self, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2), RARRAY_AREF(op, 3));
1119
- #endif
1120
1309
  else
1121
1310
  rb_raise(rb_eRuntimeError, "Invalid op specified or bad op arity");
1122
1311
  }
@@ -1125,6 +1314,179 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1125
1314
  return result;
1126
1315
  }
1127
1316
 
1317
+ VALUE Backend_idle_gc_period_set(VALUE self, VALUE period) {
1318
+ Backend_t *backend;
1319
+ GetBackend(self, backend);
1320
+ backend->base.idle_gc_period = NUM2DBL(period);
1321
+ backend->base.idle_gc_last_time = current_time();
1322
+ return self;
1323
+ }
1324
+
1325
+ VALUE Backend_idle_proc_set(VALUE self, VALUE block) {
1326
+ Backend_t *backend;
1327
+ GetBackend(self, backend);
1328
+ backend->base.idle_proc = block;
1329
+ return self;
1330
+ }
1331
+
1332
+ inline VALUE Backend_run_idle_tasks(VALUE self) {
1333
+ Backend_t *backend;
1334
+ GetBackend(self, backend);
1335
+ backend_run_idle_tasks(&backend->base);
1336
+ return self;
1337
+ }
1338
+
1339
+ inline int splice_chunks_write(Backend_t *backend, int fd, VALUE str, struct libev_rw_io *watcher, VALUE *result) {
1340
+ char *buf = RSTRING_PTR(str);
1341
+ int len = RSTRING_LEN(str);
1342
+ int left = len;
1343
+ while (left > 0) {
1344
+ ssize_t n = write(fd, buf, left);
1345
+ if (n < 0) {
1346
+ int err = errno;
1347
+ if ((err != EWOULDBLOCK && err != EAGAIN)) return err;
1348
+
1349
+ *result = libev_wait_rw_fd_with_watcher(backend, -1, fd, watcher);
1350
+ if (TEST_EXCEPTION(*result)) return -1;
1351
+ }
1352
+ else {
1353
+ buf += n;
1354
+ left -= n;
1355
+ }
1356
+ }
1357
+ return 0;
1358
+ }
1359
+
1360
+ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VALUE postfix, VALUE chunk_prefix, VALUE chunk_postfix, VALUE chunk_size) {
1361
+ Backend_t *backend;
1362
+ GetBackend(self, backend);
1363
+ int total = 0;
1364
+ int err = 0;
1365
+ VALUE result = Qnil;
1366
+
1367
+ rb_io_t *src_fptr;
1368
+ rb_io_t *dest_fptr;
1369
+
1370
+ VALUE underlying_io = rb_ivar_get(src, ID_ivar_io);
1371
+ if (underlying_io != Qnil) src = underlying_io;
1372
+ GetOpenFile(src, src_fptr);
1373
+ io_verify_blocking_mode(src_fptr, src, Qfalse);
1374
+
1375
+ underlying_io = rb_ivar_get(dest, ID_ivar_io);
1376
+ if (underlying_io != Qnil) dest = underlying_io;
1377
+ dest = rb_io_get_write_io(dest);
1378
+ GetOpenFile(dest, dest_fptr);
1379
+ io_verify_blocking_mode(dest_fptr, dest, Qfalse);
1380
+
1381
+ struct libev_rw_io watcher;
1382
+ watcher.ctx.fiber = Qnil;
1383
+ int maxlen = NUM2INT(chunk_size);
1384
+ VALUE str = Qnil;
1385
+ VALUE chunk_len_value = Qnil;
1386
+
1387
+ int pipefd[2] = { -1, -1 };
1388
+ if (pipe(pipefd) == -1) {
1389
+ err = errno;
1390
+ goto syscallerror;
1391
+ }
1392
+
1393
+ fcntl(pipefd[0], F_SETFL, O_NONBLOCK);
1394
+ fcntl(pipefd[1], F_SETFL, O_NONBLOCK);
1395
+
1396
+ if (prefix != Qnil) {
1397
+ int err = splice_chunks_write(backend, dest_fptr->fd, prefix, &watcher, &result);
1398
+ if (err == -1) goto error; else if (err) goto syscallerror;
1399
+ }
1400
+ while (1) {
1401
+ int chunk_len;
1402
+ // splice to pipe
1403
+ while (1) {
1404
+ chunk_len = splice(src_fptr->fd, 0, pipefd[1], 0, maxlen, 0);
1405
+ if (chunk_len < 0) {
1406
+ err = errno;
1407
+ if (err != EWOULDBLOCK && err != EAGAIN) goto syscallerror;
1408
+
1409
+ result = libev_wait_rw_fd_with_watcher(backend, src_fptr->fd, pipefd[1], &watcher);
1410
+ if (TEST_EXCEPTION(result)) goto error;
1411
+ }
1412
+ else {
1413
+ break;
1414
+ }
1415
+ }
1416
+ if (chunk_len == 0) break;
1417
+
1418
+ total += chunk_len;
1419
+ chunk_len_value = INT2NUM(chunk_len);
1420
+
1421
+ if (chunk_prefix != Qnil) {
1422
+ VALUE str = (TYPE(chunk_prefix) == T_STRING) ? chunk_prefix : rb_funcall(chunk_prefix, ID_call, 1, chunk_len_value);
1423
+ int err = splice_chunks_write(backend, dest_fptr->fd, str, &watcher, &result);
1424
+ if (err == -1) goto error; else if (err) goto syscallerror;
1425
+ }
1426
+
1427
+ int left = chunk_len;
1428
+ while (1) {
1429
+ int n = splice(pipefd[0], 0, dest_fptr->fd, 0, left, 0);
1430
+ if (n < 0) {
1431
+ err = errno;
1432
+ if (err != EWOULDBLOCK && err != EAGAIN) goto syscallerror;
1433
+
1434
+ result = libev_wait_rw_fd_with_watcher(backend, pipefd[0], dest_fptr->fd, &watcher);
1435
+ if (TEST_EXCEPTION(result)) goto error;
1436
+ }
1437
+ else {
1438
+ left -= n;
1439
+ if (left == 0) break;
1440
+ }
1441
+ }
1442
+
1443
+ if (chunk_postfix != Qnil) {
1444
+ VALUE str = (TYPE(chunk_postfix) == T_STRING) ? chunk_postfix : rb_funcall(chunk_postfix, ID_call, 1, chunk_len_value);
1445
+ int err = splice_chunks_write(backend, dest_fptr->fd, str, &watcher, &result);
1446
+ if (err == -1) goto error; else if (err) goto syscallerror;
1447
+ }
1448
+ }
1449
+
1450
+ if (postfix != Qnil) {
1451
+ int err = splice_chunks_write(backend, dest_fptr->fd, postfix, &watcher, &result);
1452
+ if (err == -1) goto error; else if (err) goto syscallerror;
1453
+ }
1454
+
1455
+ if (watcher.ctx.fiber == Qnil) {
1456
+ result = backend_snooze();
1457
+ if (TEST_EXCEPTION(result)) goto error;
1458
+ }
1459
+ RB_GC_GUARD(str);
1460
+ RB_GC_GUARD(chunk_len_value);
1461
+ RB_GC_GUARD(result);
1462
+ if (pipefd[0] != -1) close(pipefd[0]);
1463
+ if (pipefd[1] != -1) close(pipefd[1]);
1464
+ return INT2NUM(total);
1465
+ syscallerror:
1466
+ if (pipefd[0] != -1) close(pipefd[0]);
1467
+ if (pipefd[1] != -1) close(pipefd[1]);
1468
+ rb_syserr_fail(err, strerror(err));
1469
+ error:
1470
+ if (pipefd[0] != -1) close(pipefd[0]);
1471
+ if (pipefd[1] != -1) close(pipefd[1]);
1472
+ return RAISE_EXCEPTION(result);
1473
+ }
1474
+
1475
+ VALUE Backend_trace(int argc, VALUE *argv, VALUE self) {
1476
+ Backend_t *backend;
1477
+ GetBackend(self, backend);
1478
+ backend_trace(&backend->base, argc, argv);
1479
+ return self;
1480
+ }
1481
+
1482
+ VALUE Backend_trace_proc_set(VALUE self, VALUE block) {
1483
+ Backend_t *backend;
1484
+ GetBackend(self, backend);
1485
+
1486
+ backend->base.trace_proc = block;
1487
+ return self;
1488
+ }
1489
+
1128
1490
  void Init_Backend() {
1129
1491
  ev_set_allocator(xrealloc);
1130
1492
 
@@ -1134,11 +1496,16 @@ void Init_Backend() {
1134
1496
  rb_define_method(cBackend, "initialize", Backend_initialize, 0);
1135
1497
  rb_define_method(cBackend, "finalize", Backend_finalize, 0);
1136
1498
  rb_define_method(cBackend, "post_fork", Backend_post_fork, 0);
1499
+ rb_define_method(cBackend, "trace", Backend_trace, -1);
1500
+ rb_define_method(cBackend, "trace_proc=", Backend_trace_proc_set, 1);
1137
1501
 
1138
- rb_define_method(cBackend, "poll", Backend_poll, 3);
1502
+ rb_define_method(cBackend, "poll", Backend_poll, 1);
1139
1503
  rb_define_method(cBackend, "break", Backend_wakeup, 0);
1140
1504
  rb_define_method(cBackend, "kind", Backend_kind, 0);
1141
1505
  rb_define_method(cBackend, "chain", Backend_chain, -1);
1506
+ rb_define_method(cBackend, "idle_gc_period=", Backend_idle_gc_period_set, 1);
1507
+ rb_define_method(cBackend, "idle_proc=", Backend_idle_proc_set, 1);
1508
+ rb_define_method(cBackend, "splice_chunks", Backend_splice_chunks, 7);
1142
1509
 
1143
1510
  rb_define_method(cBackend, "accept", Backend_accept, 2);
1144
1511
  rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
@@ -1156,6 +1523,9 @@ void Init_Backend() {
1156
1523
  #ifdef POLYPHONY_LINUX
1157
1524
  rb_define_method(cBackend, "splice", Backend_splice, 3);
1158
1525
  rb_define_method(cBackend, "splice_to_eof", Backend_splice_to_eof, 3);
1526
+ #else
1527
+ rb_define_method(cBackend, "splice", Backend_fake_splice, 3);
1528
+ rb_define_method(cBackend, "splice_to_eof", Backend_fake_splice_to_eof, 3);
1159
1529
  #endif
1160
1530
 
1161
1531
  rb_define_method(cBackend, "timeout", Backend_timeout, -1);
@@ -1165,7 +1535,6 @@ void Init_Backend() {
1165
1535
  rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
1166
1536
  rb_define_method(cBackend, "write", Backend_write_m, -1);
1167
1537
 
1168
- ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
1169
1538
  SYM_libev = ID2SYM(rb_intern("libev"));
1170
1539
 
1171
1540
  SYM_send = ID2SYM(rb_intern("send"));