polyphony 0.54.0 → 0.59

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,5 @@
1
1
  #include <stdlib.h>
2
+ #include <assert.h>
2
3
  #include "ruby.h"
3
4
  #include "polyphony.h"
4
5
  #include "backend_io_uring_context.h"
@@ -15,6 +16,7 @@ const char *op_type_to_str(enum op_type type) {
15
16
  case OP_POLL: return "POLL";
16
17
  case OP_ACCEPT: return "ACCEPT";
17
18
  case OP_CONNECT: return "CONNECT";
19
+ case OP_CHAIN: return "CHAIN";
18
20
  default: return "";
19
21
  };
20
22
  }
@@ -35,7 +37,7 @@ inline op_context_t *context_store_acquire(op_context_store_t *store, enum op_ty
35
37
  ctx = malloc(sizeof(op_context_t));
36
38
  }
37
39
  ctx->id = (++store->last_id);
38
-
40
+ // printf("acquire %p %d (%s)\n", ctx, ctx->id, op_type_to_str(type));
39
41
  ctx->prev = NULL;
40
42
  ctx->next = store->taken;
41
43
  if (store->taken) store->taken->prev = ctx;
@@ -44,13 +46,21 @@ inline op_context_t *context_store_acquire(op_context_store_t *store, enum op_ty
44
46
  ctx->type = type;
45
47
  ctx->fiber = rb_fiber_current();
46
48
  ctx->resume_value = Qnil;
47
- ctx->completed = 0;
49
+ ctx->ref_count = 2;
48
50
  ctx->result = 0;
49
51
 
50
52
  return ctx;
51
53
  }
52
54
 
53
- inline void context_store_release(op_context_store_t *store, op_context_t *ctx) {
55
+ // returns true if ctx was released
56
+ inline int context_store_release(op_context_store_t *store, op_context_t *ctx) {
57
+ // printf("release %p %d (%s, ref_count: %d)\n", ctx, ctx->id, op_type_to_str(ctx->type), ctx->ref_count);
58
+
59
+ assert(ctx->ref_count);
60
+
61
+ ctx->ref_count--;
62
+ if (ctx->ref_count) return 0;
63
+
54
64
  if (ctx->next) ctx->next->prev = ctx->prev;
55
65
  if (ctx->prev) ctx->prev->next = ctx->next;
56
66
  if (store->taken == ctx) store->taken = ctx->next;
@@ -59,6 +69,7 @@ inline void context_store_release(op_context_store_t *store, op_context_t *ctx)
59
69
  ctx->next = store->available;
60
70
  if (ctx->next) ctx->next->prev = ctx;
61
71
  store->available = ctx;
72
+ return 1;
62
73
  }
63
74
 
64
75
  void context_store_free(op_context_store_t *store) {
@@ -14,14 +14,15 @@ enum op_type {
14
14
  OP_TIMEOUT,
15
15
  OP_POLL,
16
16
  OP_ACCEPT,
17
- OP_CONNECT
17
+ OP_CONNECT,
18
+ OP_CHAIN
18
19
  };
19
20
 
20
21
  typedef struct op_context {
21
22
  struct op_context *prev;
22
23
  struct op_context *next;
23
24
  enum op_type type: 16;
24
- int completed : 16;
25
+ unsigned int ref_count : 16;
25
26
  int id;
26
27
  int result;
27
28
  VALUE fiber;
@@ -38,17 +39,16 @@ const char *op_type_to_str(enum op_type type);
38
39
 
39
40
  void context_store_initialize(op_context_store_t *store);
40
41
  op_context_t *context_store_acquire(op_context_store_t *store, enum op_type type);
41
- void context_store_release(op_context_store_t *store, op_context_t *ctx);
42
+ int context_store_release(op_context_store_t *store, op_context_t *ctx);
42
43
  void context_store_free(op_context_store_t *store);
43
44
 
44
- #define OP_CONTEXT_ACQUIRE(store, op_type) context_store_acquire(store, op_type)
45
- #define OP_CONTEXT_RELEASE(store, ctx) { \
46
- if (ctx->completed) {\
47
- context_store_release(store, ctx); \
48
- } \
49
- else { \
50
- ctx->completed = 1; \
51
- } \
45
+ inline unsigned int OP_CONTEXT_RELEASE(op_context_store_t *store, op_context_t *ctx) {
46
+ int completed = !ctx->ref_count;
47
+ if (ctx->ref_count)
48
+ ctx->ref_count -= 1;
49
+ else
50
+ context_store_release(store, ctx);
51
+ return completed;
52
52
  }
53
53
 
54
54
  #endif /* BACKEND_IO_URING_CONTEXT_H */
@@ -42,7 +42,6 @@ thread.
42
42
  #define _GNU_SOURCE 1
43
43
  #endif
44
44
 
45
- #include <fcntl.h>
46
45
  #include <netdb.h>
47
46
  #include <sys/socket.h>
48
47
  #include <sys/uio.h>
@@ -52,58 +51,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"));