polyphony 0.81.1 → 0.84

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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +1 -1
  3. data/.gitmodules +3 -0
  4. data/CHANGELOG.md +17 -0
  5. data/Gemfile.lock +1 -1
  6. data/Rakefile +14 -0
  7. data/bin/test +1 -1
  8. data/ext/polyphony/backend_common.c +33 -10
  9. data/ext/polyphony/backend_common.h +20 -2
  10. data/ext/polyphony/backend_io_uring.c +182 -220
  11. data/ext/polyphony/backend_libev.c +212 -197
  12. data/ext/polyphony/extconf.rb +21 -2
  13. data/ext/polyphony/io_extensions.c +440 -0
  14. data/ext/polyphony/pipe.c +109 -0
  15. data/ext/polyphony/polyphony.c +50 -1
  16. data/ext/polyphony/polyphony.h +6 -11
  17. data/ext/polyphony/polyphony_ext.c +7 -2
  18. data/ext/polyphony/zlib_conf.rb +119 -0
  19. data/lib/polyphony/extensions/io.rb +20 -2
  20. data/lib/polyphony/extensions/pipe.rb +171 -0
  21. data/lib/polyphony/extensions.rb +1 -0
  22. data/lib/polyphony/version.rb +1 -1
  23. data/lib/polyphony.rb +4 -0
  24. data/test/helper.rb +4 -0
  25. data/test/test_backend.rb +3 -48
  26. data/test/test_global_api.rb +23 -23
  27. data/test/test_io.rb +426 -0
  28. data/test/test_pipe.rb +41 -0
  29. data/test/test_process_supervision.rb +1 -1
  30. data/test/test_raw_buffer.rb +37 -0
  31. data/test/test_socket.rb +50 -0
  32. metadata +8 -13
  33. data/ext/liburing/liburing/README.md +0 -4
  34. data/ext/liburing/liburing/barrier.h +0 -73
  35. data/ext/liburing/liburing/compat.h +0 -15
  36. data/ext/liburing/liburing/io_uring.h +0 -343
  37. data/ext/liburing/liburing.h +0 -585
  38. data/ext/liburing/queue.c +0 -333
  39. data/ext/liburing/register.c +0 -187
  40. data/ext/liburing/setup.c +0 -210
  41. data/ext/liburing/syscall.c +0 -54
  42. data/ext/liburing/syscall.h +0 -18
  43. data/ext/polyphony/liburing.c +0 -8
@@ -14,7 +14,7 @@
14
14
  #include <errno.h>
15
15
 
16
16
  #include "polyphony.h"
17
- #include "../liburing/liburing.h"
17
+ #include "liburing.h"
18
18
  #include "backend_io_uring_context.h"
19
19
  #include "ruby/thread.h"
20
20
  #include "ruby/io.h"
@@ -102,24 +102,6 @@ static VALUE Backend_initialize(VALUE self) {
102
102
  return self;
103
103
  }
104
104
 
105
- static inline struct io_buffer get_io_buffer(VALUE in) {
106
- if (FIXNUM_P(in)) {
107
- struct raw_buffer *raw = (struct raw_buffer *)(FIX2LONG(in));
108
- return (struct io_buffer){ raw->base, raw->size, 1 };
109
- }
110
- return (struct io_buffer){ RSTRING_PTR(in), RSTRING_LEN(in), 0 };
111
- }
112
-
113
- static inline VALUE coerce_io_string_or_buffer(VALUE buf) {
114
- switch (TYPE(buf)) {
115
- case T_STRING:
116
- case T_FIXNUM:
117
- return buf;
118
- default:
119
- return StringValue(buf);
120
- }
121
- }
122
-
123
105
  VALUE Backend_finalize(VALUE self) {
124
106
  Backend_t *backend;
125
107
  GetBackend(self, backend);
@@ -322,7 +304,7 @@ int io_uring_backend_defer_submit_and_await(
322
304
  // op was not completed (an exception was raised), so we need to cancel it
323
305
  ctx->result = -ECANCELED;
324
306
  sqe = io_uring_get_sqe(&backend->ring);
325
- io_uring_prep_cancel(sqe, ctx, 0);
307
+ io_uring_prep_cancel(sqe, (__u64)ctx, 0);
326
308
  backend->pending_sqes = 0;
327
309
  io_uring_submit(&backend->ring);
328
310
  }
@@ -347,8 +329,26 @@ VALUE io_uring_backend_wait_fd(Backend_t *backend, int fd, int write) {
347
329
  return resumed_value;
348
330
  }
349
331
 
332
+ static inline int fd_from_io(VALUE io, rb_io_t **fptr, int write_mode, int rectify_file_pos) {
333
+ if (rb_obj_class(io) == cPipe) {
334
+ *fptr = NULL;
335
+ return Pipe_get_fd(io, write_mode);
336
+ }
337
+ else {
338
+ VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
339
+ if (underlying_io != Qnil) io = underlying_io;
340
+
341
+ GetOpenFile(io, *fptr);
342
+ io_unset_nonblock(*fptr, io);
343
+ if (rectify_file_pos) rectify_io_file_pos(*fptr);
344
+
345
+ return (*fptr)->fd;
346
+ }
347
+ }
348
+
350
349
  VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof, VALUE pos) {
351
350
  Backend_t *backend;
351
+ int fd;
352
352
  rb_io_t *fptr;
353
353
  struct io_buffer buffer = get_io_buffer(str);
354
354
  long buf_pos = NUM2INT(pos);
@@ -356,38 +356,33 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof,
356
356
  int expandable_buffer = 0;
357
357
  long total = 0;
358
358
  int read_to_eof = RTEST(to_eof);
359
- VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
360
359
 
361
360
  if (buffer.raw) {
362
- if (buf_pos < 0 || buf_pos > buffer.size) buf_pos = buffer.size;
363
- buffer.base += buf_pos;
364
- buffer.size -= buf_pos;
361
+ if (buf_pos < 0 || buf_pos > buffer.len) buf_pos = buffer.len;
362
+ buffer.ptr += buf_pos;
363
+ buffer.len -= buf_pos;
365
364
  }
366
365
  else {
367
366
  expandable_buffer = length == Qnil;
368
367
  long expected_read_length = expandable_buffer ? 4096 : FIX2INT(length);
369
368
  long string_cap = rb_str_capacity(str);
370
- if (buf_pos < 0 || buf_pos > buffer.size) buf_pos = buffer.size;
369
+ if (buf_pos < 0 || buf_pos > buffer.len) buf_pos = buffer.len;
371
370
 
372
371
  if (string_cap < expected_read_length + buf_pos) {
373
372
  shrinkable_string = io_setstrbuf(&str, expected_read_length + buf_pos);
374
- buffer.base = RSTRING_PTR(str) + buf_pos;
375
- buffer.size = expected_read_length;
373
+ buffer.ptr = RSTRING_PTR(str) + buf_pos;
374
+ buffer.len = expected_read_length;
376
375
  }
377
376
  else {
378
- buffer.base += buf_pos;
379
- buffer.size = string_cap - buf_pos;
380
- if (buffer.size > expected_read_length)
381
- buffer.size = expected_read_length;
377
+ buffer.ptr += buf_pos;
378
+ buffer.len = string_cap - buf_pos;
379
+ if (buffer.len > expected_read_length)
380
+ buffer.len = expected_read_length;
382
381
  }
383
382
  }
384
383
 
385
384
  GetBackend(self, backend);
386
- if (underlying_io != Qnil) io = underlying_io;
387
- GetOpenFile(io, fptr);
388
- rb_io_check_byte_readable(fptr);
389
- io_unset_nonblock(fptr, io);
390
- rectify_io_file_pos(fptr);
385
+ fd = fd_from_io(io, &fptr, 0, 1);
391
386
 
392
387
  while (1) {
393
388
  VALUE resume_value = Qnil;
@@ -396,7 +391,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof,
396
391
  int result;
397
392
  int completed;
398
393
 
399
- io_uring_prep_read(sqe, fptr->fd, buffer.base, buffer.size, -1);
394
+ io_uring_prep_read(sqe, fd, buffer.ptr, buffer.len, -1);
400
395
 
401
396
  result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
402
397
  completed = context_store_release(&backend->store, ctx);
@@ -415,29 +410,28 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof,
415
410
  total += result;
416
411
  if (!read_to_eof) break;
417
412
 
418
- if (result == buffer.size) {
413
+ if (result == buffer.len) {
419
414
  if (!expandable_buffer) break;
420
415
 
421
416
  // resize buffer to double its capacity
422
417
  rb_str_resize(str, total + buf_pos);
423
418
  rb_str_modify_expand(str, rb_str_capacity(str));
424
419
  shrinkable_string = 0;
425
- buffer.base = RSTRING_PTR(str) + total + buf_pos;
426
- buffer.size = rb_str_capacity(str) - total - buf_pos;
420
+ buffer.ptr = RSTRING_PTR(str) + total + buf_pos;
421
+ buffer.len = rb_str_capacity(str) - total - buf_pos;
427
422
  }
428
423
  else {
429
- buffer.base += result;
430
- buffer.size -= result;
431
- if (!buffer.size) break;
424
+ buffer.ptr += result;
425
+ buffer.len -= result;
426
+ if (!buffer.len) break;
432
427
  }
433
428
  }
434
429
  }
435
430
 
436
431
  if (!buffer.raw) {
437
432
  io_set_read_length(str, buf_pos + total, shrinkable_string);
438
- io_enc_str(str, fptr);
433
+ if (fptr) io_enc_str(str, fptr);
439
434
  }
440
-
441
435
  if (!total) return Qnil;
442
436
 
443
437
  return buffer.raw ? INT2FIX(total) : str;
@@ -445,22 +439,18 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof,
445
439
 
446
440
  VALUE Backend_read_loop(VALUE self, VALUE io, VALUE maxlen) {
447
441
  Backend_t *backend;
442
+ int fd;
448
443
  rb_io_t *fptr;
449
444
  VALUE str;
450
445
  long total;
451
446
  long len = NUM2INT(maxlen);
452
447
  int shrinkable;
453
448
  char *buf;
454
- VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
455
449
 
456
450
  READ_LOOP_PREPARE_STR();
457
451
 
458
452
  GetBackend(self, backend);
459
- if (underlying_io != Qnil) io = underlying_io;
460
- GetOpenFile(io, fptr);
461
- rb_io_check_byte_readable(fptr);
462
- io_unset_nonblock(fptr, io);
463
- rectify_io_file_pos(fptr);
453
+ fd = fd_from_io(io, &fptr, 0, 1);
464
454
 
465
455
  while (1) {
466
456
  VALUE resume_value = Qnil;
@@ -469,7 +459,7 @@ VALUE Backend_read_loop(VALUE self, VALUE io, VALUE maxlen) {
469
459
  ssize_t result;
470
460
  int completed;
471
461
 
472
- io_uring_prep_read(sqe, fptr->fd, buf, len, -1);
462
+ io_uring_prep_read(sqe, fd, buf, len, -1);
473
463
 
474
464
  result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
475
465
  completed = context_store_release(&backend->store, ctx);
@@ -497,23 +487,19 @@ VALUE Backend_read_loop(VALUE self, VALUE io, VALUE maxlen) {
497
487
 
498
488
  VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
499
489
  Backend_t *backend;
490
+ int fd;
500
491
  rb_io_t *fptr;
501
492
  VALUE str;
502
493
  long total;
503
494
  long len = 8192;
504
495
  int shrinkable;
505
496
  char *buf;
506
- VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
507
497
  ID method_id = SYM2ID(method);
508
498
 
509
499
  READ_LOOP_PREPARE_STR();
510
500
 
511
501
  GetBackend(self, backend);
512
- if (underlying_io != Qnil) io = underlying_io;
513
- GetOpenFile(io, fptr);
514
- rb_io_check_byte_readable(fptr);
515
- io_unset_nonblock(fptr, io);
516
- rectify_io_file_pos(fptr);
502
+ fd = fd_from_io(io, &fptr, 0, 1);
517
503
 
518
504
  while (1) {
519
505
  VALUE resume_value = Qnil;
@@ -522,7 +508,7 @@ VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
522
508
  ssize_t result;
523
509
  int completed;
524
510
 
525
- io_uring_prep_read(sqe, fptr->fd, buf, len, -1);
511
+ io_uring_prep_read(sqe, fd, buf, len, -1);
526
512
 
527
513
  result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
528
514
  completed = context_store_release(&backend->store, ctx);
@@ -550,18 +536,14 @@ VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
550
536
 
551
537
  VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
552
538
  Backend_t *backend;
539
+ int fd;
553
540
  rb_io_t *fptr;
554
- VALUE underlying_io;
555
541
 
556
542
  struct io_buffer buffer = get_io_buffer(str);
557
- long left = buffer.size;
543
+ long left = buffer.len;
558
544
 
559
- underlying_io = rb_ivar_get(io, ID_ivar_io);
560
- if (underlying_io != Qnil) io = underlying_io;
561
545
  GetBackend(self, backend);
562
- io = rb_io_get_write_io(io);
563
- GetOpenFile(io, fptr);
564
- io_unset_nonblock(fptr, io);
546
+ fd = fd_from_io(io, &fptr, 1, 0);
565
547
 
566
548
  while (left > 0) {
567
549
  VALUE resume_value = Qnil;
@@ -570,7 +552,7 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
570
552
  int result;
571
553
  int completed;
572
554
 
573
- io_uring_prep_write(sqe, fptr->fd, buffer.base, left, 0);
555
+ io_uring_prep_write(sqe, fd, buffer.ptr, left, 0);
574
556
 
575
557
  result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
576
558
  completed = context_store_release(&backend->store, ctx);
@@ -584,31 +566,26 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
584
566
  if (result < 0)
585
567
  rb_syserr_fail(-result, strerror(-result));
586
568
  else {
587
- buffer.base += result;
588
- buffer.size -= result;
569
+ buffer.ptr += result;
589
570
  left -= result;
590
571
  }
591
572
  }
592
573
 
593
- return INT2NUM(buffer.size);
574
+ return INT2NUM(buffer.len);
594
575
  }
595
576
 
596
577
  VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
597
578
  Backend_t *backend;
579
+ int fd;
598
580
  rb_io_t *fptr;
599
- VALUE underlying_io;
600
581
  long total_length = 0;
601
582
  long total_written = 0;
602
583
  struct iovec *iov = 0;
603
584
  struct iovec *iov_ptr = 0;
604
585
  int iov_count = argc;
605
586
 
606
- underlying_io = rb_ivar_get(io, ID_ivar_io);
607
- if (underlying_io != Qnil) io = underlying_io;
608
587
  GetBackend(self, backend);
609
- io = rb_io_get_write_io(io);
610
- GetOpenFile(io, fptr);
611
- io_unset_nonblock(fptr, io);
588
+ fd = fd_from_io(io, &fptr, 1, 0);
612
589
 
613
590
  iov = malloc(iov_count * sizeof(struct iovec));
614
591
  for (int i = 0; i < argc; i++) {
@@ -626,7 +603,7 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
626
603
  int result;
627
604
  int completed;
628
605
 
629
- io_uring_prep_writev(sqe, fptr->fd, iov_ptr, iov_count, -1);
606
+ io_uring_prep_writev(sqe, fd, iov_ptr, iov_count, -1);
630
607
 
631
608
  result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
632
609
  completed = context_store_release(&backend->store, ctx);
@@ -676,29 +653,40 @@ VALUE Backend_write_m(int argc, VALUE *argv, VALUE self) {
676
653
 
677
654
  VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length, VALUE pos) {
678
655
  Backend_t *backend;
656
+ int fd;
679
657
  rb_io_t *fptr;
680
- long dynamic_len = length == Qnil;
681
- long len = dynamic_len ? 4096 : NUM2INT(length);
658
+ struct io_buffer buffer = get_io_buffer(str);
682
659
  long buf_pos = NUM2INT(pos);
683
- int shrinkable;
684
- char *buf;
660
+ int shrinkable_string = 0;
661
+ int expandable_buffer = 0;
685
662
  long total = 0;
686
- VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);;
687
663
 
688
- if (str != Qnil) {
689
- int current_len = RSTRING_LEN(str);
690
- if (buf_pos < 0 || buf_pos > current_len) buf_pos = current_len;
664
+ if (buffer.raw) {
665
+ if (buf_pos < 0 || buf_pos > buffer.len) buf_pos = buffer.len;
666
+ buffer.ptr += buf_pos;
667
+ buffer.len -= buf_pos;
668
+ }
669
+ else {
670
+ expandable_buffer = length == Qnil;
671
+ long expected_read_length = expandable_buffer ? 4096 : FIX2INT(length);
672
+ long string_cap = rb_str_capacity(str);
673
+ if (buf_pos < 0 || buf_pos > buffer.len) buf_pos = buffer.len;
674
+
675
+ if (string_cap < expected_read_length + buf_pos) {
676
+ shrinkable_string = io_setstrbuf(&str, expected_read_length + buf_pos);
677
+ buffer.ptr = RSTRING_PTR(str) + buf_pos;
678
+ buffer.len = expected_read_length;
679
+ }
680
+ else {
681
+ buffer.ptr += buf_pos;
682
+ buffer.len = string_cap - buf_pos;
683
+ if (buffer.len > expected_read_length)
684
+ buffer.len = expected_read_length;
685
+ }
691
686
  }
692
- else buf_pos = 0;
693
- shrinkable = io_setstrbuf(&str, buf_pos + len);
694
- buf = RSTRING_PTR(str) + buf_pos;
695
687
 
696
688
  GetBackend(self, backend);
697
- if (underlying_io != Qnil) io = underlying_io;
698
- GetOpenFile(io, fptr);
699
- rb_io_check_byte_readable(fptr);
700
- io_unset_nonblock(fptr, io);
701
- rectify_io_file_pos(fptr);
689
+ fd = fd_from_io(io, &fptr, 0, 0);
702
690
 
703
691
  while (1) {
704
692
  VALUE resume_value = Qnil;
@@ -707,7 +695,7 @@ VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length, VALUE pos) {
707
695
  int result;
708
696
  int completed;
709
697
 
710
- io_uring_prep_recv(sqe, fptr->fd, buf, len - total, 0);
698
+ io_uring_prep_recv(sqe, fd, buffer.ptr, buffer.len, 0);
711
699
 
712
700
  result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
713
701
  completed = context_store_release(&backend->store, ctx);
@@ -726,32 +714,29 @@ VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length, VALUE pos) {
726
714
  }
727
715
  }
728
716
 
729
- io_set_read_length(str, buf_pos + total, shrinkable);
730
- io_enc_str(str, fptr);
731
-
717
+ if (!buffer.raw) {
718
+ io_set_read_length(str, buf_pos + total, shrinkable_string);
719
+ if (fptr) io_enc_str(str, fptr);
720
+ }
732
721
  if (!total) return Qnil;
733
722
 
734
- return str;
723
+ return buffer.raw ? INT2FIX(total) : str;
735
724
  }
736
725
 
737
726
  VALUE Backend_recv_loop(VALUE self, VALUE io, VALUE maxlen) {
738
727
  Backend_t *backend;
728
+ int fd;
739
729
  rb_io_t *fptr;
740
730
  VALUE str;
741
731
  long total;
742
732
  long len = NUM2INT(maxlen);
743
733
  int shrinkable;
744
734
  char *buf;
745
- VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
746
735
 
747
736
  READ_LOOP_PREPARE_STR();
748
737
 
749
738
  GetBackend(self, backend);
750
- if (underlying_io != Qnil) io = underlying_io;
751
- GetOpenFile(io, fptr);
752
- rb_io_check_byte_readable(fptr);
753
- io_unset_nonblock(fptr, io);
754
- rectify_io_file_pos(fptr);
739
+ fd = fd_from_io(io, &fptr, 0, 0);
755
740
 
756
741
  while (1) {
757
742
  VALUE resume_value = Qnil;
@@ -760,7 +745,7 @@ VALUE Backend_recv_loop(VALUE self, VALUE io, VALUE maxlen) {
760
745
  int result;
761
746
  int completed;
762
747
 
763
- io_uring_prep_recv(sqe, fptr->fd, buf, len, 0);
748
+ io_uring_prep_recv(sqe, fd, buf, len, 0);
764
749
 
765
750
  result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
766
751
  completed = context_store_release(&backend->store, ctx);
@@ -787,23 +772,19 @@ VALUE Backend_recv_loop(VALUE self, VALUE io, VALUE maxlen) {
787
772
 
788
773
  VALUE Backend_recv_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
789
774
  Backend_t *backend;
775
+ int fd;
790
776
  rb_io_t *fptr;
791
777
  VALUE str;
792
778
  long total;
793
779
  long len = 8192;
794
780
  int shrinkable;
795
781
  char *buf;
796
- VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
797
782
  ID method_id = SYM2ID(method);
798
783
 
799
784
  READ_LOOP_PREPARE_STR();
800
785
 
801
786
  GetBackend(self, backend);
802
- if (underlying_io != Qnil) io = underlying_io;
803
- GetOpenFile(io, fptr);
804
- rb_io_check_byte_readable(fptr);
805
- io_unset_nonblock(fptr, io);
806
- rectify_io_file_pos(fptr);
787
+ fd = fd_from_io(io, &fptr, 0, 0);
807
788
 
808
789
  while (1) {
809
790
  VALUE resume_value = Qnil;
@@ -812,7 +793,7 @@ VALUE Backend_recv_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method)
812
793
  int result;
813
794
  int completed;
814
795
 
815
- io_uring_prep_recv(sqe, fptr->fd, buf, len, 0);
796
+ io_uring_prep_recv(sqe, fd, buf, len, 0);
816
797
 
817
798
  result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
818
799
  completed = context_store_release(&backend->store, ctx);
@@ -839,24 +820,15 @@ VALUE Backend_recv_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method)
839
820
 
840
821
  VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
841
822
  Backend_t *backend;
823
+ int fd;
842
824
  rb_io_t *fptr;
843
- VALUE underlying_io;
844
- char *buf;
845
- long len;
846
- long left;
847
- int flags_int;
848
825
 
849
- underlying_io = rb_ivar_get(io, ID_ivar_io);
850
- if (underlying_io != Qnil) io = underlying_io;
851
- GetBackend(self, backend);
852
- io = rb_io_get_write_io(io);
853
- GetOpenFile(io, fptr);
854
- io_unset_nonblock(fptr, io);
826
+ struct io_buffer buffer = get_io_buffer(str);
827
+ long left = buffer.len;
828
+ int flags_int = NUM2INT(flags);
855
829
 
856
- buf = StringValuePtr(str);
857
- len = RSTRING_LEN(str);
858
- left = len;
859
- flags_int = NUM2INT(flags);
830
+ GetBackend(self, backend);
831
+ fd = fd_from_io(io, &fptr, 1, 0);
860
832
 
861
833
  while (left > 0) {
862
834
  VALUE resume_value = Qnil;
@@ -865,7 +837,7 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
865
837
  int result;
866
838
  int completed;
867
839
 
868
- io_uring_prep_send(sqe, fptr->fd, buf, left, flags_int);
840
+ io_uring_prep_send(sqe, fd, buffer.ptr, left, flags_int);
869
841
 
870
842
  result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
871
843
  completed = context_store_release(&backend->store, ctx);
@@ -879,24 +851,22 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
879
851
  if (result < 0)
880
852
  rb_syserr_fail(-result, strerror(-result));
881
853
  else {
882
- buf += result;
854
+ buffer.ptr += result;
883
855
  left -= result;
884
856
  }
885
857
  }
886
858
 
887
- return INT2NUM(len);
859
+ return INT2NUM(buffer.len);
888
860
  }
889
861
 
890
862
  VALUE io_uring_backend_accept(Backend_t *backend, VALUE server_socket, VALUE socket_class, int loop) {
891
- rb_io_t *fptr;
863
+ int server_fd;
864
+ rb_io_t *server_fptr;
892
865
  struct sockaddr addr;
893
866
  socklen_t len = (socklen_t)sizeof addr;
894
867
  VALUE socket = Qnil;
895
- VALUE underlying_sock = rb_ivar_get(server_socket, ID_ivar_io);
896
- if (underlying_sock != Qnil) server_socket = underlying_sock;
897
868
 
898
- GetOpenFile(server_socket, fptr);
899
- io_unset_nonblock(fptr, server_socket);
869
+ server_fd = fd_from_io(server_socket, &server_fptr, 0, 0);
900
870
 
901
871
  while (1) {
902
872
  VALUE resume_value = Qnil;
@@ -905,7 +875,7 @@ VALUE io_uring_backend_accept(Backend_t *backend, VALUE server_socket, VALUE soc
905
875
  int fd;
906
876
  int completed;
907
877
 
908
- io_uring_prep_accept(sqe, fptr->fd, &addr, &len, 0);
878
+ io_uring_prep_accept(sqe, server_fd, &addr, &len, 0);
909
879
 
910
880
  fd = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
911
881
  completed = context_store_release(&backend->store, ctx);
@@ -955,22 +925,15 @@ VALUE Backend_accept_loop(VALUE self, VALUE server_socket, VALUE socket_class) {
955
925
  }
956
926
 
957
927
  VALUE io_uring_backend_splice(Backend_t *backend, VALUE src, VALUE dest, VALUE maxlen, int loop) {
928
+ int src_fd;
929
+ int dest_fd;
958
930
  rb_io_t *src_fptr;
959
931
  rb_io_t *dest_fptr;
960
- VALUE underlying_io;
961
932
  int total = 0;
962
933
  VALUE resume_value = Qnil;
963
934
 
964
- underlying_io = rb_ivar_get(src, ID_ivar_io);
965
- if (underlying_io != Qnil) src = underlying_io;
966
- GetOpenFile(src, src_fptr);
967
- io_unset_nonblock(src_fptr, src);
968
-
969
- underlying_io = rb_ivar_get(dest, ID_ivar_io);
970
- if (underlying_io != Qnil) dest = underlying_io;
971
- dest = rb_io_get_write_io(dest);
972
- GetOpenFile(dest, dest_fptr);
973
- io_unset_nonblock(dest_fptr, dest);
935
+ src_fd = fd_from_io(src, &src_fptr, 0, 0);
936
+ dest_fd = fd_from_io(dest, &dest_fptr, 1, 0);
974
937
 
975
938
  while (1) {
976
939
  op_context_t *ctx = context_store_acquire(&backend->store, OP_SPLICE);
@@ -978,7 +941,7 @@ VALUE io_uring_backend_splice(Backend_t *backend, VALUE src, VALUE dest, VALUE m
978
941
  int result;
979
942
  int completed;
980
943
 
981
- io_uring_prep_splice(sqe, src_fptr->fd, -1, dest_fptr->fd, -1, NUM2INT(maxlen), 0);
944
+ io_uring_prep_splice(sqe, src_fd, -1, dest_fd, -1, NUM2INT(maxlen), 0);
982
945
 
983
946
  result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
984
947
  completed = context_store_release(&backend->store, ctx);
@@ -998,7 +961,6 @@ VALUE io_uring_backend_splice(Backend_t *backend, VALUE src, VALUE dest, VALUE m
998
961
  VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
999
962
  Backend_t *backend;
1000
963
  GetBackend(self, backend);
1001
-
1002
964
  return io_uring_backend_splice(backend, src, dest, maxlen, 0);
1003
965
  }
1004
966
 
@@ -1009,12 +971,47 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE chunksize)
1009
971
  return io_uring_backend_splice(backend, src, dest, chunksize, 1);
1010
972
  }
1011
973
 
974
+ VALUE Backend_tee(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
975
+ Backend_t *backend;
976
+ GetBackend(self, backend);
977
+
978
+ int src_fd;
979
+ int dest_fd;
980
+ rb_io_t *src_fptr;
981
+ rb_io_t *dest_fptr;
982
+ VALUE resume_value = Qnil;
983
+
984
+ src_fd = fd_from_io(src, &src_fptr, 0, 0);
985
+ dest_fd = fd_from_io(dest, &dest_fptr, 1, 0);
986
+
987
+ while (1) {
988
+ op_context_t *ctx = context_store_acquire(&backend->store, OP_SPLICE);
989
+ struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
990
+ int result;
991
+ int completed;
992
+
993
+ io_uring_prep_tee(sqe, src_fd, dest_fd, NUM2INT(maxlen), 0);
994
+
995
+ result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
996
+ completed = context_store_release(&backend->store, ctx);
997
+ RAISE_IF_EXCEPTION(resume_value);
998
+ if (!completed) return resume_value;
999
+
1000
+ if (result < 0)
1001
+ rb_syserr_fail(-result, strerror(-result));
1002
+
1003
+ return INT2NUM(result);
1004
+ }
1005
+
1006
+ RB_GC_GUARD(resume_value);
1007
+ }
1008
+
1012
1009
  VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
1013
1010
  Backend_t *backend;
1011
+ int fd;
1014
1012
  rb_io_t *fptr;
1015
1013
  struct sockaddr *ai_addr;
1016
1014
  int ai_addrlen;
1017
- VALUE underlying_sock = rb_ivar_get(sock, ID_ivar_io);
1018
1015
  VALUE resume_value = Qnil;
1019
1016
  op_context_t *ctx;
1020
1017
  struct io_uring_sqe *sqe;
@@ -1023,15 +1020,11 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
1023
1020
 
1024
1021
  ai_addrlen = backend_getaddrinfo(host, port, &ai_addr);
1025
1022
 
1026
- if (underlying_sock != Qnil) sock = underlying_sock;
1027
-
1028
1023
  GetBackend(self, backend);
1029
- GetOpenFile(sock, fptr);
1030
- io_unset_nonblock(fptr, sock);
1031
-
1024
+ fd = fd_from_io(sock, &fptr, 1, 0);
1032
1025
  ctx = context_store_acquire(&backend->store, OP_CONNECT);
1033
1026
  sqe = io_uring_get_sqe(&backend->ring);
1034
- io_uring_prep_connect(sqe, fptr->fd, ai_addr, ai_addrlen);
1027
+ io_uring_prep_connect(sqe, fd, ai_addr, ai_addrlen);
1035
1028
  result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
1036
1029
  completed = context_store_release(&backend->store, ctx);
1037
1030
  RAISE_IF_EXCEPTION(resume_value);
@@ -1044,16 +1037,14 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
1044
1037
 
1045
1038
  VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
1046
1039
  Backend_t *backend;
1040
+ int fd;
1047
1041
  rb_io_t *fptr;
1048
- VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
1049
1042
  VALUE resume_value;
1043
+ int write_mode = RTEST(write);
1050
1044
 
1051
- if (underlying_io != Qnil) io = underlying_io;
1052
1045
  GetBackend(self, backend);
1053
- GetOpenFile(io, fptr);
1054
- io_unset_nonblock(fptr, io);
1055
-
1056
- resume_value = io_uring_backend_wait_fd(backend, fptr->fd, RTEST(write));
1046
+ fd = fd_from_io(io, &fptr, write_mode, 0);
1047
+ resume_value = io_uring_backend_wait_fd(backend, fd, write_mode);
1057
1048
 
1058
1049
  RAISE_IF_EXCEPTION(resume_value);
1059
1050
  RB_GC_GUARD(resume_value);
@@ -1063,24 +1054,19 @@ VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
1063
1054
  // VALUE Backend_close(VALUE self, VALUE io) {
1064
1055
  // Backend_t *backend;
1065
1056
  // rb_io_t *fptr;
1066
- // VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
1067
1057
  // VALUE resume_value = Qnil;
1068
1058
  // op_context_t *ctx;
1069
1059
  // struct io_uring_sqe *sqe;
1070
1060
  // int result;
1071
1061
  // int completed;
1072
1062
 
1073
- // if (underlying_io != Qnil) io = underlying_io;
1074
- // GetBackend(self, backend);
1075
- // GetOpenFile(io, fptr);
1076
-
1077
- // if (fptr->fd < 0) return Qnil;
1063
+ // if (fd < 0) return Qnil;
1078
1064
 
1079
1065
  // io_unset_nonblock(fptr, io);
1080
1066
 
1081
1067
  // ctx = context_store_acquire(&backend->store, OP_CLOSE);
1082
1068
  // sqe = io_uring_get_sqe(&backend->ring);
1083
- // io_uring_prep_close(sqe, fptr->fd);
1069
+ // io_uring_prep_close(sqe, fd);
1084
1070
  // result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
1085
1071
  // completed = context_store_release(&backend->store, ctx);
1086
1072
  // RAISE_IF_EXCEPTION(resume_value);
@@ -1090,7 +1076,7 @@ VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
1090
1076
  // if (result < 0) rb_syserr_fail(-result, strerror(-result));
1091
1077
 
1092
1078
  // fptr_finalize(fptr);
1093
- // // fptr->fd = -1;
1079
+ // // fd = -1;
1094
1080
  // return io;
1095
1081
  // }
1096
1082
 
@@ -1174,7 +1160,7 @@ VALUE Backend_timeout_ensure(VALUE arg) {
1174
1160
  timeout_ctx->ctx->result = -ECANCELED;
1175
1161
  // op was not completed, so we need to cancel it
1176
1162
  sqe = io_uring_get_sqe(&timeout_ctx->backend->ring);
1177
- io_uring_prep_cancel(sqe, timeout_ctx->ctx, 0);
1163
+ io_uring_prep_cancel(sqe, (__u64)timeout_ctx->ctx, 0);
1178
1164
  timeout_ctx->backend->pending_sqes = 0;
1179
1165
  io_uring_submit(&timeout_ctx->backend->ring);
1180
1166
  }
@@ -1276,56 +1262,39 @@ VALUE Backend_kind(VALUE self) {
1276
1262
  }
1277
1263
 
1278
1264
  struct io_uring_sqe *Backend_chain_prepare_write(Backend_t *backend, VALUE io, VALUE str) {
1265
+ int fd;
1279
1266
  rb_io_t *fptr;
1280
- VALUE underlying_io;
1281
1267
  struct io_uring_sqe *sqe;
1282
1268
 
1283
- underlying_io = rb_ivar_get(io, ID_ivar_io);
1284
- if (underlying_io != Qnil) io = underlying_io;
1285
- io = rb_io_get_write_io(io);
1286
- GetOpenFile(io, fptr);
1287
- io_unset_nonblock(fptr, io);
1288
-
1269
+ fd = fd_from_io(io, &fptr, 1, 0);
1289
1270
  sqe = io_uring_get_sqe(&backend->ring);
1290
- io_uring_prep_write(sqe, fptr->fd, StringValuePtr(str), RSTRING_LEN(str), 0);
1271
+ io_uring_prep_write(sqe, fd, StringValuePtr(str), RSTRING_LEN(str), 0);
1291
1272
  return sqe;
1292
1273
  }
1293
1274
 
1294
1275
  struct io_uring_sqe *Backend_chain_prepare_send(Backend_t *backend, VALUE io, VALUE str, VALUE flags) {
1276
+ int fd;
1295
1277
  rb_io_t *fptr;
1296
- VALUE underlying_io;
1297
1278
  struct io_uring_sqe *sqe;
1298
1279
 
1299
- underlying_io = rb_ivar_get(io, ID_ivar_io);
1300
- if (underlying_io != Qnil) io = underlying_io;
1301
- io = rb_io_get_write_io(io);
1302
- GetOpenFile(io, fptr);
1303
- io_unset_nonblock(fptr, io);
1280
+ fd = fd_from_io(io, &fptr, 1, 0);
1304
1281
 
1305
1282
  sqe = io_uring_get_sqe(&backend->ring);
1306
- io_uring_prep_send(sqe, fptr->fd, StringValuePtr(str), RSTRING_LEN(str), NUM2INT(flags));
1283
+ io_uring_prep_send(sqe, fd, StringValuePtr(str), RSTRING_LEN(str), NUM2INT(flags));
1307
1284
  return sqe;
1308
1285
  }
1309
1286
 
1310
1287
  struct io_uring_sqe *Backend_chain_prepare_splice(Backend_t *backend, VALUE src, VALUE dest, VALUE maxlen) {
1288
+ int src_fd;
1289
+ int dest_fd;
1311
1290
  rb_io_t *src_fptr;
1312
1291
  rb_io_t *dest_fptr;
1313
- VALUE underlying_io;
1314
1292
  struct io_uring_sqe *sqe;
1315
1293
 
1316
- underlying_io = rb_ivar_get(src, ID_ivar_io);
1317
- if (underlying_io != Qnil) src = underlying_io;
1318
- GetOpenFile(src, src_fptr);
1319
- io_unset_nonblock(src_fptr, src);
1320
-
1321
- underlying_io = rb_ivar_get(dest, ID_ivar_io);
1322
- if (underlying_io != Qnil) dest = underlying_io;
1323
- dest = rb_io_get_write_io(dest);
1324
- GetOpenFile(dest, dest_fptr);
1325
- io_unset_nonblock(dest_fptr, dest);
1326
-
1294
+ src_fd = fd_from_io(src, &src_fptr, 0, 0);
1295
+ dest_fd = fd_from_io(dest, &dest_fptr, 1, 0);
1327
1296
  sqe = io_uring_get_sqe(&backend->ring);
1328
- io_uring_prep_splice(sqe, src_fptr->fd, -1, dest_fptr->fd, -1, NUM2INT(maxlen), 0);
1297
+ io_uring_prep_splice(sqe, src_fd, -1, dest_fd, -1, NUM2INT(maxlen), 0);
1329
1298
  return sqe;
1330
1299
  }
1331
1300
 
@@ -1384,7 +1353,7 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1384
1353
  ctx->ref_count = sqe_count;
1385
1354
  ctx->result = -ECANCELED;
1386
1355
  sqe = io_uring_get_sqe(&backend->ring);
1387
- io_uring_prep_cancel(sqe, ctx, 0);
1356
+ io_uring_prep_cancel(sqe, (__u64)ctx, 0);
1388
1357
  backend->pending_sqes = 0;
1389
1358
  io_uring_submit(&backend->ring);
1390
1359
  }
@@ -1415,7 +1384,7 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
1415
1384
  // op was not completed (an exception was raised), so we need to cancel it
1416
1385
  ctx->result = -ECANCELED;
1417
1386
  sqe = io_uring_get_sqe(&backend->ring);
1418
- io_uring_prep_cancel(sqe, ctx, 0);
1387
+ io_uring_prep_cancel(sqe, (__u64)ctx, 0);
1419
1388
  backend->pending_sqes = 0;
1420
1389
  io_uring_submit(&backend->ring);
1421
1390
  RAISE_IF_EXCEPTION(resume_value);
@@ -1482,7 +1451,7 @@ static inline void splice_chunks_cancel(Backend_t *backend, op_context_t *ctx) {
1482
1451
 
1483
1452
  ctx->result = -ECANCELED;
1484
1453
  sqe = io_uring_get_sqe(&backend->ring);
1485
- io_uring_prep_cancel(sqe, ctx, 0);
1454
+ io_uring_prep_cancel(sqe, (__u64)ctx, 0);
1486
1455
  backend->pending_sqes = 0;
1487
1456
  io_uring_submit(&backend->ring);
1488
1457
  }
@@ -1518,25 +1487,17 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
1518
1487
  op_context_t *ctx = 0;
1519
1488
  struct io_uring_sqe *sqe = 0;
1520
1489
  int maxlen;
1521
- VALUE underlying_io;
1522
1490
  VALUE str = Qnil;
1523
1491
  VALUE chunk_len_value = Qnil;
1492
+ int src_fd;
1493
+ int dest_fd;
1524
1494
  rb_io_t *src_fptr;
1525
1495
  rb_io_t *dest_fptr;
1526
1496
  int pipefd[2] = { -1, -1 };
1527
1497
 
1528
1498
  GetBackend(self, backend);
1529
-
1530
- underlying_io = rb_ivar_get(src, ID_ivar_io);
1531
- if (underlying_io != Qnil) src = underlying_io;
1532
- GetOpenFile(src, src_fptr);
1533
- io_verify_blocking_mode(src_fptr, src, Qtrue);
1534
-
1535
- underlying_io = rb_ivar_get(dest, ID_ivar_io);
1536
- if (underlying_io != Qnil) dest = underlying_io;
1537
- dest = rb_io_get_write_io(dest);
1538
- GetOpenFile(dest, dest_fptr);
1539
- io_verify_blocking_mode(dest_fptr, dest, Qtrue);
1499
+ src_fd = fd_from_io(src, &src_fptr, 0, 0);
1500
+ dest_fd = fd_from_io(dest, &dest_fptr, 1, 0);
1540
1501
 
1541
1502
  maxlen = NUM2INT(chunk_size);
1542
1503
 
@@ -1547,7 +1508,7 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
1547
1508
 
1548
1509
  if (prefix != Qnil) {
1549
1510
  splice_chunks_get_sqe(backend, &ctx, &sqe, OP_WRITE);
1550
- splice_chunks_prep_write(ctx, sqe, dest_fptr->fd, prefix);
1511
+ splice_chunks_prep_write(ctx, sqe, dest_fd, prefix);
1551
1512
  backend->base.op_count++;
1552
1513
  }
1553
1514
 
@@ -1557,7 +1518,7 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
1557
1518
  VALUE chunk_postfix_str = Qnil;
1558
1519
 
1559
1520
  splice_chunks_get_sqe(backend, &ctx, &sqe, OP_SPLICE);
1560
- splice_chunks_prep_splice(ctx, sqe, src_fptr->fd, pipefd[1], maxlen);
1521
+ splice_chunks_prep_splice(ctx, sqe, src_fd, pipefd[1], maxlen);
1561
1522
  backend->base.op_count++;
1562
1523
 
1563
1524
  SPLICE_CHUNKS_AWAIT_OPS(backend, &ctx, &chunk_len, &switchpoint_result);
@@ -1570,18 +1531,18 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
1570
1531
  if (chunk_prefix != Qnil) {
1571
1532
  chunk_prefix_str = (TYPE(chunk_prefix) == T_STRING) ? chunk_prefix : rb_funcall(chunk_prefix, ID_call, 1, chunk_len_value);
1572
1533
  splice_chunks_get_sqe(backend, &ctx, &sqe, OP_WRITE);
1573
- splice_chunks_prep_write(ctx, sqe, dest_fptr->fd, chunk_prefix_str);
1534
+ splice_chunks_prep_write(ctx, sqe, dest_fd, chunk_prefix_str);
1574
1535
  backend->base.op_count++;
1575
1536
  }
1576
1537
 
1577
1538
  splice_chunks_get_sqe(backend, &ctx, &sqe, OP_SPLICE);
1578
- splice_chunks_prep_splice(ctx, sqe, pipefd[0], dest_fptr->fd, chunk_len);
1539
+ splice_chunks_prep_splice(ctx, sqe, pipefd[0], dest_fd, chunk_len);
1579
1540
  backend->base.op_count++;
1580
1541
 
1581
1542
  if (chunk_postfix != Qnil) {
1582
1543
  chunk_postfix_str = (TYPE(chunk_postfix) == T_STRING) ? chunk_postfix : rb_funcall(chunk_postfix, ID_call, 1, chunk_len_value);
1583
1544
  splice_chunks_get_sqe(backend, &ctx, &sqe, OP_WRITE);
1584
- splice_chunks_prep_write(ctx, sqe, dest_fptr->fd, chunk_postfix_str);
1545
+ splice_chunks_prep_write(ctx, sqe, dest_fd, chunk_postfix_str);
1585
1546
  backend->base.op_count++;
1586
1547
  }
1587
1548
 
@@ -1591,7 +1552,7 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
1591
1552
 
1592
1553
  if (postfix != Qnil) {
1593
1554
  splice_chunks_get_sqe(backend, &ctx, &sqe, OP_WRITE);
1594
- splice_chunks_prep_write(ctx, sqe, dest_fptr->fd, postfix);
1555
+ splice_chunks_prep_write(ctx, sqe, dest_fd, postfix);
1595
1556
  backend->base.op_count++;
1596
1557
  }
1597
1558
  if (ctx) {
@@ -1693,6 +1654,7 @@ void Init_Backend() {
1693
1654
  rb_define_method(cBackend, "sleep", Backend_sleep, 1);
1694
1655
  rb_define_method(cBackend, "splice", Backend_splice, 3);
1695
1656
  rb_define_method(cBackend, "splice_to_eof", Backend_splice_to_eof, 3);
1657
+ rb_define_method(cBackend, "tee", Backend_tee, 3);
1696
1658
  rb_define_method(cBackend, "timeout", Backend_timeout, -1);
1697
1659
  rb_define_method(cBackend, "timer_loop", Backend_timer_loop, 1);
1698
1660
  rb_define_method(cBackend, "wait_event", Backend_wait_event, 1);