polyphony 0.53.1 → 0.57.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,17 +1,18 @@
1
- #include <time.h>
1
+ #ifndef BACKEND_COMMON_H
2
+ #define BACKEND_COMMON_H
2
3
 
3
4
  #include "ruby.h"
4
5
  #include "ruby/io.h"
5
6
 
7
+ struct Backend_base {
8
+ unsigned int currently_polling;
9
+ unsigned int pending_count;
10
+ double idle_gc_period;
11
+ double idle_gc_last_time;
12
+ };
6
13
 
7
14
  #ifdef POLYPHONY_USE_PIDFD_OPEN
8
- #ifndef __NR_pidfd_open
9
- #define __NR_pidfd_open 434 /* System call # on most architectures */
10
- #endif
11
-
12
- static int pidfd_open(pid_t pid, unsigned int flags) {
13
- return syscall(__NR_pidfd_open, pid, flags);
14
- }
15
+ int pidfd_open(pid_t pid, unsigned int flags);
15
16
  #endif
16
17
 
17
18
  //////////////////////////////////////////////////////////////////////
@@ -26,75 +27,19 @@ struct io_internal_read_struct {
26
27
 
27
28
  #define StringValue(v) rb_string_value(&(v))
28
29
 
29
- int io_setstrbuf(VALUE *str, long len) {
30
- #ifdef _WIN32
31
- len = (len + 1) & ~1L; /* round up for wide char */
32
- #endif
33
- if (*str == Qnil) {
34
- *str = rb_str_new(0, len);
35
- return 1;
36
- }
37
- else {
38
- VALUE s = StringValue(*str);
39
- long clen = RSTRING_LEN(s);
40
- if (clen >= len) {
41
- rb_str_modify(s);
42
- return 0;
43
- }
44
- len -= clen;
45
- }
46
- rb_str_modify_expand(*str, len);
47
- return 0;
48
- }
49
-
50
- #define MAX_REALLOC_GAP 4096
51
-
52
- inline void io_shrink_read_string(VALUE str, long n) {
53
- if (rb_str_capacity(str) - n > MAX_REALLOC_GAP) {
54
- rb_str_resize(str, n);
55
- }
56
- }
57
-
58
- void io_set_read_length(VALUE str, long n, int shrinkable) {
59
- if (RSTRING_LEN(str) != n) {
60
- rb_str_modify(str);
61
- rb_str_set_len(str, n);
62
- if (shrinkable) io_shrink_read_string(str, n);
63
- }
64
- }
65
-
66
- inline rb_encoding* io_read_encoding(rb_io_t *fptr) {
67
- if (fptr->encs.enc) {
68
- return fptr->encs.enc;
69
- }
70
- return rb_default_external_encoding();
71
- }
72
-
73
- VALUE io_enc_str(VALUE str, rb_io_t *fptr) {
74
- OBJ_TAINT(str);
75
- rb_enc_associate(str, io_read_encoding(fptr));
76
- return str;
77
- }
30
+ int io_setstrbuf(VALUE *str, long len);
31
+ void io_shrink_read_string(VALUE str, long n);
32
+ void io_set_read_length(VALUE str, long n, int shrinkable);
33
+ rb_encoding* io_read_encoding(rb_io_t *fptr);
34
+ VALUE io_enc_str(VALUE str, rb_io_t *fptr);
78
35
 
79
36
  //////////////////////////////////////////////////////////////////////
80
37
  //////////////////////////////////////////////////////////////////////
81
38
 
82
- inline VALUE backend_await(Backend_t *backend) {
83
- VALUE ret;
84
- backend->pending_count++;
85
- ret = Thread_switch_fiber(rb_thread_current());
86
- backend->pending_count--;
87
- RB_GC_GUARD(ret);
88
- return ret;
89
- }
90
-
91
- inline VALUE backend_snooze() {
92
- Fiber_make_runnable(rb_fiber_current(), Qnil);
93
- return Thread_switch_fiber(rb_thread_current());
94
- }
39
+ VALUE backend_await(struct Backend_base *backend);
40
+ VALUE backend_snooze();
95
41
 
96
42
  // macros for doing read loops
97
-
98
43
  #define READ_LOOP_PREPARE_STR() { \
99
44
  str = Qnil; \
100
45
  shrinkable = io_setstrbuf(&str, len); \
@@ -117,63 +62,13 @@ inline VALUE backend_snooze() {
117
62
  READ_LOOP_PREPARE_STR(); \
118
63
  }
119
64
 
120
- inline void rectify_io_file_pos(rb_io_t *fptr) {
121
- // Apparently after reopening a closed file, the file position is not reset,
122
- // which causes the read to fail. Fortunately we can use fptr->rbuf.len to
123
- // find out if that's the case.
124
- // See: https://github.com/digital-fabric/polyphony/issues/30
125
- if (fptr->rbuf.len > 0) {
126
- lseek(fptr->fd, -fptr->rbuf.len, SEEK_CUR);
127
- fptr->rbuf.len = 0;
128
- }
129
- }
130
-
131
- inline double current_time() {
132
- struct timespec ts;
133
- clock_gettime(CLOCK_MONOTONIC, &ts);
134
- long long ns = ts.tv_sec;
135
- ns = ns * 1e9 + ts.tv_nsec;
136
- double t = ns;
137
- return t / 1e9;
138
- }
139
-
140
- inline VALUE backend_timeout_exception(VALUE exception) {
141
- if (rb_obj_is_kind_of(exception, rb_cArray) == Qtrue)
142
- return rb_funcall(rb_ary_entry(exception, 0), ID_new, 1, rb_ary_entry(exception, 1));
143
- else if (rb_obj_is_kind_of(exception, rb_cClass) == Qtrue)
144
- return rb_funcall(exception, ID_new, 0);
145
- else
146
- return rb_funcall(rb_eRuntimeError, ID_new, 1, exception);
147
- }
65
+ void rectify_io_file_pos(rb_io_t *fptr);
66
+ double current_time();
67
+ VALUE backend_timeout_exception(VALUE exception);
68
+ VALUE Backend_timeout_ensure_safe(VALUE arg);
69
+ VALUE Backend_timeout_ensure_safe(VALUE arg);
70
+ VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags);
71
+ void backend_run_idle_tasks(struct Backend_base *base);
72
+ void io_verify_blocking_mode(rb_io_t *fptr, VALUE io, VALUE blocking);
148
73
 
149
- VALUE Backend_timeout_safe(VALUE arg) {
150
- return rb_yield(arg);
151
- }
152
-
153
- VALUE Backend_timeout_rescue(VALUE arg, VALUE exception) {
154
- return exception;
155
- }
156
-
157
- VALUE Backend_timeout_ensure_safe(VALUE arg) {
158
- return rb_rescue2(Backend_timeout_safe, Qnil, Backend_timeout_rescue, Qnil, rb_eException, (VALUE)0);
159
- }
160
-
161
- static VALUE empty_string = Qnil;
162
-
163
- VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags) {
164
- switch (RARRAY_LEN(ary)) {
165
- case 0:
166
- return Qnil;
167
- case 1:
168
- return Backend_send(self, io, RARRAY_AREF(ary, 0), flags);
169
- default:
170
- if (empty_string == Qnil) {
171
- empty_string = rb_str_new_literal("");
172
- rb_global_variable(&empty_string);
173
- }
174
- VALUE joined = rb_ary_join(ary, empty_string);
175
- VALUE result = Backend_send(self, io, joined, flags);
176
- RB_GC_GUARD(joined);
177
- return result;
178
- }
179
- }
74
+ #endif /* BACKEND_COMMON_H */
@@ -4,7 +4,6 @@
4
4
  #include <sys/socket.h>
5
5
  #include <sys/uio.h>
6
6
  #include <unistd.h>
7
- #include <fcntl.h>
8
7
  #include <netinet/in.h>
9
8
  #include <arpa/inet.h>
10
9
  #include <stdnoreturn.h>
@@ -19,39 +18,21 @@
19
18
  #include "backend_io_uring_context.h"
20
19
  #include "ruby/thread.h"
21
20
  #include "ruby/io.h"
21
+ #include "backend_common.h"
22
22
 
23
23
  VALUE SYM_io_uring;
24
+ VALUE SYM_send;
25
+ VALUE SYM_splice;
26
+ VALUE SYM_write;
24
27
 
25
28
  #ifdef POLYPHONY_UNSET_NONBLOCK
26
- ID ID_ivar_is_nonblocking;
27
-
28
- // One of the changes introduced in Ruby 3.0 as part of the work on the
29
- // FiberScheduler interface is that all created sockets are marked as
30
- // non-blocking. This prevents the io_uring backend from working correctly,
31
- // since it will return an EAGAIN error just like a normal syscall. So here
32
- // instead of setting O_NONBLOCK (which is required for the libev backend), we
33
- // unset it.
34
- inline void io_unset_nonblock(rb_io_t *fptr, VALUE io) {
35
- VALUE is_nonblocking = rb_ivar_get(io, ID_ivar_is_nonblocking);
36
- if (is_nonblocking == Qfalse) return;
37
-
38
- rb_ivar_set(io, ID_ivar_is_nonblocking, Qfalse);
39
-
40
- int oflags = fcntl(fptr->fd, F_GETFL);
41
- if ((oflags == -1) && (oflags & O_NONBLOCK)) return;
42
- oflags &= !O_NONBLOCK;
43
- fcntl(fptr->fd, F_SETFL, oflags);
44
- }
29
+ #define io_unset_nonblock(fptr, io) io_verify_blocking_mode(fptr, io, Qtrue)
45
30
  #else
46
- // NOP
47
31
  #define io_unset_nonblock(fptr, io)
48
32
  #endif
49
33
 
50
34
  typedef struct Backend_t {
51
- // common fields
52
- unsigned int currently_polling;
53
- unsigned int pending_count;
54
- unsigned int poll_no_wait_count;
35
+ struct Backend_base base;
55
36
 
56
37
  // implementation-specific fields
57
38
  struct io_uring ring;
@@ -61,8 +42,6 @@ typedef struct Backend_t {
61
42
  int event_fd;
62
43
  } Backend_t;
63
44
 
64
- #include "backend_common.h"
65
-
66
45
  static size_t Backend_size(const void *ptr) {
67
46
  return sizeof(Backend_t);
68
47
  }
@@ -86,9 +65,11 @@ static VALUE Backend_initialize(VALUE self) {
86
65
  Backend_t *backend;
87
66
  GetBackend(self, backend);
88
67
 
89
- backend->currently_polling = 0;
90
- backend->pending_count = 0;
91
- backend->poll_no_wait_count = 0;
68
+ backend->base.currently_polling = 0;
69
+ backend->base.pending_count = 0;
70
+ backend->base.idle_gc_period = 0;
71
+ backend->base.idle_gc_last_time = 0;
72
+
92
73
  backend->pending_sqes = 0;
93
74
  backend->prepared_limit = 2048;
94
75
 
@@ -116,9 +97,8 @@ VALUE Backend_post_fork(VALUE self) {
116
97
  io_uring_queue_exit(&backend->ring);
117
98
  io_uring_queue_init(backend->prepared_limit, &backend->ring, 0);
118
99
  context_store_free(&backend->store);
119
- backend->currently_polling = 0;
120
- backend->pending_count = 0;
121
- backend->poll_no_wait_count = 0;
100
+ backend->base.currently_polling = 0;
101
+ backend->base.pending_count = 0;
122
102
  backend->pending_sqes = 0;
123
103
 
124
104
  return self;
@@ -128,7 +108,7 @@ unsigned int Backend_pending_count(VALUE self) {
128
108
  Backend_t *backend;
129
109
  GetBackend(self, backend);
130
110
 
131
- return backend->pending_count;
111
+ return backend->base.pending_count;
132
112
  }
133
113
 
134
114
  typedef struct poll_context {
@@ -155,17 +135,9 @@ static inline void io_uring_backend_handle_completion(struct io_uring_cqe *cqe,
155
135
  if (!ctx) return;
156
136
 
157
137
  ctx->result = cqe->res;
158
-
159
- if (ctx->completed)
160
- // already marked as deleted as result of fiber resuming before op
161
- // completion, so we can release the context
162
- context_store_release(&backend->store, ctx);
163
- else {
164
- // otherwise, we mark it as completed, schedule the fiber and let it deal
165
- // with releasing the context
166
- ctx->completed = 1;
167
- if (ctx->result != -ECANCELED) Fiber_make_runnable(ctx->fiber, ctx->resume_value);
168
- }
138
+ if (ctx->ref_count == 2 && ctx->result != -ECANCELED && ctx->fiber)
139
+ Fiber_make_runnable(ctx->fiber, ctx->resume_value);
140
+ context_store_release(&backend->store, ctx);
169
141
  }
170
142
 
171
143
  // adapted from io_uring_peek_batch_cqe in queue.c
@@ -205,9 +177,9 @@ void io_uring_backend_poll(Backend_t *backend) {
205
177
  io_uring_submit(&backend->ring);
206
178
  }
207
179
 
208
- backend->currently_polling = 1;
180
+ backend->base.currently_polling = 1;
209
181
  rb_thread_call_without_gvl(io_uring_backend_poll_without_gvl, (void *)&poll_ctx, RUBY_UBF_IO, 0);
210
- backend->currently_polling = 0;
182
+ backend->base.currently_polling = 0;
211
183
  if (poll_ctx.result < 0) return;
212
184
 
213
185
  io_uring_backend_handle_completion(poll_ctx.cqe, backend);
@@ -219,16 +191,6 @@ VALUE Backend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE runqueue
219
191
  Backend_t *backend;
220
192
  GetBackend(self, backend);
221
193
 
222
- if (is_nowait) {
223
- backend->poll_no_wait_count++;
224
- if (backend->poll_no_wait_count < 10) return self;
225
-
226
- long runnable_count = Runqueue_len(runqueue);
227
- if (backend->poll_no_wait_count < runnable_count) return self;
228
- }
229
-
230
- backend->poll_no_wait_count = 0;
231
-
232
194
  if (is_nowait && backend->pending_sqes) {
233
195
  backend->pending_sqes = 0;
234
196
  io_uring_submit(&backend->ring);
@@ -246,7 +208,7 @@ VALUE Backend_wakeup(VALUE self) {
246
208
  Backend_t *backend;
247
209
  GetBackend(self, backend);
248
210
 
249
- if (backend->currently_polling) {
211
+ if (backend->base.currently_polling) {
250
212
  // Since we're currently blocking while waiting for a completion, we add a
251
213
  // NOP which would cause the io_uring_enter syscall to return
252
214
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
@@ -277,16 +239,17 @@ int io_uring_backend_defer_submit_and_await(
277
239
  {
278
240
  VALUE switchpoint_result = Qnil;
279
241
 
280
- io_uring_sqe_set_data(sqe, ctx);
281
- io_uring_sqe_set_flags(sqe, IOSQE_ASYNC);
242
+ if (sqe) {
243
+ io_uring_sqe_set_data(sqe, ctx);
244
+ io_uring_sqe_set_flags(sqe, IOSQE_ASYNC);
245
+ }
282
246
  io_uring_backend_defer_submit(backend);
283
247
 
284
- switchpoint_result = backend_await(backend);
248
+ switchpoint_result = backend_await((struct Backend_base *)backend);
285
249
 
286
- if (!ctx->completed) {
250
+ if (ctx->ref_count > 1) {
251
+ // op was not completed (an exception was raised), so we need to cancel it
287
252
  ctx->result = -ECANCELED;
288
-
289
- // op was not completed, so we need to cancel it
290
253
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
291
254
  io_uring_prep_cancel(sqe, ctx, 0);
292
255
  backend->pending_sqes = 0;
@@ -300,7 +263,7 @@ int io_uring_backend_defer_submit_and_await(
300
263
  }
301
264
 
302
265
  VALUE io_uring_backend_wait_fd(Backend_t *backend, int fd, int write) {
303
- op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_POLL);
266
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_POLL);
304
267
  VALUE resumed_value = Qnil;
305
268
 
306
269
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
@@ -332,14 +295,14 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
332
295
 
333
296
  while (1) {
334
297
  VALUE resume_value = Qnil;
335
- op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_READ);
298
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_READ);
336
299
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
337
300
  io_uring_prep_read(sqe, fptr->fd, buf, buffer_size - total, -1);
338
301
 
339
302
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
340
- OP_CONTEXT_RELEASE(&backend->store, ctx);
303
+ int completed = context_store_release(&backend->store, ctx);
341
304
  RAISE_IF_EXCEPTION(resume_value);
342
- if (!ctx->completed) return resume_value;
305
+ if (!completed) return resume_value;
343
306
  RB_GC_GUARD(resume_value);
344
307
 
345
308
  if (result < 0)
@@ -393,14 +356,14 @@ VALUE Backend_read_loop(VALUE self, VALUE io) {
393
356
 
394
357
  while (1) {
395
358
  VALUE resume_value = Qnil;
396
- op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_READ);
359
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_READ);
397
360
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
398
361
  io_uring_prep_read(sqe, fptr->fd, buf, len, -1);
399
362
 
400
363
  ssize_t result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
401
- OP_CONTEXT_RELEASE(&backend->store, ctx);
364
+ int completed = context_store_release(&backend->store, ctx);
402
365
  RAISE_IF_EXCEPTION(resume_value);
403
- if (!ctx->completed) return resume_value;
366
+ if (!completed) return resume_value;
404
367
  RB_GC_GUARD(resume_value);
405
368
 
406
369
  if (result < 0)
@@ -440,14 +403,14 @@ VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
440
403
 
441
404
  while (1) {
442
405
  VALUE resume_value = Qnil;
443
- op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_READ);
406
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_READ);
444
407
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
445
408
  io_uring_prep_read(sqe, fptr->fd, buf, len, -1);
446
409
 
447
410
  ssize_t result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
448
- OP_CONTEXT_RELEASE(&backend->store, ctx);
411
+ int completed = context_store_release(&backend->store, ctx);
449
412
  RAISE_IF_EXCEPTION(resume_value);
450
- if (!ctx->completed) return resume_value;
413
+ if (!completed) return resume_value;
451
414
  RB_GC_GUARD(resume_value);
452
415
 
453
416
  if (result < 0)
@@ -483,14 +446,14 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
483
446
 
484
447
  while (left > 0) {
485
448
  VALUE resume_value = Qnil;
486
- op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_WRITE);
449
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_WRITE);
487
450
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
488
- io_uring_prep_write(sqe, fptr->fd, buf, left, -1);
451
+ io_uring_prep_write(sqe, fptr->fd, buf, left, 0);
489
452
 
490
453
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
491
- OP_CONTEXT_RELEASE(&backend->store, ctx);
454
+ int completed = context_store_release(&backend->store, ctx);
492
455
  RAISE_IF_EXCEPTION(resume_value);
493
- if (!ctx->completed) return resume_value;
456
+ if (!completed) return resume_value;
494
457
  RB_GC_GUARD(resume_value);
495
458
 
496
459
  if (result < 0)
@@ -532,17 +495,17 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
532
495
 
533
496
  while (1) {
534
497
  VALUE resume_value = Qnil;
535
- op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_WRITEV);
498
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_WRITEV);
536
499
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
537
500
  io_uring_prep_writev(sqe, fptr->fd, iov_ptr, iov_count, -1);
538
501
 
539
502
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
540
- OP_CONTEXT_RELEASE(&backend->store, ctx);
503
+ int completed = context_store_release(&backend->store, ctx);
541
504
  if (TEST_EXCEPTION(resume_value)) {
542
505
  free(iov);
543
506
  RAISE_EXCEPTION(resume_value);
544
507
  }
545
- if (!ctx->completed) {
508
+ if (!completed) {
546
509
  free(iov);
547
510
  return resume_value;
548
511
  }
@@ -605,14 +568,14 @@ VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length) {
605
568
 
606
569
  while (1) {
607
570
  VALUE resume_value = Qnil;
608
- op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_RECV);
571
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_RECV);
609
572
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
610
573
  io_uring_prep_recv(sqe, fptr->fd, buf, len - total, 0);
611
574
 
612
575
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
613
- OP_CONTEXT_RELEASE(&backend->store, ctx);
576
+ int completed = context_store_release(&backend->store, ctx);
614
577
  RAISE_IF_EXCEPTION(resume_value);
615
- if (!ctx->completed) return resume_value;
578
+ if (!completed) return resume_value;
616
579
  RB_GC_GUARD(resume_value);
617
580
 
618
581
  if (result < 0)
@@ -652,14 +615,14 @@ VALUE Backend_recv_loop(VALUE self, VALUE io) {
652
615
 
653
616
  while (1) {
654
617
  VALUE resume_value = Qnil;
655
- op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_RECV);
618
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_RECV);
656
619
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
657
620
  io_uring_prep_recv(sqe, fptr->fd, buf, len, 0);
658
621
 
659
622
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
660
- OP_CONTEXT_RELEASE(&backend->store, ctx);
623
+ int completed = context_store_release(&backend->store, ctx);
661
624
  RAISE_IF_EXCEPTION(resume_value);
662
- if (!ctx->completed) return resume_value;
625
+ if (!completed) return resume_value;
663
626
  RB_GC_GUARD(resume_value);
664
627
 
665
628
  if (result < 0)
@@ -698,14 +661,14 @@ VALUE Backend_recv_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method)
698
661
 
699
662
  while (1) {
700
663
  VALUE resume_value = Qnil;
701
- op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_RECV);
664
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_RECV);
702
665
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
703
666
  io_uring_prep_recv(sqe, fptr->fd, buf, len, 0);
704
667
 
705
668
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
706
- OP_CONTEXT_RELEASE(&backend->store, ctx);
669
+ int completed = context_store_release(&backend->store, ctx);
707
670
  RAISE_IF_EXCEPTION(resume_value);
708
- if (!ctx->completed) return resume_value;
671
+ if (!completed) return resume_value;
709
672
  RB_GC_GUARD(resume_value);
710
673
 
711
674
  if (result < 0)
@@ -741,14 +704,14 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
741
704
 
742
705
  while (left > 0) {
743
706
  VALUE resume_value = Qnil;
744
- op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_SEND);
707
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_SEND);
745
708
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
746
709
  io_uring_prep_send(sqe, fptr->fd, buf, left, flags_int);
747
710
 
748
711
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
749
- OP_CONTEXT_RELEASE(&backend->store, ctx);
712
+ int completed = context_store_release(&backend->store, ctx);
750
713
  RAISE_IF_EXCEPTION(resume_value);
751
- if (!ctx->completed) return resume_value;
714
+ if (!completed) return resume_value;
752
715
  RB_GC_GUARD(resume_value);
753
716
 
754
717
  if (result < 0)
@@ -775,14 +738,14 @@ VALUE io_uring_backend_accept(Backend_t *backend, VALUE server_socket, VALUE soc
775
738
 
776
739
  while (1) {
777
740
  VALUE resume_value = Qnil;
778
- op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_ACCEPT);
741
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_ACCEPT);
779
742
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
780
743
  io_uring_prep_accept(sqe, fptr->fd, &addr, &len, 0);
781
744
 
782
745
  int fd = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
783
- OP_CONTEXT_RELEASE(&backend->store, ctx);
746
+ int completed = context_store_release(&backend->store, ctx);
784
747
  RAISE_IF_EXCEPTION(resume_value);
785
- if (!ctx->completed) return resume_value;
748
+ if (!completed) return resume_value;
786
749
  RB_GC_GUARD(resume_value);
787
750
 
788
751
  if (fd < 0)
@@ -846,14 +809,14 @@ VALUE io_uring_backend_splice(Backend_t *backend, VALUE src, VALUE dest, VALUE m
846
809
  VALUE resume_value = Qnil;
847
810
 
848
811
  while (1) {
849
- op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_SPLICE);
812
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_SPLICE);
850
813
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
851
814
  io_uring_prep_splice(sqe, src_fptr->fd, -1, dest_fptr->fd, -1, NUM2INT(maxlen), 0);
852
815
 
853
816
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
854
- OP_CONTEXT_RELEASE(&backend->store, ctx);
817
+ int completed = context_store_release(&backend->store, ctx);
855
818
  RAISE_IF_EXCEPTION(resume_value);
856
- if (!ctx->completed) return resume_value;
819
+ if (!completed) return resume_value;
857
820
 
858
821
  if (result < 0)
859
822
  rb_syserr_fail(-result, strerror(-result));
@@ -897,13 +860,13 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
897
860
  addr.sin_port = htons(NUM2INT(port));
898
861
 
899
862
  VALUE resume_value = Qnil;
900
- op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_CONNECT);
863
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_CONNECT);
901
864
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
902
865
  io_uring_prep_connect(sqe, fptr->fd, (struct sockaddr *)&addr, sizeof(addr));
903
866
  int result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
904
- OP_CONTEXT_RELEASE(&backend->store, ctx);
867
+ int completed = context_store_release(&backend->store, ctx);
905
868
  RAISE_IF_EXCEPTION(resume_value);
906
- if (!ctx->completed) return resume_value;
869
+ if (!completed) return resume_value;
907
870
  RB_GC_GUARD(resume_value);
908
871
 
909
872
  if (result < 0) rb_syserr_fail(-result, strerror(-result));
@@ -943,12 +906,11 @@ int io_uring_backend_submit_timeout_and_await(Backend_t *backend, double duratio
943
906
  struct __kernel_timespec ts = double_to_timespec(duration);
944
907
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
945
908
 
946
- op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_TIMEOUT);
909
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_TIMEOUT);
947
910
  io_uring_prep_timeout(sqe, &ts, 0, 0);
948
911
 
949
912
  io_uring_backend_defer_submit_and_await(backend, sqe, ctx, resume_value);
950
- OP_CONTEXT_RELEASE(&backend->store, ctx);
951
- return ctx->completed;
913
+ return context_store_release(&backend->store, ctx);
952
914
  }
953
915
 
954
916
  VALUE Backend_sleep(VALUE self, VALUE duration) {
@@ -996,7 +958,7 @@ struct Backend_timeout_ctx {
996
958
 
997
959
  VALUE Backend_timeout_ensure(VALUE arg) {
998
960
  struct Backend_timeout_ctx *timeout_ctx = (struct Backend_timeout_ctx *)arg;
999
- if (!timeout_ctx->ctx->completed) {
961
+ if (timeout_ctx->ctx->ref_count) {
1000
962
  timeout_ctx->ctx->result = -ECANCELED;
1001
963
 
1002
964
  // op was not completed, so we need to cancel it
@@ -1005,7 +967,7 @@ VALUE Backend_timeout_ensure(VALUE arg) {
1005
967
  timeout_ctx->backend->pending_sqes = 0;
1006
968
  io_uring_submit(&timeout_ctx->backend->ring);
1007
969
  }
1008
- OP_CONTEXT_RELEASE(&timeout_ctx->backend->store, timeout_ctx->ctx);
970
+ context_store_release(&timeout_ctx->backend->store, timeout_ctx->ctx);
1009
971
  return Qnil;
1010
972
  }
1011
973
 
@@ -1023,7 +985,7 @@ VALUE Backend_timeout(int argc, VALUE *argv, VALUE self) {
1023
985
 
1024
986
  struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
1025
987
 
1026
- op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_TIMEOUT);
988
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_TIMEOUT);
1027
989
  ctx->resume_value = timeout;
1028
990
  io_uring_prep_timeout(sqe, &ts, 0, 0);
1029
991
  io_uring_sqe_set_data(sqe, ctx);
@@ -1091,6 +1053,299 @@ VALUE Backend_kind(VALUE self) {
1091
1053
  return SYM_io_uring;
1092
1054
  }
1093
1055
 
1056
+ struct io_uring_sqe *Backend_chain_prepare_write(Backend_t *backend, VALUE io, VALUE str) {
1057
+ rb_io_t *fptr;
1058
+ VALUE underlying_io;
1059
+
1060
+ underlying_io = rb_ivar_get(io, ID_ivar_io);
1061
+ if (underlying_io != Qnil) io = underlying_io;
1062
+ io = rb_io_get_write_io(io);
1063
+ GetOpenFile(io, fptr);
1064
+ io_unset_nonblock(fptr, io);
1065
+
1066
+ char *buf = StringValuePtr(str);
1067
+ long len = RSTRING_LEN(str);
1068
+
1069
+ struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
1070
+ io_uring_prep_write(sqe, fptr->fd, buf, len, 0);
1071
+ return sqe;
1072
+ }
1073
+
1074
+ struct io_uring_sqe *Backend_chain_prepare_send(Backend_t *backend, VALUE io, VALUE str, VALUE flags) {
1075
+ rb_io_t *fptr;
1076
+ VALUE underlying_io;
1077
+
1078
+ underlying_io = rb_ivar_get(io, ID_ivar_io);
1079
+ if (underlying_io != Qnil) io = underlying_io;
1080
+ io = rb_io_get_write_io(io);
1081
+ GetOpenFile(io, fptr);
1082
+ io_unset_nonblock(fptr, io);
1083
+
1084
+ char *buf = StringValuePtr(str);
1085
+ long len = RSTRING_LEN(str);
1086
+ int flags_int = NUM2INT(flags);
1087
+
1088
+ struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
1089
+ io_uring_prep_send(sqe, fptr->fd, buf, len, flags_int);
1090
+ return sqe;
1091
+ }
1092
+
1093
+ struct io_uring_sqe *Backend_chain_prepare_splice(Backend_t *backend, VALUE src, VALUE dest, VALUE maxlen) {
1094
+ rb_io_t *src_fptr;
1095
+ rb_io_t *dest_fptr;
1096
+ VALUE underlying_io;
1097
+
1098
+ underlying_io = rb_ivar_get(src, ID_ivar_io);
1099
+ if (underlying_io != Qnil) src = underlying_io;
1100
+ GetOpenFile(src, src_fptr);
1101
+ io_unset_nonblock(src_fptr, src);
1102
+
1103
+ underlying_io = rb_ivar_get(dest, ID_ivar_io);
1104
+ if (underlying_io != Qnil) dest = underlying_io;
1105
+ dest = rb_io_get_write_io(dest);
1106
+ GetOpenFile(dest, dest_fptr);
1107
+ io_unset_nonblock(dest_fptr, dest);
1108
+
1109
+ struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
1110
+ io_uring_prep_splice(sqe, src_fptr->fd, -1, dest_fptr->fd, -1, NUM2INT(maxlen), 0);
1111
+ return sqe;
1112
+ }
1113
+
1114
+ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1115
+ VALUE resume_value = Qnil;
1116
+ unsigned int sqe_count = 0;
1117
+ struct io_uring_sqe *last_sqe = 0;
1118
+ Backend_t *backend;
1119
+ GetBackend(self, backend);
1120
+ if (argc == 0) return resume_value;
1121
+
1122
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_CHAIN);
1123
+ for (int i = 0; i < argc; i++) {
1124
+ VALUE op = argv[i];
1125
+ VALUE op_type = RARRAY_AREF(op, 0);
1126
+ VALUE op_len = RARRAY_LEN(op);
1127
+
1128
+ if (op_type == SYM_write && op_len == 3) {
1129
+ last_sqe = Backend_chain_prepare_write(backend, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2));
1130
+ }
1131
+ else if (op_type == SYM_send && op_len == 4)
1132
+ last_sqe = Backend_chain_prepare_send(backend, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2), RARRAY_AREF(op, 3));
1133
+ else if (op_type == SYM_splice && op_len == 4)
1134
+ last_sqe = Backend_chain_prepare_splice(backend, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2), RARRAY_AREF(op, 3));
1135
+ else {
1136
+ if (sqe_count) {
1137
+ io_uring_sqe_set_data(last_sqe, ctx);
1138
+ io_uring_sqe_set_flags(last_sqe, IOSQE_ASYNC);
1139
+
1140
+ ctx->ref_count = sqe_count;
1141
+ ctx->result = -ECANCELED;
1142
+ struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
1143
+ io_uring_prep_cancel(sqe, ctx, 0);
1144
+ backend->pending_sqes = 0;
1145
+ io_uring_submit(&backend->ring);
1146
+ }
1147
+ else {
1148
+ ctx->ref_count = 1;
1149
+ context_store_release(&backend->store, ctx);
1150
+ }
1151
+ rb_raise(rb_eRuntimeError, "Invalid op specified or bad op arity");
1152
+ }
1153
+
1154
+ io_uring_sqe_set_data(last_sqe, ctx);
1155
+ unsigned int flags = (i == argc - 1) ? IOSQE_ASYNC : IOSQE_ASYNC & IOSQE_IO_LINK;
1156
+ io_uring_sqe_set_flags(last_sqe, flags);
1157
+ sqe_count++;
1158
+ }
1159
+
1160
+ ctx->ref_count = sqe_count + 1;
1161
+ io_uring_backend_defer_submit(backend);
1162
+ resume_value = backend_await((struct Backend_base *)backend);
1163
+ int result = ctx->result;
1164
+ int completed = context_store_release(&backend->store, ctx);
1165
+ if (!completed) {
1166
+ // op was not completed (an exception was raised), so we need to cancel it
1167
+ ctx->result = -ECANCELED;
1168
+ struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
1169
+ io_uring_prep_cancel(sqe, ctx, 0);
1170
+ backend->pending_sqes = 0;
1171
+ io_uring_submit(&backend->ring);
1172
+ RAISE_IF_EXCEPTION(resume_value);
1173
+ return resume_value;
1174
+ }
1175
+
1176
+ RB_GC_GUARD(resume_value);
1177
+ return INT2NUM(result);
1178
+ }
1179
+
1180
+ VALUE Backend_idle_gc_period_set(VALUE self, VALUE period) {
1181
+ Backend_t *backend;
1182
+ GetBackend(self, backend);
1183
+ backend->base.idle_gc_period = NUM2DBL(period);
1184
+ backend->base.idle_gc_last_time = current_time();
1185
+ return self;
1186
+ }
1187
+
1188
+ inline VALUE Backend_run_idle_tasks(VALUE self) {
1189
+ Backend_t *backend;
1190
+ GetBackend(self, backend);
1191
+ backend_run_idle_tasks(&backend->base);
1192
+ return self;
1193
+ }
1194
+
1195
+ static inline void splice_chunks_prep_write(op_context_t *ctx, struct io_uring_sqe *sqe, int fd, VALUE str) {
1196
+ char *buf = RSTRING_PTR(str);
1197
+ int len = RSTRING_LEN(str);
1198
+ io_uring_prep_write(sqe, fd, buf, len, 0);
1199
+ // io_uring_prep_send(sqe, fd, buf, len, 0);
1200
+ io_uring_sqe_set_data(sqe, ctx);
1201
+ }
1202
+
1203
+ static inline void splice_chunks_prep_splice(op_context_t *ctx, struct io_uring_sqe *sqe, int src, int dest, int maxlen) {
1204
+ io_uring_prep_splice(sqe, src, -1, dest, -1, maxlen, 0);
1205
+ io_uring_sqe_set_data(sqe, ctx);
1206
+ }
1207
+
1208
+ static inline void splice_chunks_get_sqe(
1209
+ Backend_t *backend,
1210
+ op_context_t **ctx,
1211
+ struct io_uring_sqe **sqe,
1212
+ enum op_type type
1213
+ )
1214
+ {
1215
+ if (*ctx) {
1216
+ if (*sqe) (*sqe)->flags |= IOSQE_IO_LINK;
1217
+ (*ctx)->ref_count++;
1218
+ }
1219
+ else
1220
+ *ctx = context_store_acquire(&backend->store, type);
1221
+ (*sqe) = io_uring_get_sqe(&backend->ring);
1222
+ }
1223
+
1224
+ static inline void splice_chunks_cancel(Backend_t *backend, op_context_t *ctx) {
1225
+ ctx->result = -ECANCELED;
1226
+ struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
1227
+ io_uring_prep_cancel(sqe, ctx, 0);
1228
+ backend->pending_sqes = 0;
1229
+ io_uring_submit(&backend->ring);
1230
+ }
1231
+
1232
+ static inline int splice_chunks_await_ops(
1233
+ Backend_t *backend,
1234
+ op_context_t **ctx,
1235
+ int *result,
1236
+ VALUE *switchpoint_result
1237
+ )
1238
+ {
1239
+ int res = io_uring_backend_defer_submit_and_await(backend, 0, *ctx, switchpoint_result);
1240
+ if (result) (*result) = res;
1241
+ int completed = context_store_release(&backend->store, *ctx);
1242
+ if (!completed) {
1243
+ splice_chunks_cancel(backend, *ctx);
1244
+ if (TEST_EXCEPTION(*switchpoint_result)) return 1;
1245
+ }
1246
+ *ctx = 0;
1247
+ return 0;
1248
+ }
1249
+
1250
+ #define SPLICE_CHUNKS_AWAIT_OPS(backend, ctx, result, switchpoint_result) \
1251
+ if (splice_chunks_await_ops(backend, ctx, result, switchpoint_result)) goto error;
1252
+
1253
+ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VALUE postfix, VALUE chunk_prefix, VALUE chunk_postfix, VALUE chunk_size) {
1254
+ Backend_t *backend;
1255
+ GetBackend(self, backend);
1256
+ int total = 0;
1257
+ int err = 0;
1258
+ VALUE switchpoint_result = Qnil;
1259
+ op_context_t *ctx = 0;
1260
+ struct io_uring_sqe *sqe = 0;
1261
+
1262
+ rb_io_t *src_fptr;
1263
+ rb_io_t *dest_fptr;
1264
+
1265
+ VALUE underlying_io = rb_ivar_get(src, ID_ivar_io);
1266
+ if (underlying_io != Qnil) src = underlying_io;
1267
+ GetOpenFile(src, src_fptr);
1268
+ io_verify_blocking_mode(src_fptr, src, Qtrue);
1269
+
1270
+ underlying_io = rb_ivar_get(dest, ID_ivar_io);
1271
+ if (underlying_io != Qnil) dest = underlying_io;
1272
+ dest = rb_io_get_write_io(dest);
1273
+ GetOpenFile(dest, dest_fptr);
1274
+ io_verify_blocking_mode(dest_fptr, dest, Qtrue);
1275
+
1276
+ int maxlen = NUM2INT(chunk_size);
1277
+ VALUE str = Qnil;
1278
+ VALUE chunk_len_value = Qnil;
1279
+
1280
+ int pipefd[2] = { -1, -1 };
1281
+ if (pipe(pipefd) == -1) {
1282
+ err = errno;
1283
+ goto syscallerror;
1284
+ }
1285
+
1286
+ if (prefix != Qnil) {
1287
+ splice_chunks_get_sqe(backend, &ctx, &sqe, OP_WRITE);
1288
+ splice_chunks_prep_write(ctx, sqe, dest_fptr->fd, prefix);
1289
+ }
1290
+
1291
+ while (1) {
1292
+ int chunk_len;
1293
+ VALUE chunk_prefix_str = Qnil;
1294
+ VALUE chunk_postfix_str = Qnil;
1295
+
1296
+ splice_chunks_get_sqe(backend, &ctx, &sqe, OP_SPLICE);
1297
+ splice_chunks_prep_splice(ctx, sqe, src_fptr->fd, pipefd[1], maxlen);
1298
+
1299
+ SPLICE_CHUNKS_AWAIT_OPS(backend, &ctx, &chunk_len, &switchpoint_result);
1300
+ if (chunk_len == 0) break;
1301
+
1302
+ total += chunk_len;
1303
+ chunk_len_value = INT2NUM(chunk_len);
1304
+
1305
+
1306
+ if (chunk_prefix != Qnil) {
1307
+ chunk_prefix_str = (TYPE(chunk_prefix) == T_STRING) ? chunk_prefix : rb_funcall(chunk_prefix, ID_call, 1, chunk_len_value);
1308
+ splice_chunks_get_sqe(backend, &ctx, &sqe, OP_WRITE);
1309
+ splice_chunks_prep_write(ctx, sqe, dest_fptr->fd, chunk_prefix_str);
1310
+ }
1311
+
1312
+ splice_chunks_get_sqe(backend, &ctx, &sqe, OP_SPLICE);
1313
+ splice_chunks_prep_splice(ctx, sqe, pipefd[0], dest_fptr->fd, chunk_len);
1314
+
1315
+ if (chunk_postfix != Qnil) {
1316
+ chunk_postfix_str = (TYPE(chunk_postfix) == T_STRING) ? chunk_postfix : rb_funcall(chunk_postfix, ID_call, 1, chunk_len_value);
1317
+ splice_chunks_get_sqe(backend, &ctx, &sqe, OP_WRITE);
1318
+ splice_chunks_prep_write(ctx, sqe, dest_fptr->fd, chunk_postfix_str);
1319
+ }
1320
+
1321
+ RB_GC_GUARD(chunk_prefix_str);
1322
+ RB_GC_GUARD(chunk_postfix_str);
1323
+ }
1324
+
1325
+ if (postfix != Qnil) {
1326
+ splice_chunks_get_sqe(backend, &ctx, &sqe, OP_WRITE);
1327
+ splice_chunks_prep_write(ctx, sqe, dest_fptr->fd, postfix);
1328
+ }
1329
+ if (ctx) {
1330
+ SPLICE_CHUNKS_AWAIT_OPS(backend, &ctx, 0, &switchpoint_result);
1331
+ }
1332
+
1333
+ RB_GC_GUARD(str);
1334
+ RB_GC_GUARD(chunk_len_value);
1335
+ RB_GC_GUARD(switchpoint_result);
1336
+ if (pipefd[0] != -1) close(pipefd[0]);
1337
+ if (pipefd[1] != -1) close(pipefd[1]);
1338
+ return INT2NUM(total);
1339
+ syscallerror:
1340
+ if (pipefd[0] != -1) close(pipefd[0]);
1341
+ if (pipefd[1] != -1) close(pipefd[1]);
1342
+ rb_syserr_fail(err, strerror(err));
1343
+ error:
1344
+ if (pipefd[0] != -1) close(pipefd[0]);
1345
+ if (pipefd[1] != -1) close(pipefd[1]);
1346
+ return RAISE_EXCEPTION(switchpoint_result);
1347
+ }
1348
+
1094
1349
  void Init_Backend() {
1095
1350
  VALUE cBackend = rb_define_class_under(mPolyphony, "Backend", rb_cObject);
1096
1351
  rb_define_alloc_func(cBackend, Backend_allocate);
@@ -1102,6 +1357,9 @@ void Init_Backend() {
1102
1357
  rb_define_method(cBackend, "poll", Backend_poll, 3);
1103
1358
  rb_define_method(cBackend, "break", Backend_wakeup, 0);
1104
1359
  rb_define_method(cBackend, "kind", Backend_kind, 0);
1360
+ rb_define_method(cBackend, "chain", Backend_chain, -1);
1361
+ rb_define_method(cBackend, "idle_gc_period=", Backend_idle_gc_period_set, 1);
1362
+ rb_define_method(cBackend, "splice_chunks", Backend_splice_chunks, 7);
1105
1363
 
1106
1364
  rb_define_method(cBackend, "accept", Backend_accept, 2);
1107
1365
  rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
@@ -1130,6 +1388,9 @@ void Init_Backend() {
1130
1388
  #endif
1131
1389
 
1132
1390
  SYM_io_uring = ID2SYM(rb_intern("io_uring"));
1391
+ SYM_send = ID2SYM(rb_intern("send"));
1392
+ SYM_splice = ID2SYM(rb_intern("splice"));
1393
+ SYM_write = ID2SYM(rb_intern("write"));
1133
1394
  }
1134
1395
 
1135
1396
  #endif // POLYPHONY_BACKEND_LIBURING