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
@@ -266,41 +266,74 @@ VALUE libev_wait_fd(Backend_t *backend, int fd, int events, int raise_exception)
266
266
  return switchpoint_result;
267
267
  }
268
268
 
269
+ static inline int fd_from_io(VALUE io, rb_io_t **fptr, int write_mode, int rectify_file_pos) {
270
+ if (rb_obj_class(io) == cPipe) {
271
+ *fptr = NULL;
272
+ Pipe_verify_blocking_mode(io, Qfalse);
273
+ return Pipe_get_fd(io, write_mode);
274
+ }
275
+ else {
276
+ VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
277
+ if (underlying_io != Qnil) io = underlying_io;
278
+
279
+ GetOpenFile(io, *fptr);
280
+ io_verify_blocking_mode(*fptr, io, Qfalse);
281
+ if (rectify_file_pos) rectify_io_file_pos(*fptr);
282
+
283
+ return (*fptr)->fd;
284
+ }
285
+ }
286
+
269
287
  VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof, VALUE pos) {
270
288
  Backend_t *backend;
271
289
  struct libev_io watcher;
290
+ int fd;
272
291
  rb_io_t *fptr;
273
- long dynamic_len = length == Qnil;
274
- long len = dynamic_len ? 4096 : NUM2INT(length);
292
+
293
+ struct io_buffer buffer = get_io_buffer(str);
275
294
  long buf_pos = NUM2INT(pos);
276
- if (str != Qnil) {
277
- int current_len = RSTRING_LEN(str);
278
- if (buf_pos < 0 || buf_pos > current_len) buf_pos = current_len;
279
- }
280
- else buf_pos = 0;
281
- int shrinkable = io_setstrbuf(&str, buf_pos + len);
282
- char *buf = RSTRING_PTR(str) + buf_pos;
295
+ int shrinkable_string = 0;
296
+ int expandable_buffer = 0;
283
297
  long total = 0;
284
298
  VALUE switchpoint_result = Qnil;
285
299
  int read_to_eof = RTEST(to_eof);
286
- VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
300
+
301
+ if (buffer.raw) {
302
+ if (buf_pos < 0 || buf_pos > buffer.len) buf_pos = buffer.len;
303
+ buffer.ptr += buf_pos;
304
+ buffer.len -= buf_pos;
305
+ }
306
+ else {
307
+ expandable_buffer = length == Qnil;
308
+ long expected_read_length = expandable_buffer ? 4096 : FIX2INT(length);
309
+ long string_cap = rb_str_capacity(str);
310
+ if (buf_pos < 0 || buf_pos > buffer.len) buf_pos = buffer.len;
311
+
312
+ if (string_cap < expected_read_length + buf_pos) {
313
+ shrinkable_string = io_setstrbuf(&str, expected_read_length + buf_pos);
314
+ buffer.ptr = RSTRING_PTR(str) + buf_pos;
315
+ buffer.len = expected_read_length;
316
+ }
317
+ else {
318
+ buffer.ptr += buf_pos;
319
+ buffer.len = string_cap - buf_pos;
320
+ if (buffer.len > expected_read_length)
321
+ buffer.len = expected_read_length;
322
+ }
323
+ }
287
324
 
288
325
  GetBackend(self, backend);
289
- if (underlying_io != Qnil) io = underlying_io;
290
- GetOpenFile(io, fptr);
291
- rb_io_check_byte_readable(fptr);
292
- io_verify_blocking_mode(fptr, io, Qfalse);
293
- rectify_io_file_pos(fptr);
326
+ fd = fd_from_io(io, &fptr, 0, 1);
294
327
  watcher.fiber = Qnil;
295
328
 
296
329
  while (1) {
297
330
  backend->base.op_count++;
298
- ssize_t n = read(fptr->fd, buf, len - total);
299
- if (n < 0) {
331
+ ssize_t result = read(fd, buffer.ptr, buffer.len);
332
+ if (result < 0) {
300
333
  int e = errno;
301
334
  if (e != EWOULDBLOCK && e != EAGAIN) rb_syserr_fail(e, strerror(e));
302
335
 
303
- switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
336
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fd, &watcher, EV_READ);
304
337
 
305
338
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
306
339
  }
@@ -308,32 +341,39 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof,
308
341
  switchpoint_result = backend_snooze(&backend->base);
309
342
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
310
343
 
311
- if (n == 0) break; // EOF
312
- total = total + n;
344
+ if (!result) break; // EOF
345
+
346
+ total += result;
313
347
  if (!read_to_eof) break;
314
348
 
315
- if (total == len) {
316
- if (!dynamic_len) break;
349
+ if (result == buffer.len) {
350
+ if (!expandable_buffer) break;
317
351
 
318
- rb_str_resize(str, buf_pos + total);
319
- rb_str_modify_expand(str, len);
320
- buf = RSTRING_PTR(str) + buf_pos + total;
321
- shrinkable = 0;
322
- len += len;
352
+ // resize buffer to double its capacity
353
+ rb_str_resize(str, total + buf_pos);
354
+ rb_str_modify_expand(str, rb_str_capacity(str));
355
+ shrinkable_string = 0;
356
+ buffer.ptr = RSTRING_PTR(str) + total + buf_pos;
357
+ buffer.len = rb_str_capacity(str) - total - buf_pos;
358
+ }
359
+ else {
360
+ buffer.ptr += result;
361
+ buffer.len -= result;
362
+ if (!buffer.len) break;
323
363
  }
324
- else buf += n;
325
364
  }
326
365
  }
327
366
 
328
- io_set_read_length(str, buf_pos + total, shrinkable);
329
- io_enc_str(str, fptr);
330
-
331
- if (total == 0) return Qnil;
367
+ if (!buffer.raw) {
368
+ io_set_read_length(str, buf_pos + total, shrinkable_string);
369
+ if (fptr) io_enc_str(str, fptr);
370
+ }
371
+ if (!total) return Qnil;
332
372
 
333
373
  RB_GC_GUARD(watcher.fiber);
334
374
  RB_GC_GUARD(switchpoint_result);
335
375
 
336
- return str;
376
+ return buffer.raw ? INT2FIX(total) : str;
337
377
  error:
338
378
  return RAISE_EXCEPTION(switchpoint_result);
339
379
  }
@@ -345,6 +385,7 @@ VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length, VALUE pos) {
345
385
  VALUE Backend_read_loop(VALUE self, VALUE io, VALUE maxlen) {
346
386
  Backend_t *backend;
347
387
  struct libev_io watcher;
388
+ int fd;
348
389
  rb_io_t *fptr;
349
390
  VALUE str;
350
391
  long total;
@@ -352,26 +393,21 @@ VALUE Backend_read_loop(VALUE self, VALUE io, VALUE maxlen) {
352
393
  int shrinkable;
353
394
  char *buf;
354
395
  VALUE switchpoint_result = Qnil;
355
- VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
356
396
 
357
397
  READ_LOOP_PREPARE_STR();
358
398
 
359
399
  GetBackend(self, backend);
360
- if (underlying_io != Qnil) io = underlying_io;
361
- GetOpenFile(io, fptr);
362
- rb_io_check_byte_readable(fptr);
363
- io_verify_blocking_mode(fptr, io, Qfalse);
364
- rectify_io_file_pos(fptr);
400
+ fd = fd_from_io(io, &fptr, 0, 1);
365
401
  watcher.fiber = Qnil;
366
402
 
367
403
  while (1) {
368
404
  backend->base.op_count++;
369
- ssize_t n = read(fptr->fd, buf, len);
405
+ ssize_t n = read(fd, buf, len);
370
406
  if (n < 0) {
371
407
  int e = errno;
372
408
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
373
409
 
374
- switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
410
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fd, &watcher, EV_READ);
375
411
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
376
412
  }
377
413
  else {
@@ -397,6 +433,7 @@ error:
397
433
  VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
398
434
  Backend_t *backend;
399
435
  struct libev_io watcher;
436
+ int fd;
400
437
  rb_io_t *fptr;
401
438
  VALUE str;
402
439
  long total;
@@ -404,27 +441,22 @@ VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
404
441
  int shrinkable;
405
442
  char *buf;
406
443
  VALUE switchpoint_result = Qnil;
407
- VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
408
444
  ID method_id = SYM2ID(method);
409
445
 
410
446
  READ_LOOP_PREPARE_STR();
411
447
 
412
448
  GetBackend(self, backend);
413
- if (underlying_io != Qnil) io = underlying_io;
414
- GetOpenFile(io, fptr);
415
- rb_io_check_byte_readable(fptr);
416
- io_verify_blocking_mode(fptr, io, Qfalse);
417
- rectify_io_file_pos(fptr);
449
+ fd = fd_from_io(io, &fptr, 0, 1);
418
450
  watcher.fiber = Qnil;
419
451
 
420
452
  while (1) {
421
453
  backend->base.op_count++;
422
- ssize_t n = read(fptr->fd, buf, len);
454
+ ssize_t n = read(fd, buf, len);
423
455
  if (n < 0) {
424
456
  int e = errno;
425
457
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
426
458
 
427
- switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
459
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fd, &watcher, EV_READ);
428
460
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
429
461
  }
430
462
  else {
@@ -450,35 +482,31 @@ error:
450
482
  VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
451
483
  Backend_t *backend;
452
484
  struct libev_io watcher;
485
+ int fd;
453
486
  rb_io_t *fptr;
454
487
  VALUE switchpoint_result = Qnil;
455
- VALUE underlying_io;
456
- char *buf = StringValuePtr(str);
457
- long len = RSTRING_LEN(str);
458
- long left = len;
459
488
 
460
- underlying_io = rb_ivar_get(io, ID_ivar_io);
461
- if (underlying_io != Qnil) io = underlying_io;
489
+ struct io_buffer buffer = get_io_buffer(str);
490
+ long left = buffer.len;
491
+
462
492
  GetBackend(self, backend);
463
- io = rb_io_get_write_io(io);
464
- GetOpenFile(io, fptr);
465
- io_verify_blocking_mode(fptr, io, Qfalse);
493
+ fd = fd_from_io(io, &fptr, 1, 0);
466
494
  watcher.fiber = Qnil;
467
495
 
468
496
  while (left > 0) {
469
497
  backend->base.op_count++;
470
- ssize_t n = write(fptr->fd, buf, left);
471
- if (n < 0) {
498
+ ssize_t result = write(fd, buffer.ptr, left);
499
+ if (result < 0) {
472
500
  int e = errno;
473
501
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
474
502
 
475
- switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_WRITE);
503
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fd, &watcher, EV_WRITE);
476
504
 
477
505
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
478
506
  }
479
507
  else {
480
- buf += n;
481
- left -= n;
508
+ buffer.ptr += result;
509
+ left -= result;
482
510
  }
483
511
  }
484
512
 
@@ -491,7 +519,7 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
491
519
  RB_GC_GUARD(watcher.fiber);
492
520
  RB_GC_GUARD(switchpoint_result);
493
521
 
494
- return INT2NUM(len);
522
+ return INT2NUM(buffer.len);
495
523
  error:
496
524
  return RAISE_EXCEPTION(switchpoint_result);
497
525
  }
@@ -499,21 +527,17 @@ error:
499
527
  VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
500
528
  Backend_t *backend;
501
529
  struct libev_io watcher;
530
+ int fd;
502
531
  rb_io_t *fptr;
503
532
  VALUE switchpoint_result = Qnil;
504
- VALUE underlying_io;
505
533
  long total_length = 0;
506
534
  long total_written = 0;
507
535
  struct iovec *iov = 0;
508
536
  struct iovec *iov_ptr = 0;
509
537
  int iov_count = argc;
510
538
 
511
- underlying_io = rb_ivar_get(io, ID_ivar_io);
512
- if (underlying_io != Qnil) io = underlying_io;
513
539
  GetBackend(self, backend);
514
- io = rb_io_get_write_io(io);
515
- GetOpenFile(io, fptr);
516
- io_verify_blocking_mode(fptr, io, Qfalse);
540
+ fd = fd_from_io(io, &fptr, 1, 0);
517
541
  watcher.fiber = Qnil;
518
542
 
519
543
  iov = malloc(iov_count * sizeof(struct iovec));
@@ -527,7 +551,7 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
527
551
 
528
552
  while (1) {
529
553
  backend->base.op_count++;
530
- ssize_t n = writev(fptr->fd, iov_ptr, iov_count);
554
+ ssize_t n = writev(fd, iov_ptr, iov_count);
531
555
  if (n < 0) {
532
556
  int e = errno;
533
557
  if ((e != EWOULDBLOCK && e != EAGAIN)) {
@@ -535,7 +559,7 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
535
559
  rb_syserr_fail(e, strerror(e));
536
560
  }
537
561
 
538
- switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_WRITE);
562
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fd, &watcher, EV_WRITE);
539
563
 
540
564
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
541
565
  }
@@ -585,26 +609,25 @@ VALUE Backend_write_m(int argc, VALUE *argv, VALUE self) {
585
609
  VALUE Backend_accept(VALUE self, VALUE server_socket, VALUE socket_class) {
586
610
  Backend_t *backend;
587
611
  struct libev_io watcher;
588
- rb_io_t *fptr;
612
+ int server_fd;
613
+ rb_io_t *server_fptr;
589
614
  int fd;
590
615
  struct sockaddr addr;
591
616
  socklen_t len = (socklen_t)sizeof addr;
592
617
  VALUE switchpoint_result = Qnil;
593
- VALUE underlying_sock = rb_ivar_get(server_socket, ID_ivar_io);
594
- if (underlying_sock != Qnil) server_socket = underlying_sock;
595
618
 
596
619
  GetBackend(self, backend);
597
- GetOpenFile(server_socket, fptr);
598
- io_verify_blocking_mode(fptr, server_socket, Qfalse);
620
+ server_fd = fd_from_io(server_socket, &server_fptr, 0, 0);
599
621
  watcher.fiber = Qnil;
622
+
600
623
  while (1) {
601
624
  backend->base.op_count++;
602
- fd = accept(fptr->fd, &addr, &len);
625
+ fd = accept(server_fd, &addr, &len);
603
626
  if (fd < 0) {
604
627
  int e = errno;
605
628
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
606
629
 
607
- switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
630
+ switchpoint_result = libev_wait_fd_with_watcher(backend, server_fd, &watcher, EV_READ);
608
631
 
609
632
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
610
633
  }
@@ -642,28 +665,25 @@ error:
642
665
  VALUE Backend_accept_loop(VALUE self, VALUE server_socket, VALUE socket_class) {
643
666
  Backend_t *backend;
644
667
  struct libev_io watcher;
645
- rb_io_t *fptr;
646
- int fd;
668
+ int server_fd;
669
+ rb_io_t *server_fptr;
647
670
  struct sockaddr addr;
648
671
  socklen_t len = (socklen_t)sizeof addr;
649
672
  VALUE switchpoint_result = Qnil;
650
673
  VALUE socket = Qnil;
651
- VALUE underlying_sock = rb_ivar_get(server_socket, ID_ivar_io);
652
- if (underlying_sock != Qnil) server_socket = underlying_sock;
653
674
 
654
675
  GetBackend(self, backend);
655
- GetOpenFile(server_socket, fptr);
656
- io_verify_blocking_mode(fptr, server_socket, Qfalse);
676
+ server_fd = fd_from_io(server_socket, &server_fptr, 0, 0);
657
677
  watcher.fiber = Qnil;
658
678
 
659
679
  while (1) {
660
680
  backend->base.op_count++;
661
- fd = accept(fptr->fd, &addr, &len);
681
+ int fd = accept(server_fd, &addr, &len);
662
682
  if (fd < 0) {
663
683
  int e = errno;
664
684
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
665
685
 
666
- switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
686
+ switchpoint_result = libev_wait_fd_with_watcher(backend, server_fd, &watcher, EV_READ);
667
687
 
668
688
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
669
689
  }
@@ -701,28 +721,25 @@ error:
701
721
  VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
702
722
  Backend_t *backend;
703
723
  struct libev_io watcher;
724
+ int fd;
704
725
  rb_io_t *fptr;
705
726
  struct sockaddr *ai_addr;
706
727
  int ai_addrlen;
707
728
  VALUE switchpoint_result = Qnil;
708
- VALUE underlying_sock = rb_ivar_get(sock, ID_ivar_io);
709
729
 
710
730
  ai_addrlen = backend_getaddrinfo(host, port, &ai_addr);
711
731
 
712
- if (underlying_sock != Qnil) sock = underlying_sock;
713
-
714
732
  GetBackend(self, backend);
715
- GetOpenFile(sock, fptr);
716
- io_verify_blocking_mode(fptr, sock, Qfalse);
733
+ fd = fd_from_io(sock, &fptr, 1, 0);
717
734
  watcher.fiber = Qnil;
718
735
 
719
736
  backend->base.op_count++;
720
- int result = connect(fptr->fd, ai_addr, ai_addrlen);
737
+ int result = connect(fd, ai_addr, ai_addrlen);
721
738
  if (result < 0) {
722
739
  int e = errno;
723
740
  if (e != EINPROGRESS) rb_syserr_fail(e, strerror(e));
724
741
 
725
- switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_WRITE);
742
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fd, &watcher, EV_WRITE);
726
743
 
727
744
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
728
745
  }
@@ -740,36 +757,32 @@ error:
740
757
  VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
741
758
  Backend_t *backend;
742
759
  struct libev_io watcher;
760
+ int fd;
743
761
  rb_io_t *fptr;
744
762
  VALUE switchpoint_result = Qnil;
745
- VALUE underlying_io;
746
- char *buf = StringValuePtr(str);
747
- long len = RSTRING_LEN(str);
748
- long left = len;
763
+
764
+ struct io_buffer buffer = get_io_buffer(str);
765
+ long left = buffer.len;
749
766
  int flags_int = NUM2INT(flags);
750
767
 
751
- underlying_io = rb_ivar_get(io, ID_ivar_io);
752
- if (underlying_io != Qnil) io = underlying_io;
753
768
  GetBackend(self, backend);
754
- io = rb_io_get_write_io(io);
755
- GetOpenFile(io, fptr);
756
- io_verify_blocking_mode(fptr, io, Qfalse);
769
+ fd = fd_from_io(io, &fptr, 1, 0);
757
770
  watcher.fiber = Qnil;
758
771
 
759
772
  while (left > 0) {
760
773
  backend->base.op_count++;
761
- ssize_t n = send(fptr->fd, buf, left, flags_int);
762
- if (n < 0) {
774
+ ssize_t result = send(fd, buffer.ptr, left, flags_int);
775
+ if (result < 0) {
763
776
  int e = errno;
764
777
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
765
778
 
766
- switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_WRITE);
779
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fd, &watcher, EV_WRITE);
767
780
 
768
781
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
769
782
  }
770
783
  else {
771
- buf += n;
772
- left -= n;
784
+ buffer.ptr += result;
785
+ left -= result;
773
786
  }
774
787
  }
775
788
 
@@ -782,7 +795,7 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
782
795
  RB_GC_GUARD(watcher.fiber);
783
796
  RB_GC_GUARD(switchpoint_result);
784
797
 
785
- return INT2NUM(len);
798
+ return INT2NUM(buffer.len);
786
799
  error:
787
800
  return RAISE_EXCEPTION(switchpoint_result);
788
801
  }
@@ -837,41 +850,30 @@ VALUE libev_wait_rw_fd_with_watcher(Backend_t *backend, int r_fd, int w_fd, stru
837
850
  return switchpoint_result;
838
851
  }
839
852
 
840
-
841
-
842
-
843
853
  #ifdef POLYPHONY_LINUX
844
854
  VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
845
855
  Backend_t *backend;
846
856
  struct libev_rw_io watcher;
847
857
  VALUE switchpoint_result = Qnil;
848
- VALUE underlying_io;
858
+ int src_fd;
859
+ int dest_fd;
849
860
  rb_io_t *src_fptr;
850
861
  rb_io_t *dest_fptr;
851
862
  int len;
852
863
 
853
864
  GetBackend(self, backend);
854
-
855
- underlying_io = rb_ivar_get(src, ID_ivar_io);
856
- if (underlying_io != Qnil) src = underlying_io;
857
- GetOpenFile(src, src_fptr);
858
- io_verify_blocking_mode(src_fptr, src, Qfalse);
859
-
860
- underlying_io = rb_ivar_get(dest, ID_ivar_io);
861
- if (underlying_io != Qnil) dest = underlying_io;
862
- dest = rb_io_get_write_io(dest);
863
- GetOpenFile(dest, dest_fptr);
864
- io_verify_blocking_mode(dest_fptr, dest, Qfalse);
865
-
865
+ src_fd = fd_from_io(src, &src_fptr, 0, 0);
866
+ dest_fd = fd_from_io(dest, &dest_fptr, 1, 0);
866
867
  watcher.ctx.fiber = Qnil;
868
+
867
869
  while (1) {
868
870
  backend->base.op_count++;
869
- len = splice(src_fptr->fd, 0, dest_fptr->fd, 0, NUM2INT(maxlen), 0);
871
+ len = splice(src_fd, 0, dest_fd, 0, NUM2INT(maxlen), 0);
870
872
  if (len < 0) {
871
873
  int e = errno;
872
874
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
873
875
 
874
- switchpoint_result = libev_wait_rw_fd_with_watcher(backend, src_fptr->fd, dest_fptr->fd, &watcher);
876
+ switchpoint_result = libev_wait_rw_fd_with_watcher(backend, src_fd, dest_fd, &watcher);
875
877
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
876
878
  }
877
879
  else {
@@ -896,34 +898,26 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
896
898
  Backend_t *backend;
897
899
  struct libev_rw_io watcher;
898
900
  VALUE switchpoint_result = Qnil;
899
- VALUE underlying_io;
901
+ int src_fd;
902
+ int dest_fd;
900
903
  rb_io_t *src_fptr;
901
904
  rb_io_t *dest_fptr;
902
905
  int len;
903
906
  int total = 0;
904
907
 
905
908
  GetBackend(self, backend);
906
-
907
- underlying_io = rb_ivar_get(src, ID_ivar_io);
908
- if (underlying_io != Qnil) src = underlying_io;
909
- GetOpenFile(src, src_fptr);
910
- io_verify_blocking_mode(src_fptr, src, Qfalse);
911
-
912
- underlying_io = rb_ivar_get(dest, ID_ivar_io);
913
- if (underlying_io != Qnil) dest = underlying_io;
914
- dest = rb_io_get_write_io(dest);
915
- GetOpenFile(dest, dest_fptr);
916
- io_verify_blocking_mode(dest_fptr, dest, Qfalse);
917
-
909
+ src_fd = fd_from_io(src, &src_fptr, 0, 0);
910
+ dest_fd = fd_from_io(dest, &dest_fptr, 1, 0);
918
911
  watcher.ctx.fiber = Qnil;
912
+
919
913
  while (1) {
920
914
  backend->base.op_count++;
921
- len = splice(src_fptr->fd, 0, dest_fptr->fd, 0, NUM2INT(maxlen), 0);
915
+ len = splice(src_fd, 0, dest_fd, 0, NUM2INT(maxlen), 0);
922
916
  if (len < 0) {
923
917
  int e = errno;
924
918
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
925
919
 
926
- switchpoint_result = libev_wait_rw_fd_with_watcher(backend, src_fptr->fd, dest_fptr->fd, &watcher);
920
+ switchpoint_result = libev_wait_rw_fd_with_watcher(backend, src_fd, dest_fd, &watcher);
927
921
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
928
922
  }
929
923
  else if (len == 0) {
@@ -946,12 +940,58 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
946
940
  error:
947
941
  return RAISE_EXCEPTION(switchpoint_result);
948
942
  }
943
+
944
+ VALUE Backend_tee(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
945
+ Backend_t *backend;
946
+ struct libev_rw_io watcher;
947
+ VALUE switchpoint_result = Qnil;
948
+ int src_fd;
949
+ int dest_fd;
950
+ rb_io_t *src_fptr;
951
+ rb_io_t *dest_fptr;
952
+ int len;
953
+
954
+ GetBackend(self, backend);
955
+ src_fd = fd_from_io(src, &src_fptr, 0, 0);
956
+ dest_fd = fd_from_io(dest, &dest_fptr, 1, 0);
957
+ watcher.ctx.fiber = Qnil;
958
+
959
+ while (1) {
960
+ backend->base.op_count++;
961
+ len = tee(src_fd, dest_fd, NUM2INT(maxlen), 0);
962
+ if (len < 0) {
963
+ int e = errno;
964
+ if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
965
+
966
+ switchpoint_result = libev_wait_rw_fd_with_watcher(backend, src_fd, dest_fd, &watcher);
967
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
968
+ }
969
+ else {
970
+ break;
971
+ }
972
+ }
973
+
974
+ if (watcher.ctx.fiber == Qnil) {
975
+ switchpoint_result = backend_snooze(&backend->base);
976
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
977
+ }
978
+
979
+ RB_GC_GUARD(watcher.ctx.fiber);
980
+ RB_GC_GUARD(switchpoint_result);
981
+
982
+ return INT2NUM(len);
983
+ error:
984
+ return RAISE_EXCEPTION(switchpoint_result);
985
+ }
986
+
949
987
  #else
988
+
950
989
  VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
951
990
  Backend_t *backend;
952
991
  struct libev_io watcher;
953
992
  VALUE switchpoint_result = Qnil;
954
- VALUE underlying_io;
993
+ int src_fd;
994
+ int dest_fd;
955
995
  rb_io_t *src_fptr;
956
996
  rb_io_t *dest_fptr;
957
997
  int len = NUM2INT(maxlen);
@@ -961,28 +1001,18 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
961
1001
  int total = 0;
962
1002
 
963
1003
  GetBackend(self, backend);
964
-
965
- underlying_io = rb_ivar_get(src, ID_ivar_io);
966
- if (underlying_io != Qnil) src = underlying_io;
967
- GetOpenFile(src, src_fptr);
968
- io_verify_blocking_mode(src_fptr, src, Qfalse);
969
-
970
- underlying_io = rb_ivar_get(dest, ID_ivar_io);
971
- if (underlying_io != Qnil) dest = underlying_io;
972
- dest = rb_io_get_write_io(dest);
973
- GetOpenFile(dest, dest_fptr);
974
- io_verify_blocking_mode(dest_fptr, dest, Qfalse);
975
-
1004
+ src_fd = fd_from_io(src, &src_fptr, 0, 0);
1005
+ dest_fd = fd_from_io(dest, &dest_fptr, 1, 0);
976
1006
  watcher.fiber = Qnil;
977
1007
 
978
1008
  while (1) {
979
1009
  backend->base.op_count++;
980
- ssize_t n = read(src_fptr->fd, buf, len);
1010
+ ssize_t n = read(src_fd, buf, len);
981
1011
  if (n < 0) {
982
1012
  int e = errno;
983
1013
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
984
1014
 
985
- switchpoint_result = libev_wait_fd_with_watcher(backend, src_fptr->fd, &watcher, EV_READ);
1015
+ switchpoint_result = libev_wait_fd_with_watcher(backend, src_fd, &watcher, EV_READ);
986
1016
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
987
1017
  }
988
1018
  else {
@@ -993,12 +1023,12 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
993
1023
 
994
1024
  while (left > 0) {
995
1025
  backend->base.op_count++;
996
- ssize_t n = write(dest_fptr->fd, buf, left);
1026
+ ssize_t n = write(dest_fd, buf, left);
997
1027
  if (n < 0) {
998
1028
  int e = errno;
999
1029
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
1000
1030
 
1001
- switchpoint_result = libev_wait_fd_with_watcher(backend, dest_fptr->fd, &watcher, EV_WRITE);
1031
+ switchpoint_result = libev_wait_fd_with_watcher(backend, dest_fd, &watcher, EV_WRITE);
1002
1032
 
1003
1033
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
1004
1034
  }
@@ -1026,7 +1056,8 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
1026
1056
  Backend_t *backend;
1027
1057
  struct libev_io watcher;
1028
1058
  VALUE switchpoint_result = Qnil;
1029
- VALUE underlying_io;
1059
+ int src_fd;
1060
+ int dest_fd;
1030
1061
  rb_io_t *src_fptr;
1031
1062
  rb_io_t *dest_fptr;
1032
1063
  int len = NUM2INT(maxlen);
@@ -1036,30 +1067,20 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
1036
1067
  int total = 0;
1037
1068
 
1038
1069
  GetBackend(self, backend);
1039
-
1040
- underlying_io = rb_ivar_get(src, ID_ivar_io);
1041
- if (underlying_io != Qnil) src = underlying_io;
1042
- GetOpenFile(src, src_fptr);
1043
- io_verify_blocking_mode(src_fptr, src, Qfalse);
1044
-
1045
- underlying_io = rb_ivar_get(dest, ID_ivar_io);
1046
- if (underlying_io != Qnil) dest = underlying_io;
1047
- dest = rb_io_get_write_io(dest);
1048
- GetOpenFile(dest, dest_fptr);
1049
- io_verify_blocking_mode(dest_fptr, dest, Qfalse);
1050
-
1070
+ src_fd = fd_from_io(src, &src_fptr, 0, 0);
1071
+ dest_fd = fd_from_io(dest, &dest_fptr, 1, 0);
1051
1072
  watcher.fiber = Qnil;
1052
1073
 
1053
1074
  while (1) {
1054
1075
  char *ptr = buf;
1055
1076
  while (1) {
1056
1077
  backend->base.op_count++;
1057
- ssize_t n = read(src_fptr->fd, ptr, len);
1078
+ ssize_t n = read(src_fd, ptr, len);
1058
1079
  if (n < 0) {
1059
1080
  int e = errno;
1060
1081
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
1061
1082
 
1062
- switchpoint_result = libev_wait_fd_with_watcher(backend, src_fptr->fd, &watcher, EV_READ);
1083
+ switchpoint_result = libev_wait_fd_with_watcher(backend, src_fd, &watcher, EV_READ);
1063
1084
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
1064
1085
  }
1065
1086
  else if (n == 0) goto done;
@@ -1072,12 +1093,12 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
1072
1093
 
1073
1094
  while (left > 0) {
1074
1095
  backend->base.op_count++;
1075
- ssize_t n = write(dest_fptr->fd, ptr, left);
1096
+ ssize_t n = write(dest_fd, ptr, left);
1076
1097
  if (n < 0) {
1077
1098
  int e = errno;
1078
1099
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
1079
1100
 
1080
- switchpoint_result = libev_wait_fd_with_watcher(backend, dest_fptr->fd, &watcher, EV_WRITE);
1101
+ switchpoint_result = libev_wait_fd_with_watcher(backend, dest_fd, &watcher, EV_WRITE);
1081
1102
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
1082
1103
  }
1083
1104
  else {
@@ -1105,15 +1126,15 @@ error:
1105
1126
 
1106
1127
  VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
1107
1128
  Backend_t *backend;
1129
+ int fd;
1108
1130
  rb_io_t *fptr;
1109
- int events = RTEST(write) ? EV_WRITE : EV_READ;
1110
- VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
1111
- if (underlying_io != Qnil) io = underlying_io;
1131
+ int write_mode = RTEST(write);
1132
+ int events = write_mode ? EV_WRITE : EV_READ;
1112
1133
  GetBackend(self, backend);
1113
- GetOpenFile(io, fptr);
1134
+ fd = fd_from_io(io, &fptr, write_mode, 0);
1114
1135
 
1115
1136
  backend->base.op_count++;
1116
- return libev_wait_fd(backend, fptr->fd, events, 1);
1137
+ return libev_wait_fd(backend, fd, events, 1);
1117
1138
  }
1118
1139
 
1119
1140
  struct libev_timer {
@@ -1460,19 +1481,13 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
1460
1481
  int err = 0;
1461
1482
  VALUE result = Qnil;
1462
1483
 
1484
+ int src_fd;
1485
+ int dest_fd;
1463
1486
  rb_io_t *src_fptr;
1464
1487
  rb_io_t *dest_fptr;
1465
1488
 
1466
- VALUE underlying_io = rb_ivar_get(src, ID_ivar_io);
1467
- if (underlying_io != Qnil) src = underlying_io;
1468
- GetOpenFile(src, src_fptr);
1469
- io_verify_blocking_mode(src_fptr, src, Qfalse);
1470
-
1471
- underlying_io = rb_ivar_get(dest, ID_ivar_io);
1472
- if (underlying_io != Qnil) dest = underlying_io;
1473
- dest = rb_io_get_write_io(dest);
1474
- GetOpenFile(dest, dest_fptr);
1475
- io_verify_blocking_mode(dest_fptr, dest, Qfalse);
1489
+ src_fd = fd_from_io(src, &src_fptr, 0, 0);
1490
+ dest_fd = fd_from_io(dest, &dest_fptr, 1, 0);
1476
1491
 
1477
1492
  struct libev_rw_io watcher;
1478
1493
  watcher.ctx.fiber = Qnil;
@@ -1490,12 +1505,12 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
1490
1505
  fcntl(pipefd[1], F_SETFL, O_NONBLOCK);
1491
1506
 
1492
1507
  if (prefix != Qnil) {
1493
- err = splice_chunks_write(backend, dest_fptr->fd, prefix, &watcher, &result);
1508
+ err = splice_chunks_write(backend, dest_fd, prefix, &watcher, &result);
1494
1509
  if (err == -1) goto error; else if (err) goto syscallerror;
1495
1510
  }
1496
1511
  while (1) {
1497
1512
  int chunk_len = 0;
1498
- err = splice_chunks_splice(backend, src_fptr->fd, pipefd[1], maxlen, &watcher, &result, &chunk_len);
1513
+ err = splice_chunks_splice(backend, src_fd, pipefd[1], maxlen, &watcher, &result, &chunk_len);
1499
1514
  if (err == -1) goto error; else if (err) goto syscallerror;
1500
1515
  if (chunk_len == 0) break;
1501
1516
 
@@ -1504,14 +1519,14 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
1504
1519
 
1505
1520
  if (chunk_prefix != Qnil) {
1506
1521
  VALUE str = (TYPE(chunk_prefix) == T_STRING) ? chunk_prefix : rb_funcall(chunk_prefix, ID_call, 1, chunk_len_value);
1507
- int err = splice_chunks_write(backend, dest_fptr->fd, str, &watcher, &result);
1522
+ int err = splice_chunks_write(backend, dest_fd, str, &watcher, &result);
1508
1523
  if (err == -1) goto error; else if (err) goto syscallerror;
1509
1524
  }
1510
1525
 
1511
1526
  int left = chunk_len;
1512
1527
  while (left > 0) {
1513
1528
  int len;
1514
- err = splice_chunks_splice(backend, pipefd[0], dest_fptr->fd, left, &watcher, &result, &len);
1529
+ err = splice_chunks_splice(backend, pipefd[0], dest_fd, left, &watcher, &result, &len);
1515
1530
  if (err == -1) goto error; else if (err) goto syscallerror;
1516
1531
 
1517
1532
  left -= len;
@@ -1519,13 +1534,13 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
1519
1534
 
1520
1535
  if (chunk_postfix != Qnil) {
1521
1536
  VALUE str = (TYPE(chunk_postfix) == T_STRING) ? chunk_postfix : rb_funcall(chunk_postfix, ID_call, 1, chunk_len_value);
1522
- int err = splice_chunks_write(backend, dest_fptr->fd, str, &watcher, &result);
1537
+ int err = splice_chunks_write(backend, dest_fd, str, &watcher, &result);
1523
1538
  if (err == -1) goto error; else if (err) goto syscallerror;
1524
1539
  }
1525
1540
  }
1526
1541
 
1527
1542
  if (postfix != Qnil) {
1528
- int err = splice_chunks_write(backend, dest_fptr->fd, postfix, &watcher, &result);
1543
+ int err = splice_chunks_write(backend, dest_fd, postfix, &watcher, &result);
1529
1544
  if (err == -1) goto error; else if (err) goto syscallerror;
1530
1545
  }
1531
1546