io-event 1.19.5 → 1.20.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f7b5b3a617c2f1ef060c072fc860927172b31a8e7e574cc3607f8107326b57bf
4
- data.tar.gz: '078687c1ee12579d38d9c5d85f8ad4a49827872567ed48f6835098a8081a6929'
3
+ metadata.gz: 648484dee2c6698568b24073b4fcf90a32629dcfa2119c450df16cae77f52c49
4
+ data.tar.gz: 895241950306f5dbc0f1c0b31f6981ec78096c6f90160c018a56dccc6102b799
5
5
  SHA512:
6
- metadata.gz: '063501649153028126c8b1087d60c8c089aeb03f536ca86e3f5be2a494d891ab1bfca0b08fc796f5d050a8d28dab68c1ffda6fcf2f6e3ea4b20c4ea0310ae8c3'
7
- data.tar.gz: cd0df567da5d936491da4c454106ae0c80e4b5a0368bc2ce2ddf02642dd5e313aec14e34d74d3b700be46d9f15f9e76413dae7a8397716afcc08e24a8280b1d9
6
+ metadata.gz: ff589133ec2d19d400c0c80daf0dc1cbf448005d26ca2c2df1d893c08371387cc12d37e8d1133379b17ed599cbe947197e0765e59c9c30c603802794283d9226
7
+ data.tar.gz: ec3c7a5d210053cf1e66d22eb4673c208184a044cb3b55404f34d645fcca0267711fe36ee12b7e8a7ef360d5ff6ed70a3da5ad0d64765406ec52ea2295119c40
checksums.yaml.gz.sig CHANGED
Binary file
@@ -33,21 +33,21 @@ input, output = IO.pipe
33
33
 
34
34
  writer = Fiber.new do
35
35
  puts "[writer] Writing data"
36
-
36
+
37
37
  output.write("Hello World")
38
38
  output.close
39
39
  end
40
40
 
41
41
  reader = Fiber.new do
42
42
  puts "[reader] No data available, waiting for input"
43
-
43
+
44
44
  # Suspend this Fiber until the input becomes readable.
45
45
  selector.io_wait(
46
46
  Fiber.current,
47
47
  input,
48
48
  IO::READABLE
49
49
  )
50
-
50
+
51
51
  # Execution resumes here once the selector detects the event.
52
52
  puts "[reader] Received: #{input.read.inspect}"
53
53
  end
@@ -593,9 +593,11 @@ VALUE IO_Event_Selector_EPoll_io_wait(VALUE self, VALUE fiber, VALUE io, VALUE e
593
593
  #ifdef HAVE_RUBY_IO_BUFFER_H
594
594
 
595
595
  struct io_read_arguments {
596
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
596
597
  VALUE self;
597
598
  VALUE fiber;
598
599
  VALUE io;
600
+ #endif
599
601
 
600
602
  int flags;
601
603
 
@@ -605,14 +607,20 @@ struct io_read_arguments {
605
607
  void *base;
606
608
  size_t size;
607
609
 
610
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
608
611
  // The minimum number of bytes requested by the caller.
609
612
  size_t length;
613
+ #endif
610
614
  };
611
615
 
612
616
  static
613
617
  VALUE io_read_loop(VALUE _arguments) {
614
618
  struct io_read_arguments *arguments = (struct io_read_arguments *)_arguments;
615
619
 
620
+ #if RUBY_FIBER_SCHEDULER_VERSION >= 4
621
+ ssize_t result = read(arguments->descriptor, arguments->base, arguments->size);
622
+ return rb_fiber_scheduler_io_result(result, errno);
623
+ #else
616
624
  size_t length = arguments->length;
617
625
  size_t total = 0;
618
626
 
@@ -635,6 +643,7 @@ VALUE io_read_loop(VALUE _arguments) {
635
643
  }
636
644
 
637
645
  return rb_fiber_scheduler_io_result(total, 0);
646
+ #endif
638
647
  }
639
648
 
640
649
  static
@@ -646,14 +655,27 @@ VALUE io_read_ensure(VALUE _arguments) {
646
655
  return Qnil;
647
656
  }
648
657
 
649
- VALUE IO_Event_Selector_EPoll_io_read(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _length, VALUE _offset) {
650
- size_t offset = NUM2SIZET(_offset);
651
- size_t length = NUM2SIZET(_length);
652
-
658
+ VALUE IO_Event_Selector_EPoll_io_read(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _first, VALUE _second) {
653
659
  void *base;
654
660
  size_t size;
655
661
  rb_io_buffer_get_bytes_for_writing(buffer, &base, &size);
656
662
 
663
+ #if RUBY_FIBER_SCHEDULER_VERSION >= 4
664
+ size_t offset = NUM2SIZET(_first);
665
+ size_t length = NUM2SIZET(_second);
666
+
667
+ if (!IO_Event_Selector_valid_buffer_range(size, offset, length)) {
668
+ return rb_fiber_scheduler_io_result(-1, EINVAL);
669
+ } else if (length == 0) {
670
+ return rb_fiber_scheduler_io_result(0, 0);
671
+ }
672
+
673
+ base = (char*)base + offset;
674
+ size = length;
675
+ #else
676
+ size_t length = NUM2SIZET(_first);
677
+ size_t offset = NUM2SIZET(_second);
678
+
657
679
  if (offset > size) {
658
680
  return rb_fiber_scheduler_io_result(-1, EINVAL);
659
681
  } else if (offset == size) {
@@ -662,19 +684,24 @@ VALUE IO_Event_Selector_EPoll_io_read(VALUE self, VALUE fiber, VALUE io, VALUE b
662
684
 
663
685
  base = (char*)base + offset;
664
686
  size -= offset;
687
+ #endif
665
688
 
666
689
  int descriptor = IO_Event_Selector_io_descriptor(io);
667
690
 
668
691
  struct io_read_arguments io_read_arguments = {
692
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
669
693
  .self = self,
670
694
  .fiber = fiber,
671
695
  .io = io,
696
+ #endif
672
697
 
673
698
  .flags = IO_Event_Selector_nonblock_set(descriptor),
674
699
  .descriptor = descriptor,
675
700
  .base = base,
676
701
  .size = size,
702
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
677
703
  .length = length,
704
+ #endif
678
705
  };
679
706
 
680
707
  RB_OBJ_WRITTEN(self, Qundef, fiber);
@@ -682,6 +709,7 @@ VALUE IO_Event_Selector_EPoll_io_read(VALUE self, VALUE fiber, VALUE io, VALUE b
682
709
  return rb_ensure(io_read_loop, (VALUE)&io_read_arguments, io_read_ensure, (VALUE)&io_read_arguments);
683
710
  }
684
711
 
712
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
685
713
  VALUE IO_Event_Selector_EPoll_io_read_compatible(int argc, VALUE *argv, VALUE self)
686
714
  {
687
715
  rb_check_arity(argc, 4, 5);
@@ -694,11 +722,14 @@ VALUE IO_Event_Selector_EPoll_io_read_compatible(int argc, VALUE *argv, VALUE se
694
722
 
695
723
  return IO_Event_Selector_EPoll_io_read(self, argv[0], argv[1], argv[2], argv[3], _offset);
696
724
  }
725
+ #endif
697
726
 
698
727
  struct io_write_arguments {
728
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
699
729
  VALUE self;
700
730
  VALUE fiber;
701
731
  VALUE io;
732
+ #endif
702
733
 
703
734
  int flags;
704
735
 
@@ -708,14 +739,20 @@ struct io_write_arguments {
708
739
  const void *base;
709
740
  size_t size;
710
741
 
742
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
711
743
  // The minimum number of bytes requested by the caller.
712
744
  size_t length;
745
+ #endif
713
746
  };
714
747
 
715
748
  static
716
749
  VALUE io_write_loop(VALUE _arguments) {
717
750
  struct io_write_arguments *arguments = (struct io_write_arguments *)_arguments;
718
751
 
752
+ #if RUBY_FIBER_SCHEDULER_VERSION >= 4
753
+ ssize_t result = write(arguments->descriptor, arguments->base, arguments->size);
754
+ return rb_fiber_scheduler_io_result(result, errno);
755
+ #else
719
756
  size_t length = arguments->length;
720
757
  size_t total = 0;
721
758
 
@@ -738,6 +775,7 @@ VALUE io_write_loop(VALUE _arguments) {
738
775
  }
739
776
 
740
777
  return rb_fiber_scheduler_io_result(total, 0);
778
+ #endif
741
779
  };
742
780
 
743
781
  static
@@ -749,14 +787,27 @@ VALUE io_write_ensure(VALUE _arguments) {
749
787
  return Qnil;
750
788
  };
751
789
 
752
- VALUE IO_Event_Selector_EPoll_io_write(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _length, VALUE _offset) {
753
- size_t length = NUM2SIZET(_length);
754
- size_t offset = NUM2SIZET(_offset);
755
-
790
+ VALUE IO_Event_Selector_EPoll_io_write(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _first, VALUE _second) {
756
791
  const void *base;
757
792
  size_t size;
758
793
  rb_io_buffer_get_bytes_for_reading(buffer, &base, &size);
759
794
 
795
+ #if RUBY_FIBER_SCHEDULER_VERSION >= 4
796
+ size_t offset = NUM2SIZET(_first);
797
+ size_t length = NUM2SIZET(_second);
798
+
799
+ if (!IO_Event_Selector_valid_buffer_range(size, offset, length)) {
800
+ return rb_fiber_scheduler_io_result(-1, EINVAL);
801
+ } else if (length == 0) {
802
+ return rb_fiber_scheduler_io_result(0, 0);
803
+ }
804
+
805
+ base = (const char*)base + offset;
806
+ size = length;
807
+ #else
808
+ size_t length = NUM2SIZET(_first);
809
+ size_t offset = NUM2SIZET(_second);
810
+
760
811
  if (length > size) {
761
812
  rb_raise(rb_eRuntimeError, "Length exceeds size of buffer!");
762
813
  }
@@ -769,19 +820,24 @@ VALUE IO_Event_Selector_EPoll_io_write(VALUE self, VALUE fiber, VALUE io, VALUE
769
820
 
770
821
  base = (const char*)base + offset;
771
822
  size -= offset;
823
+ #endif
772
824
 
773
825
  int descriptor = IO_Event_Selector_io_descriptor(io);
774
826
 
775
827
  struct io_write_arguments io_write_arguments = {
828
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
776
829
  .self = self,
777
830
  .fiber = fiber,
778
831
  .io = io,
832
+ #endif
779
833
 
780
834
  .flags = IO_Event_Selector_nonblock_set(descriptor),
781
835
  .descriptor = descriptor,
782
836
  .base = base,
783
837
  .size = size,
838
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
784
839
  .length = length,
840
+ #endif
785
841
  };
786
842
 
787
843
  RB_OBJ_WRITTEN(self, Qundef, fiber);
@@ -789,6 +845,7 @@ VALUE IO_Event_Selector_EPoll_io_write(VALUE self, VALUE fiber, VALUE io, VALUE
789
845
  return rb_ensure(io_write_loop, (VALUE)&io_write_arguments, io_write_ensure, (VALUE)&io_write_arguments);
790
846
  }
791
847
 
848
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
792
849
  VALUE IO_Event_Selector_EPoll_io_write_compatible(int argc, VALUE *argv, VALUE self)
793
850
  {
794
851
  rb_check_arity(argc, 4, 5);
@@ -801,6 +858,7 @@ VALUE IO_Event_Selector_EPoll_io_write_compatible(int argc, VALUE *argv, VALUE s
801
858
 
802
859
  return IO_Event_Selector_EPoll_io_write(self, argv[0], argv[1], argv[2], argv[3], _offset);
803
860
  }
861
+ #endif
804
862
 
805
863
  #endif
806
864
 
@@ -1123,13 +1181,14 @@ void Init_IO_Event_Selector_EPoll(VALUE IO_Event_Selector) {
1123
1181
  rb_define_method(IO_Event_Selector_EPoll, "io_wait", IO_Event_Selector_EPoll_io_wait, 3);
1124
1182
 
1125
1183
  #ifdef HAVE_RUBY_IO_BUFFER_H
1184
+ #if RUBY_FIBER_SCHEDULER_VERSION >= 4
1185
+ rb_define_method(IO_Event_Selector_EPoll, "io_read", IO_Event_Selector_EPoll_io_read, 5);
1186
+ rb_define_method(IO_Event_Selector_EPoll, "io_write", IO_Event_Selector_EPoll_io_write, 5);
1187
+ #else
1126
1188
  rb_define_method(IO_Event_Selector_EPoll, "io_read", IO_Event_Selector_EPoll_io_read_compatible, -1);
1127
1189
  rb_define_method(IO_Event_Selector_EPoll, "io_write", IO_Event_Selector_EPoll_io_write_compatible, -1);
1128
1190
  #endif
1129
-
1130
- // Once compatibility isn't a concern, we can do this:
1131
- // rb_define_method(IO_Event_Selector_EPoll, "io_read", IO_Event_Selector_EPoll_io_read, 5);
1132
- // rb_define_method(IO_Event_Selector_EPoll, "io_write", IO_Event_Selector_EPoll_io_write, 5);
1191
+ #endif
1133
1192
 
1134
1193
  rb_define_method(IO_Event_Selector_EPoll, "process_wait", IO_Event_Selector_EPoll_process_wait, 3);
1135
1194
  }
@@ -582,9 +582,11 @@ VALUE IO_Event_Selector_KQueue_io_wait(VALUE self, VALUE fiber, VALUE io, VALUE
582
582
  #ifdef HAVE_RUBY_IO_BUFFER_H
583
583
 
584
584
  struct io_read_arguments {
585
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
585
586
  VALUE self;
586
587
  VALUE fiber;
587
588
  VALUE io;
589
+ #endif
588
590
 
589
591
  int flags;
590
592
 
@@ -594,14 +596,20 @@ struct io_read_arguments {
594
596
  void *base;
595
597
  size_t size;
596
598
 
599
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
597
600
  // The minimum number of bytes requested by the caller.
598
601
  size_t length;
602
+ #endif
599
603
  };
600
604
 
601
605
  static
602
606
  VALUE io_read_loop(VALUE _arguments) {
603
607
  struct io_read_arguments *arguments = (struct io_read_arguments *)_arguments;
604
608
 
609
+ #if RUBY_FIBER_SCHEDULER_VERSION >= 4
610
+ ssize_t result = read(arguments->descriptor, arguments->base, arguments->size);
611
+ return rb_fiber_scheduler_io_result(result, errno);
612
+ #else
605
613
  size_t length = arguments->length;
606
614
  size_t total = 0;
607
615
 
@@ -631,6 +639,7 @@ VALUE io_read_loop(VALUE _arguments) {
631
639
 
632
640
  if (DEBUG_IO_READ) fprintf(stderr, "io_read_loop(fd=%d, length=%zu) -> %zu\n", arguments->descriptor, length, total);
633
641
  return rb_fiber_scheduler_io_result(total, 0);
642
+ #endif
634
643
  }
635
644
 
636
645
  static
@@ -642,17 +651,30 @@ VALUE io_read_ensure(VALUE _arguments) {
642
651
  return Qnil;
643
652
  }
644
653
 
645
- VALUE IO_Event_Selector_KQueue_io_read(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _length, VALUE _offset) {
654
+ VALUE IO_Event_Selector_KQueue_io_read(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _first, VALUE _second) {
646
655
  struct IO_Event_Selector_KQueue *selector = NULL;
647
656
  TypedData_Get_Struct(self, struct IO_Event_Selector_KQueue, &IO_Event_Selector_KQueue_Type, selector);
648
657
 
649
- size_t length = NUM2SIZET(_length);
650
- size_t offset = NUM2SIZET(_offset);
651
-
652
658
  void *base;
653
659
  size_t size;
654
660
  rb_io_buffer_get_bytes_for_writing(buffer, &base, &size);
655
661
 
662
+ #if RUBY_FIBER_SCHEDULER_VERSION >= 4
663
+ size_t offset = NUM2SIZET(_first);
664
+ size_t length = NUM2SIZET(_second);
665
+
666
+ if (!IO_Event_Selector_valid_buffer_range(size, offset, length)) {
667
+ return rb_fiber_scheduler_io_result(-1, EINVAL);
668
+ } else if (length == 0) {
669
+ return rb_fiber_scheduler_io_result(0, 0);
670
+ }
671
+
672
+ base = (char*)base + offset;
673
+ size = length;
674
+ #else
675
+ size_t length = NUM2SIZET(_first);
676
+ size_t offset = NUM2SIZET(_second);
677
+
656
678
  if (offset > size) {
657
679
  return rb_fiber_scheduler_io_result(-1, EINVAL);
658
680
  } else if (offset == size) {
@@ -661,19 +683,24 @@ VALUE IO_Event_Selector_KQueue_io_read(VALUE self, VALUE fiber, VALUE io, VALUE
661
683
 
662
684
  base = (char*)base + offset;
663
685
  size -= offset;
686
+ #endif
664
687
 
665
688
  int descriptor = IO_Event_Selector_io_descriptor(io);
666
689
 
667
690
  struct io_read_arguments io_read_arguments = {
691
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
668
692
  .self = self,
669
693
  .fiber = fiber,
670
694
  .io = io,
695
+ #endif
671
696
 
672
697
  .flags = IO_Event_Selector_nonblock_set(descriptor),
673
698
  .descriptor = descriptor,
674
699
  .base = base,
675
700
  .size = size,
701
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
676
702
  .length = length,
703
+ #endif
677
704
  };
678
705
 
679
706
  RB_OBJ_WRITTEN(self, Qundef, fiber);
@@ -681,6 +708,7 @@ VALUE IO_Event_Selector_KQueue_io_read(VALUE self, VALUE fiber, VALUE io, VALUE
681
708
  return rb_ensure(io_read_loop, (VALUE)&io_read_arguments, io_read_ensure, (VALUE)&io_read_arguments);
682
709
  }
683
710
 
711
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
684
712
  static VALUE IO_Event_Selector_KQueue_io_read_compatible(int argc, VALUE *argv, VALUE self)
685
713
  {
686
714
  rb_check_arity(argc, 4, 5);
@@ -693,11 +721,14 @@ static VALUE IO_Event_Selector_KQueue_io_read_compatible(int argc, VALUE *argv,
693
721
 
694
722
  return IO_Event_Selector_KQueue_io_read(self, argv[0], argv[1], argv[2], argv[3], _offset);
695
723
  }
724
+ #endif
696
725
 
697
726
  struct io_write_arguments {
727
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
698
728
  VALUE self;
699
729
  VALUE fiber;
700
730
  VALUE io;
731
+ #endif
701
732
 
702
733
  int flags;
703
734
 
@@ -707,14 +738,20 @@ struct io_write_arguments {
707
738
  const void *base;
708
739
  size_t size;
709
740
 
741
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
710
742
  // The minimum number of bytes requested by the caller.
711
743
  size_t length;
744
+ #endif
712
745
  };
713
746
 
714
747
  static
715
748
  VALUE io_write_loop(VALUE _arguments) {
716
749
  struct io_write_arguments *arguments = (struct io_write_arguments *)_arguments;
717
750
 
751
+ #if RUBY_FIBER_SCHEDULER_VERSION >= 4
752
+ ssize_t result = write(arguments->descriptor, arguments->base, arguments->size);
753
+ return rb_fiber_scheduler_io_result(result, errno);
754
+ #else
718
755
  size_t length = arguments->length;
719
756
  size_t total = 0;
720
757
 
@@ -744,6 +781,7 @@ VALUE io_write_loop(VALUE _arguments) {
744
781
 
745
782
  if (DEBUG_IO_WRITE) fprintf(stderr, "io_write_loop(fd=%d, length=%zu) -> %zu\n", arguments->descriptor, length, total);
746
783
  return rb_fiber_scheduler_io_result(total, 0);
784
+ #endif
747
785
  };
748
786
 
749
787
  static
@@ -755,17 +793,30 @@ VALUE io_write_ensure(VALUE _arguments) {
755
793
  return Qnil;
756
794
  };
757
795
 
758
- VALUE IO_Event_Selector_KQueue_io_write(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _length, VALUE _offset) {
796
+ VALUE IO_Event_Selector_KQueue_io_write(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _first, VALUE _second) {
759
797
  struct IO_Event_Selector_KQueue *selector = NULL;
760
798
  TypedData_Get_Struct(self, struct IO_Event_Selector_KQueue, &IO_Event_Selector_KQueue_Type, selector);
761
799
 
762
- size_t length = NUM2SIZET(_length);
763
- size_t offset = NUM2SIZET(_offset);
764
-
765
800
  const void *base;
766
801
  size_t size;
767
802
  rb_io_buffer_get_bytes_for_reading(buffer, &base, &size);
768
803
 
804
+ #if RUBY_FIBER_SCHEDULER_VERSION >= 4
805
+ size_t offset = NUM2SIZET(_first);
806
+ size_t length = NUM2SIZET(_second);
807
+
808
+ if (!IO_Event_Selector_valid_buffer_range(size, offset, length)) {
809
+ return rb_fiber_scheduler_io_result(-1, EINVAL);
810
+ } else if (length == 0) {
811
+ return rb_fiber_scheduler_io_result(0, 0);
812
+ }
813
+
814
+ base = (const char*)base + offset;
815
+ size = length;
816
+ #else
817
+ size_t length = NUM2SIZET(_first);
818
+ size_t offset = NUM2SIZET(_second);
819
+
769
820
  if (length > size) {
770
821
  rb_raise(rb_eRuntimeError, "Length exceeds size of buffer!");
771
822
  }
@@ -778,19 +829,24 @@ VALUE IO_Event_Selector_KQueue_io_write(VALUE self, VALUE fiber, VALUE io, VALUE
778
829
 
779
830
  base = (const char*)base + offset;
780
831
  size -= offset;
832
+ #endif
781
833
 
782
834
  int descriptor = IO_Event_Selector_io_descriptor(io);
783
835
 
784
836
  struct io_write_arguments io_write_arguments = {
837
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
785
838
  .self = self,
786
839
  .fiber = fiber,
787
840
  .io = io,
841
+ #endif
788
842
 
789
843
  .flags = IO_Event_Selector_nonblock_set(descriptor),
790
844
  .descriptor = descriptor,
791
845
  .base = base,
792
846
  .size = size,
847
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
793
848
  .length = length,
849
+ #endif
794
850
  };
795
851
 
796
852
  RB_OBJ_WRITTEN(self, Qundef, fiber);
@@ -798,6 +854,7 @@ VALUE IO_Event_Selector_KQueue_io_write(VALUE self, VALUE fiber, VALUE io, VALUE
798
854
  return rb_ensure(io_write_loop, (VALUE)&io_write_arguments, io_write_ensure, (VALUE)&io_write_arguments);
799
855
  }
800
856
 
857
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
801
858
  static VALUE IO_Event_Selector_KQueue_io_write_compatible(int argc, VALUE *argv, VALUE self)
802
859
  {
803
860
  rb_check_arity(argc, 4, 5);
@@ -810,6 +867,7 @@ static VALUE IO_Event_Selector_KQueue_io_write_compatible(int argc, VALUE *argv,
810
867
 
811
868
  return IO_Event_Selector_KQueue_io_write(self, argv[0], argv[1], argv[2], argv[3], _offset);
812
869
  }
870
+ #endif
813
871
 
814
872
  #endif
815
873
 
@@ -1125,8 +1183,13 @@ void Init_IO_Event_Selector_KQueue(VALUE IO_Event_Selector) {
1125
1183
  rb_define_method(IO_Event_Selector_KQueue, "io_wait", IO_Event_Selector_KQueue_io_wait, 3);
1126
1184
 
1127
1185
  #ifdef HAVE_RUBY_IO_BUFFER_H
1186
+ #if RUBY_FIBER_SCHEDULER_VERSION >= 4
1187
+ rb_define_method(IO_Event_Selector_KQueue, "io_read", IO_Event_Selector_KQueue_io_read, 5);
1188
+ rb_define_method(IO_Event_Selector_KQueue, "io_write", IO_Event_Selector_KQueue_io_write, 5);
1189
+ #else
1128
1190
  rb_define_method(IO_Event_Selector_KQueue, "io_read", IO_Event_Selector_KQueue_io_read_compatible, -1);
1129
1191
  rb_define_method(IO_Event_Selector_KQueue, "io_write", IO_Event_Selector_KQueue_io_write_compatible, -1);
1192
+ #endif
1130
1193
  #endif
1131
1194
 
1132
1195
  rb_define_method(IO_Event_Selector_KQueue, "process_wait", IO_Event_Selector_KQueue_process_wait, 3);
@@ -40,6 +40,10 @@ static inline int IO_Event_try_again(int error) {
40
40
  return error == EAGAIN || error == EWOULDBLOCK;
41
41
  }
42
42
 
43
+ static inline int IO_Event_Selector_valid_buffer_range(size_t size, size_t offset, size_t length) {
44
+ return offset <= size && length <= size - offset;
45
+ }
46
+
43
47
  // Enter a blocking region without the GVL. On Rubies without
44
48
  // `RB_NOGVL_PENDING_INTR_FAIL`, refresh pending-interrupt state immediately
45
49
  // before releasing the GVL so a signal cannot be stranded in the queue.
@@ -81,8 +81,26 @@ struct IO_Event_Selector_URing_Completion
81
81
  struct IO_Event_List list;
82
82
 
83
83
  struct IO_Event_Selector_URing_Waiting *waiting;
84
+ bool cancellation_pending;
84
85
  };
85
86
 
87
+ // Cancellation completions use the low bit to distinguish them from the
88
+ // completion of the operation being cancelled. Completion records are aligned,
89
+ // so the low bit is otherwise always clear.
90
+ #define IO_EVENT_SELECTOR_URING_CANCELLATION_TAG ((uint64_t)1)
91
+
92
+ static inline uint64_t
93
+ IO_Event_Selector_URing_Completion_cancellation_data(struct IO_Event_Selector_URing_Completion *completion)
94
+ {
95
+ return ((uint64_t)(uintptr_t)completion) | IO_EVENT_SELECTOR_URING_CANCELLATION_TAG;
96
+ }
97
+
98
+ static inline struct IO_Event_Selector_URing_Completion *
99
+ IO_Event_Selector_URing_Completion_from_cancellation_data(uint64_t data)
100
+ {
101
+ return (struct IO_Event_Selector_URing_Completion *)(uintptr_t)(data & ~IO_EVENT_SELECTOR_URING_CANCELLATION_TAG);
102
+ }
103
+
86
104
  static
87
105
  void IO_Event_Selector_URing_Completion_mark(void *_completion)
88
106
  {
@@ -188,6 +206,9 @@ struct IO_Event_Selector_URing_Completion * IO_Event_Selector_URing_Completion_a
188
206
 
189
207
  if (DEBUG_COMPLETION) fprintf(stderr, "IO_Event_Selector_URing_Completion_acquire(%p, limit=%ld)\n", (void*)completion, selector->completions.limit);
190
208
 
209
+ assert(completion->waiting == NULL);
210
+ assert(!completion->cancellation_pending);
211
+
191
212
  waiting->completion = completion;
192
213
  completion->waiting = waiting;
193
214
 
@@ -206,14 +227,41 @@ void IO_Event_Selector_URing_Completion_cancel(struct IO_Event_Selector_URing_Co
206
227
  }
207
228
 
208
229
  inline static
209
- void IO_Event_Selector_URing_Completion_release(struct IO_Event_Selector_URing *selector, struct IO_Event_Selector_URing_Completion *completion)
230
+ void IO_Event_Selector_URing_Completion_recycle(struct IO_Event_Selector_URing *selector, struct IO_Event_Selector_URing_Completion *completion)
210
231
  {
211
- if (DEBUG_COMPLETION) fprintf(stderr, "IO_Event_Selector_URing_Completion_release(%p)\n", (void*)completion);
232
+ assert(completion->waiting == NULL);
233
+ assert(!completion->cancellation_pending);
212
234
 
213
- IO_Event_Selector_URing_Completion_cancel(completion);
214
235
  IO_Event_List_prepend(&selector->free_list, &completion->list);
215
236
  }
216
237
 
238
+ inline static
239
+ void IO_Event_Selector_URing_Completion_complete(struct IO_Event_Selector_URing *selector, struct IO_Event_Selector_URing_Completion *completion)
240
+ {
241
+ if (DEBUG_COMPLETION) fprintf(stderr, "IO_Event_Selector_URing_Completion_complete(%p)\n", (void*)completion);
242
+
243
+ IO_Event_Selector_URing_Completion_cancel(completion);
244
+
245
+ // A cancellation SQE still refers to this completion record. Keep it out of
246
+ // the free list until that CQE has also been observed, avoiding ABA reuse.
247
+ if (!completion->cancellation_pending) {
248
+ IO_Event_Selector_URing_Completion_recycle(selector, completion);
249
+ }
250
+ }
251
+
252
+ inline static
253
+ void IO_Event_Selector_URing_Completion_cancellation_complete(struct IO_Event_Selector_URing *selector, struct IO_Event_Selector_URing_Completion *completion)
254
+ {
255
+ if (DEBUG_COMPLETION) fprintf(stderr, "IO_Event_Selector_URing_Completion_cancellation_complete(%p)\n", (void*)completion);
256
+
257
+ assert(completion->cancellation_pending);
258
+ completion->cancellation_pending = false;
259
+
260
+ if (completion->waiting == NULL) {
261
+ IO_Event_Selector_URing_Completion_recycle(selector, completion);
262
+ }
263
+ }
264
+
217
265
  inline static
218
266
  void IO_Event_Selector_URing_Waiting_cancel(struct IO_Event_Selector_URing_Waiting *waiting)
219
267
  {
@@ -234,6 +282,8 @@ void IO_Event_Selector_URing_Completion_initialize(void *element)
234
282
  struct IO_Event_Selector_URing_Completion *completion = element;
235
283
  IO_Event_List_initialize(&completion->list);
236
284
  completion->list.type = &IO_Event_Selector_URing_Completion_Type;
285
+ completion->waiting = NULL;
286
+ completion->cancellation_pending = false;
237
287
  }
238
288
 
239
289
  void IO_Event_Selector_URing_Completion_free(void *element)
@@ -500,6 +550,36 @@ struct io_uring_sqe * io_get_sqe(struct IO_Event_Selector_URing *selector) {
500
550
  return sqe;
501
551
  }
502
552
 
553
+ static
554
+ void IO_Event_Selector_URing_Completion_cancel_async(struct IO_Event_Selector_URing *selector, struct IO_Event_Selector_URing_Completion *completion)
555
+ {
556
+ if (completion->cancellation_pending) return;
557
+
558
+ completion->cancellation_pending = true;
559
+
560
+ struct io_uring_sqe *sqe = io_get_sqe(selector);
561
+ io_uring_prep_cancel(sqe, completion, 0);
562
+ io_uring_sqe_set_data64(sqe, IO_Event_Selector_URing_Completion_cancellation_data(completion));
563
+ io_uring_submit_pending(selector);
564
+ }
565
+
566
+ static
567
+ void IO_Event_Selector_URing_Waiting_cancel_and_wait(struct IO_Event_Selector_URing *selector, struct IO_Event_Selector_URing_Waiting *waiting)
568
+ {
569
+ if (waiting->completion) {
570
+ IO_Event_Selector_URing_Completion_cancel_async(selector, waiting->completion);
571
+
572
+ // The kernel may still be reading from or writing to the supplied buffer.
573
+ // Keep the C frame, buffer lock and completion record alive until the
574
+ // original operation CQE confirms that it can no longer access memory.
575
+ while (waiting->completion) {
576
+ IO_Event_Selector_loop_yield(&selector->backend);
577
+ }
578
+ }
579
+
580
+ IO_Event_Selector_URing_Waiting_cancel(waiting);
581
+ }
582
+
503
583
  #pragma mark - Process.wait
504
584
 
505
585
  #ifdef IO_EVENT_SELECTOR_URING_USE_WAITID
@@ -604,6 +684,10 @@ static
604
684
  VALUE process_wait_ensure(VALUE _arguments) {
605
685
  struct process_wait_arguments *arguments = (struct process_wait_arguments *)_arguments;
606
686
 
687
+ if (arguments->waiting->completion) {
688
+ IO_Event_Selector_URing_Completion_cancel_async(arguments->selector, arguments->waiting->completion);
689
+ }
690
+
607
691
  #ifndef IO_EVENT_SELECTOR_URING_USE_WAITID
608
692
  close(arguments->descriptor);
609
693
  #endif
@@ -714,13 +798,8 @@ static
714
798
  VALUE io_wait_ensure(VALUE _arguments) {
715
799
  struct io_wait_arguments *arguments = (struct io_wait_arguments *)_arguments;
716
800
 
717
- // If the operation is still in progress, cancel it:
718
801
  if (arguments->waiting->completion) {
719
- if (DEBUG) fprintf(stderr, "io_wait_ensure:io_uring_prep_cancel(waiting=%p, completion=%p)\n", (void*)arguments->waiting, (void*)arguments->waiting->completion);
720
- struct io_uring_sqe *sqe = io_get_sqe(arguments->selector);
721
- io_uring_prep_cancel(sqe, (void*)arguments->waiting->completion, 0);
722
- io_uring_sqe_set_data(sqe, NULL);
723
- io_uring_submit_now(arguments->selector);
802
+ IO_Event_Selector_URing_Completion_cancel_async(arguments->selector, arguments->waiting->completion);
724
803
  }
725
804
 
726
805
  IO_Event_Selector_URing_Waiting_cancel(arguments->waiting);
@@ -824,7 +903,7 @@ io_read_submit(VALUE _arguments)
824
903
  struct io_uring_sqe *sqe = io_get_sqe(selector);
825
904
  io_uring_prep_read(sqe, arguments->descriptor, arguments->buffer, arguments->length, arguments->offset);
826
905
  io_uring_sqe_set_data(sqe, arguments->waiting->completion);
827
- io_uring_submit_now(selector);
906
+ io_uring_submit_pending(selector);
828
907
 
829
908
  IO_Event_Selector_loop_yield(&selector->backend);
830
909
 
@@ -837,16 +916,7 @@ io_read_ensure(VALUE _arguments)
837
916
  struct io_read_arguments *arguments = (struct io_read_arguments *)_arguments;
838
917
  struct IO_Event_Selector_URing *selector = arguments->selector;
839
918
 
840
- // If the operation is still in progress, cancel it:
841
- if (arguments->waiting->completion) {
842
- if (DEBUG) fprintf(stderr, "io_read_ensure:io_uring_prep_cancel(waiting=%p, completion=%p)\n", (void*)arguments->waiting, (void*)arguments->waiting->completion);
843
- struct io_uring_sqe *sqe = io_get_sqe(selector);
844
- io_uring_prep_cancel(sqe, (void*)arguments->waiting->completion, 0);
845
- io_uring_sqe_set_data(sqe, NULL);
846
- io_uring_submit_now(selector);
847
- }
848
-
849
- IO_Event_Selector_URing_Waiting_cancel(arguments->waiting);
919
+ IO_Event_Selector_URing_Waiting_cancel_and_wait(selector, arguments->waiting);
850
920
 
851
921
  return Qnil;
852
922
  }
@@ -876,16 +946,86 @@ io_read(struct IO_Event_Selector_URing *selector, VALUE fiber, int descriptor, c
876
946
  );
877
947
  }
878
948
 
879
- VALUE IO_Event_Selector_URing_io_read(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _length, VALUE _offset) {
949
+ #if RUBY_FIBER_SCHEDULER_VERSION >= 4
950
+ struct io_read_locked_arguments {
951
+ struct IO_Event_Selector_URing *selector;
952
+ VALUE fiber;
953
+ VALUE io;
954
+ off_t from;
955
+ size_t offset;
956
+ size_t length;
957
+ bool positional;
958
+ };
959
+
960
+ static VALUE
961
+ io_read_locked(void *base, size_t size, VALUE _arguments)
962
+ {
963
+ struct io_read_locked_arguments *arguments = (struct io_read_locked_arguments *)_arguments;
964
+
965
+ if (!IO_Event_Selector_valid_buffer_range(size, arguments->offset, arguments->length)) {
966
+ return rb_fiber_scheduler_io_result(-1, EINVAL);
967
+ } else if (arguments->length == 0) {
968
+ return rb_fiber_scheduler_io_result(0, 0);
969
+ }
970
+
971
+ int descriptor = IO_Event_Selector_io_descriptor(arguments->io);
972
+ char *buffer = (char*)base + arguments->offset;
973
+
974
+ // Avoid the submission and suspension overhead when the operation can
975
+ // complete immediately. The descriptor must be non-blocking for this
976
+ // optimistic syscall; if it would block, restore its original mode and
977
+ // submit the operation to io_uring instead.
978
+ int flags = IO_Event_Selector_nonblock_set(descriptor);
979
+ ssize_t result;
980
+
981
+ if (arguments->positional) {
982
+ result = pread(descriptor, buffer, arguments->length, arguments->from);
983
+ } else {
984
+ result = read(descriptor, buffer, arguments->length);
985
+ }
986
+
987
+ int error = errno;
988
+ IO_Event_Selector_nonblock_restore(descriptor, flags);
989
+
990
+ if (result >= 0) {
991
+ return rb_fiber_scheduler_io_result(result, 0);
992
+ } else if (!IO_Event_try_again(error)) {
993
+ return rb_fiber_scheduler_io_result(-1, error);
994
+ }
995
+
996
+ off_t from = arguments->positional ? arguments->from : io_seekable(descriptor);
997
+
998
+ int completion = io_read(arguments->selector, arguments->fiber, descriptor, buffer, arguments->length, from);
999
+ if (completion < 0) {
1000
+ return rb_fiber_scheduler_io_result(-1, -completion);
1001
+ }
1002
+
1003
+ return rb_fiber_scheduler_io_result(completion, 0);
1004
+ }
1005
+ #endif
1006
+
1007
+ VALUE IO_Event_Selector_URing_io_read(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _first, VALUE _second) {
880
1008
  struct IO_Event_Selector_URing *selector = NULL;
881
1009
  TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
882
1010
 
1011
+ #if RUBY_FIBER_SCHEDULER_VERSION >= 4
1012
+ struct io_read_locked_arguments arguments = {
1013
+ .selector = selector,
1014
+ .fiber = fiber,
1015
+ .io = io,
1016
+ .offset = NUM2SIZET(_first),
1017
+ .length = NUM2SIZET(_second),
1018
+ .positional = false,
1019
+ };
1020
+
1021
+ return rb_io_buffer_locked_for_writing(buffer, io_read_locked, (VALUE)&arguments);
1022
+ #else
883
1023
  void *base;
884
1024
  size_t size;
885
1025
  rb_io_buffer_get_bytes_for_writing(buffer, &base, &size);
886
1026
 
887
- size_t length = NUM2SIZET(_length);
888
- size_t offset = NUM2SIZET(_offset);
1027
+ size_t length = NUM2SIZET(_first);
1028
+ size_t offset = NUM2SIZET(_second);
889
1029
  size_t total = 0;
890
1030
 
891
1031
  // Ensure offset is within the bounds of the buffer to avoid size_t underflow and out-of-bounds pointer arithmetic on (char *)base + offset.
@@ -894,10 +1034,10 @@ VALUE IO_Event_Selector_URing_io_read(VALUE self, VALUE fiber, VALUE io, VALUE b
894
1034
  } else if (offset == size) {
895
1035
  return rb_fiber_scheduler_io_result(0, 0);
896
1036
  }
1037
+ #endif
897
1038
 
898
1039
  int descriptor = IO_Event_Selector_io_descriptor(io);
899
1040
  off_t from = io_seekable(descriptor);
900
-
901
1041
  size_t maximum_size = size - offset;
902
1042
 
903
1043
  // Are we performing a non-blocking read?
@@ -933,8 +1073,10 @@ VALUE IO_Event_Selector_URing_io_read(VALUE self, VALUE fiber, VALUE io, VALUE b
933
1073
  }
934
1074
 
935
1075
  return rb_fiber_scheduler_io_result(total, 0);
1076
+ #endif
936
1077
  }
937
1078
 
1079
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
938
1080
  static VALUE IO_Event_Selector_URing_io_read_compatible(int argc, VALUE *argv, VALUE self)
939
1081
  {
940
1082
  rb_check_arity(argc, 4, 5);
@@ -947,19 +1089,32 @@ static VALUE IO_Event_Selector_URing_io_read_compatible(int argc, VALUE *argv, V
947
1089
 
948
1090
  return IO_Event_Selector_URing_io_read(self, argv[0], argv[1], argv[2], argv[3], _offset);
949
1091
  }
1092
+ #endif
950
1093
 
951
- VALUE IO_Event_Selector_URing_io_pread(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _from, VALUE _length, VALUE _offset) {
1094
+ VALUE IO_Event_Selector_URing_io_pread(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _from, VALUE _first, VALUE _second) {
952
1095
  struct IO_Event_Selector_URing *selector = NULL;
953
1096
  TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
954
1097
 
1098
+ #if RUBY_FIBER_SCHEDULER_VERSION >= 4
1099
+ struct io_read_locked_arguments arguments = {
1100
+ .selector = selector,
1101
+ .fiber = fiber,
1102
+ .io = io,
1103
+ .from = NUM2OFFT(_from),
1104
+ .offset = NUM2SIZET(_first),
1105
+ .length = NUM2SIZET(_second),
1106
+ .positional = true,
1107
+ };
1108
+
1109
+ return rb_io_buffer_locked_for_writing(buffer, io_read_locked, (VALUE)&arguments);
1110
+ #else
955
1111
  void *base;
956
1112
  size_t size;
957
1113
  rb_io_buffer_get_bytes_for_writing(buffer, &base, &size);
958
1114
 
959
- size_t length = NUM2SIZET(_length);
960
- size_t offset = NUM2SIZET(_offset);
1115
+ size_t length = NUM2SIZET(_first);
1116
+ size_t offset = NUM2SIZET(_second);
961
1117
  size_t total = 0;
962
- off_t from = NUM2OFFT(_from);
963
1118
 
964
1119
  // Ensure offset is within the bounds of the buffer to avoid size_t underflow and out-of-bounds pointer arithmetic on (char *)base + offset.
965
1120
  if (offset > size) {
@@ -967,9 +1122,9 @@ VALUE IO_Event_Selector_URing_io_pread(VALUE self, VALUE fiber, VALUE io, VALUE
967
1122
  } else if (offset == size) {
968
1123
  return rb_fiber_scheduler_io_result(0, 0);
969
1124
  }
970
-
1125
+ #endif
1126
+ off_t from = NUM2OFFT(_from);
971
1127
  int descriptor = IO_Event_Selector_io_descriptor(io);
972
-
973
1128
  size_t maximum_size = size - offset;
974
1129
  while (maximum_size) {
975
1130
  int result = io_read(selector, fiber, descriptor, (char*)base+offset, maximum_size, from);
@@ -992,6 +1147,7 @@ VALUE IO_Event_Selector_URing_io_pread(VALUE self, VALUE fiber, VALUE io, VALUE
992
1147
  }
993
1148
 
994
1149
  return rb_fiber_scheduler_io_result(total, 0);
1150
+ #endif
995
1151
  }
996
1152
 
997
1153
  #pragma mark - IO#write
@@ -1029,16 +1185,7 @@ io_write_ensure(VALUE _argument)
1029
1185
  struct io_write_arguments *arguments = (struct io_write_arguments*)_argument;
1030
1186
  struct IO_Event_Selector_URing *selector = arguments->selector;
1031
1187
 
1032
- // If the operation is still in progress, cancel it:
1033
- if (arguments->waiting->completion) {
1034
- if (DEBUG) fprintf(stderr, "io_write_ensure:io_uring_prep_cancel(waiting=%p, completion=%p)\n", (void*)arguments->waiting, (void*)arguments->waiting->completion);
1035
- struct io_uring_sqe *sqe = io_get_sqe(selector);
1036
- io_uring_prep_cancel(sqe, (void*)arguments->waiting->completion, 0);
1037
- io_uring_sqe_set_data(sqe, NULL);
1038
- io_uring_submit_now(selector);
1039
- }
1040
-
1041
- IO_Event_Selector_URing_Waiting_cancel(arguments->waiting);
1188
+ IO_Event_Selector_URing_Waiting_cancel_and_wait(selector, arguments->waiting);
1042
1189
 
1043
1190
  return Qnil;
1044
1191
  }
@@ -1068,16 +1215,86 @@ io_write(struct IO_Event_Selector_URing *selector, VALUE fiber, int descriptor,
1068
1215
  );
1069
1216
  }
1070
1217
 
1071
- VALUE IO_Event_Selector_URing_io_write(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _length, VALUE _offset) {
1218
+ #if RUBY_FIBER_SCHEDULER_VERSION >= 4
1219
+ struct io_write_locked_arguments {
1220
+ struct IO_Event_Selector_URing *selector;
1221
+ VALUE fiber;
1222
+ VALUE io;
1223
+ off_t from;
1224
+ size_t offset;
1225
+ size_t length;
1226
+ bool positional;
1227
+ };
1228
+
1229
+ static VALUE
1230
+ io_write_locked(const void *base, size_t size, VALUE _arguments)
1231
+ {
1232
+ struct io_write_locked_arguments *arguments = (struct io_write_locked_arguments *)_arguments;
1233
+
1234
+ if (!IO_Event_Selector_valid_buffer_range(size, arguments->offset, arguments->length)) {
1235
+ return rb_fiber_scheduler_io_result(-1, EINVAL);
1236
+ } else if (arguments->length == 0) {
1237
+ return rb_fiber_scheduler_io_result(0, 0);
1238
+ }
1239
+
1240
+ int descriptor = IO_Event_Selector_io_descriptor(arguments->io);
1241
+ const char *buffer = (const char*)base + arguments->offset;
1242
+
1243
+ // Avoid the submission and suspension overhead when the operation can
1244
+ // complete immediately. The descriptor must be non-blocking for this
1245
+ // optimistic syscall; if it would block, restore its original mode and
1246
+ // submit the operation to io_uring instead.
1247
+ int flags = IO_Event_Selector_nonblock_set(descriptor);
1248
+ ssize_t result;
1249
+
1250
+ if (arguments->positional) {
1251
+ result = pwrite(descriptor, buffer, arguments->length, arguments->from);
1252
+ } else {
1253
+ result = write(descriptor, buffer, arguments->length);
1254
+ }
1255
+
1256
+ int error = errno;
1257
+ IO_Event_Selector_nonblock_restore(descriptor, flags);
1258
+
1259
+ if (result >= 0) {
1260
+ return rb_fiber_scheduler_io_result(result, 0);
1261
+ } else if (!IO_Event_try_again(error)) {
1262
+ return rb_fiber_scheduler_io_result(-1, error);
1263
+ }
1264
+
1265
+ off_t from = arguments->positional ? arguments->from : io_seekable(descriptor);
1266
+
1267
+ int completion = io_write(arguments->selector, arguments->fiber, descriptor, (char*)buffer, arguments->length, from);
1268
+ if (completion < 0) {
1269
+ return rb_fiber_scheduler_io_result(-1, -completion);
1270
+ }
1271
+
1272
+ return rb_fiber_scheduler_io_result(completion, 0);
1273
+ }
1274
+ #endif
1275
+
1276
+ VALUE IO_Event_Selector_URing_io_write(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _first, VALUE _second) {
1072
1277
  struct IO_Event_Selector_URing *selector = NULL;
1073
1278
  TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
1074
1279
 
1280
+ #if RUBY_FIBER_SCHEDULER_VERSION >= 4
1281
+ struct io_write_locked_arguments arguments = {
1282
+ .selector = selector,
1283
+ .fiber = fiber,
1284
+ .io = io,
1285
+ .offset = NUM2SIZET(_first),
1286
+ .length = NUM2SIZET(_second),
1287
+ .positional = false,
1288
+ };
1289
+
1290
+ return rb_io_buffer_locked_for_reading(buffer, io_write_locked, (VALUE)&arguments);
1291
+ #else
1075
1292
  const void *base;
1076
1293
  size_t size;
1077
1294
  rb_io_buffer_get_bytes_for_reading(buffer, &base, &size);
1078
1295
 
1079
- size_t length = NUM2SIZET(_length);
1080
- size_t offset = NUM2SIZET(_offset);
1296
+ size_t length = NUM2SIZET(_first);
1297
+ size_t offset = NUM2SIZET(_second);
1081
1298
  size_t total = 0;
1082
1299
 
1083
1300
  if (length > size) {
@@ -1090,10 +1307,10 @@ VALUE IO_Event_Selector_URing_io_write(VALUE self, VALUE fiber, VALUE io, VALUE
1090
1307
  } else if (offset == size) {
1091
1308
  return rb_fiber_scheduler_io_result(0, 0);
1092
1309
  }
1310
+ #endif
1093
1311
 
1094
1312
  int descriptor = IO_Event_Selector_io_descriptor(io);
1095
1313
  off_t from = io_seekable(descriptor);
1096
-
1097
1314
  size_t maximum_size = size - offset;
1098
1315
  while (maximum_size) {
1099
1316
  int result = io_write(selector, fiber, descriptor, (char*)base+offset, maximum_size, from);
@@ -1115,8 +1332,10 @@ VALUE IO_Event_Selector_URing_io_write(VALUE self, VALUE fiber, VALUE io, VALUE
1115
1332
  }
1116
1333
 
1117
1334
  return rb_fiber_scheduler_io_result(total, 0);
1335
+ #endif
1118
1336
  }
1119
1337
 
1338
+ #if RUBY_FIBER_SCHEDULER_VERSION < 4
1120
1339
  static VALUE IO_Event_Selector_URing_io_write_compatible(int argc, VALUE *argv, VALUE self)
1121
1340
  {
1122
1341
  rb_check_arity(argc, 4, 5);
@@ -1129,19 +1348,32 @@ static VALUE IO_Event_Selector_URing_io_write_compatible(int argc, VALUE *argv,
1129
1348
 
1130
1349
  return IO_Event_Selector_URing_io_write(self, argv[0], argv[1], argv[2], argv[3], _offset);
1131
1350
  }
1351
+ #endif
1132
1352
 
1133
- VALUE IO_Event_Selector_URing_io_pwrite(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _from, VALUE _length, VALUE _offset) {
1353
+ VALUE IO_Event_Selector_URing_io_pwrite(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _from, VALUE _first, VALUE _second) {
1134
1354
  struct IO_Event_Selector_URing *selector = NULL;
1135
1355
  TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
1136
1356
 
1357
+ #if RUBY_FIBER_SCHEDULER_VERSION >= 4
1358
+ struct io_write_locked_arguments arguments = {
1359
+ .selector = selector,
1360
+ .fiber = fiber,
1361
+ .io = io,
1362
+ .from = NUM2OFFT(_from),
1363
+ .offset = NUM2SIZET(_first),
1364
+ .length = NUM2SIZET(_second),
1365
+ .positional = true,
1366
+ };
1367
+
1368
+ return rb_io_buffer_locked_for_reading(buffer, io_write_locked, (VALUE)&arguments);
1369
+ #else
1137
1370
  const void *base;
1138
1371
  size_t size;
1139
1372
  rb_io_buffer_get_bytes_for_reading(buffer, &base, &size);
1140
1373
 
1141
- size_t length = NUM2SIZET(_length);
1142
- size_t offset = NUM2SIZET(_offset);
1374
+ size_t length = NUM2SIZET(_first);
1375
+ size_t offset = NUM2SIZET(_second);
1143
1376
  size_t total = 0;
1144
- off_t from = NUM2OFFT(_from);
1145
1377
 
1146
1378
  if (length > size) {
1147
1379
  rb_raise(rb_eRuntimeError, "Length exceeds size of buffer!");
@@ -1153,9 +1385,9 @@ VALUE IO_Event_Selector_URing_io_pwrite(VALUE self, VALUE fiber, VALUE io, VALUE
1153
1385
  } else if (offset == size) {
1154
1386
  return rb_fiber_scheduler_io_result(0, 0);
1155
1387
  }
1156
-
1388
+ #endif
1389
+ off_t from = NUM2OFFT(_from);
1157
1390
  int descriptor = IO_Event_Selector_io_descriptor(io);
1158
-
1159
1391
  size_t maximum_size = size - offset;
1160
1392
  while (maximum_size) {
1161
1393
  int result = io_write(selector, fiber, descriptor, (char*)base+offset, maximum_size, from);
@@ -1178,6 +1410,7 @@ VALUE IO_Event_Selector_URing_io_pwrite(VALUE self, VALUE fiber, VALUE io, VALUE
1178
1410
  }
1179
1411
 
1180
1412
  return rb_fiber_scheduler_io_result(total, 0);
1413
+ #endif
1181
1414
  }
1182
1415
 
1183
1416
  #endif
@@ -1329,6 +1562,13 @@ unsigned select_process_completions(struct IO_Event_Selector_URing *selector) {
1329
1562
  continue;
1330
1563
  }
1331
1564
 
1565
+ if (cqe->user_data & IO_EVENT_SELECTOR_URING_CANCELLATION_TAG) {
1566
+ struct IO_Event_Selector_URing_Completion *completion = IO_Event_Selector_URing_Completion_from_cancellation_data(cqe->user_data);
1567
+ IO_Event_Selector_URing_Completion_cancellation_complete(selector, completion);
1568
+ io_uring_cq_advance(ring, 1);
1569
+ continue;
1570
+ }
1571
+
1332
1572
  struct IO_Event_Selector_URing_Completion *completion = (void*)cqe->user_data;
1333
1573
  struct IO_Event_Selector_URing_Waiting *waiting = completion->waiting;
1334
1574
 
@@ -1343,13 +1583,11 @@ unsigned select_process_completions(struct IO_Event_Selector_URing *selector) {
1343
1583
 
1344
1584
  VALUE fiber = 0;
1345
1585
  if (waiting && waiting->fiber) {
1346
- assert(waiting->result != -ECANCELED);
1347
-
1348
1586
  fiber = waiting->fiber;
1349
1587
  }
1350
1588
 
1351
1589
  // This marks the waiting operation as "complete":
1352
- IO_Event_Selector_URing_Completion_release(selector, completion);
1590
+ IO_Event_Selector_URing_Completion_complete(selector, completion);
1353
1591
 
1354
1592
  if (fiber) {
1355
1593
  IO_Event_Selector_loop_resume(&selector->backend, fiber, 0, NULL);
@@ -1511,8 +1749,13 @@ void Init_IO_Event_Selector_URing(VALUE IO_Event_Selector) {
1511
1749
  rb_define_method(IO_Event_Selector_URing, "io_wait", IO_Event_Selector_URing_io_wait, 3);
1512
1750
 
1513
1751
  #ifdef HAVE_RUBY_IO_BUFFER_H
1752
+ #if RUBY_FIBER_SCHEDULER_VERSION >= 4
1753
+ rb_define_method(IO_Event_Selector_URing, "io_read", IO_Event_Selector_URing_io_read, 5);
1754
+ rb_define_method(IO_Event_Selector_URing, "io_write", IO_Event_Selector_URing_io_write, 5);
1755
+ #else
1514
1756
  rb_define_method(IO_Event_Selector_URing, "io_read", IO_Event_Selector_URing_io_read_compatible, -1);
1515
1757
  rb_define_method(IO_Event_Selector_URing, "io_write", IO_Event_Selector_URing_io_write_compatible, -1);
1758
+ #endif
1516
1759
  rb_define_method(IO_Event_Selector_URing, "io_pread", IO_Event_Selector_URing_io_pread, 6);
1517
1760
  rb_define_method(IO_Event_Selector_URing, "io_pwrite", IO_Event_Selector_URing_io_pwrite, 6);
1518
1761
  #endif
@@ -177,15 +177,15 @@ module IO::Event
177
177
  end
178
178
 
179
179
  # Read from the given IO, forwarded to the underlying selector.
180
- def io_read(fiber, io, buffer, length, offset = 0)
181
- log("Reading from IO #{io.inspect} with buffer #{buffer}; length #{length} offset #{offset}")
182
- @selector.io_read(fiber, io, buffer, length, offset)
180
+ def io_read(fiber, io, buffer, *arguments)
181
+ log("Reading from IO #{io.inspect} with buffer #{buffer}; arguments #{arguments.inspect}")
182
+ @selector.io_read(fiber, io, buffer, *arguments)
183
183
  end
184
184
 
185
185
  # Write to the given IO, forwarded to the underlying selector.
186
- def io_write(fiber, io, buffer, length, offset = 0)
187
- log("Writing to IO #{io.inspect} with buffer #{buffer}; length #{length} offset #{offset}")
188
- @selector.io_write(fiber, io, buffer, length, offset)
186
+ def io_write(fiber, io, buffer, *arguments)
187
+ log("Writing to IO #{io.inspect} with buffer #{buffer}; arguments #{arguments.inspect}")
188
+ @selector.io_write(fiber, io, buffer, *arguments)
189
189
  end
190
190
 
191
191
  # Forward the given method to the underlying selector.
@@ -197,78 +197,112 @@ module IO::Event
197
197
  errno == EAGAIN or errno == EWOULDBLOCK
198
198
  end
199
199
 
200
- # Read from the given IO to the buffer.
201
- #
202
- # @parameter length [Integer] The minimum number of bytes to read.
203
- # @parameter offset [Integer] The offset into the buffer to read to.
204
- def io_read(fiber, io, buffer, length, offset = 0)
205
- # Ensure offset is within the bounds of the buffer to avoid ArgumentError
206
- if offset > buffer.size
207
- return -Errno::EINVAL::Errno
208
- elsif offset == buffer.size
209
- return 0
200
+ if defined?(IO::Buffer::VERSION) && IO::Buffer::VERSION >= 3
201
+ # Read at most `length` bytes from the given IO to the buffer in one operation.
202
+ #
203
+ # @parameter offset [Integer] The offset into the buffer to read to.
204
+ # @parameter length [Integer] The maximum number of bytes to read.
205
+ def io_read(fiber, io, buffer, offset, length)
206
+ if offset > buffer.size || length > buffer.size - offset
207
+ return -Errno::EINVAL::Errno
208
+ elsif length == 0
209
+ return 0
210
+ end
211
+
212
+ Selector.nonblock(io) do
213
+ return Fiber.blocking{buffer.read(io, offset, length)}
214
+ end
210
215
  end
211
216
 
212
- total = 0
213
-
214
- Selector.nonblock(io) do
215
- while true
216
- result = Fiber.blocking{buffer.read(io, 0, offset)}
217
-
218
- if result < 0
219
- if length > 0 and again?(result)
220
- self.io_wait(fiber, io, IO::READABLE)
217
+ # Write at most `length` bytes to the given IO from the buffer in one operation.
218
+ #
219
+ # @parameter offset [Integer] The offset into the buffer to write from.
220
+ # @parameter length [Integer] The maximum number of bytes to write.
221
+ def io_write(fiber, io, buffer, offset, length)
222
+ if offset > buffer.size || length > buffer.size - offset
223
+ return -Errno::EINVAL::Errno
224
+ elsif length == 0
225
+ return 0
226
+ end
227
+
228
+ Selector.nonblock(io) do
229
+ return Fiber.blocking{buffer.write(io, offset, length)}
230
+ end
231
+ end
232
+ else
233
+ # Read from the given IO to the buffer.
234
+ #
235
+ # @parameter length [Integer] The minimum number of bytes to read.
236
+ # @parameter offset [Integer] The offset into the buffer to read to.
237
+ def io_read(fiber, io, buffer, length, offset = 0)
238
+ # Ensure offset is within the bounds of the buffer to avoid `ArgumentError`:
239
+ if offset > buffer.size
240
+ return -Errno::EINVAL::Errno
241
+ elsif offset == buffer.size
242
+ return 0
243
+ end
244
+
245
+ total = 0
246
+
247
+ Selector.nonblock(io) do
248
+ while true
249
+ result = Fiber.blocking{buffer.read(io, 0, offset)}
250
+
251
+ if result < 0
252
+ if length > 0 and again?(result)
253
+ self.io_wait(fiber, io, IO::READABLE)
254
+ else
255
+ return result
256
+ end
257
+ elsif result == 0
258
+ break
221
259
  else
222
- return result
260
+ total += result
261
+ break if total >= length
262
+ offset += result
223
263
  end
224
- elsif result == 0
225
- break
226
- else
227
- total += result
228
- break if total >= length
229
- offset += result
230
264
  end
231
265
  end
266
+
267
+ return total
232
268
  end
233
269
 
234
- return total
235
- end
236
-
237
- # Write to the given IO from the buffer.
238
- #
239
- # @parameter length [Integer] The minimum number of bytes to write.
240
- # @parameter offset [Integer] The offset into the buffer to write from.
241
- def io_write(fiber, io, buffer, length, offset = 0)
242
- # Ensure offset is within the bounds of the buffer to avoid ArgumentError
243
- if offset > buffer.size
244
- return -Errno::EINVAL::Errno
245
- elsif offset == buffer.size
246
- return 0
247
- end
248
-
249
- total = 0
250
-
251
- Selector.nonblock(io) do
252
- while true
253
- result = Fiber.blocking{buffer.write(io, 0, offset)}
254
-
255
- if result < 0
256
- if length > 0 and again?(result)
257
- self.io_wait(fiber, io, IO::WRITABLE)
270
+ # Write to the given IO from the buffer.
271
+ #
272
+ # @parameter length [Integer] The minimum number of bytes to write.
273
+ # @parameter offset [Integer] The offset into the buffer to write from.
274
+ def io_write(fiber, io, buffer, length, offset = 0)
275
+ # Ensure offset is within the bounds of the buffer to avoid `ArgumentError`:
276
+ if offset > buffer.size
277
+ return -Errno::EINVAL::Errno
278
+ elsif offset == buffer.size
279
+ return 0
280
+ end
281
+
282
+ total = 0
283
+
284
+ Selector.nonblock(io) do
285
+ while true
286
+ result = Fiber.blocking{buffer.write(io, 0, offset)}
287
+
288
+ if result < 0
289
+ if length > 0 and again?(result)
290
+ self.io_wait(fiber, io, IO::WRITABLE)
291
+ else
292
+ return result
293
+ end
294
+ elsif result == 0
295
+ break result
258
296
  else
259
- return result
297
+ total += result
298
+ break if total >= length
299
+ offset += result
260
300
  end
261
- elsif result == 0
262
- break result
263
- else
264
- total += result
265
- break if total >= length
266
- offset += result
267
301
  end
268
302
  end
303
+
304
+ return total
269
305
  end
270
-
271
- return total
272
306
  end
273
307
 
274
308
  # Wait for a process to change state.
@@ -7,6 +7,6 @@
7
7
  class IO
8
8
  # @namespace
9
9
  module Event
10
- VERSION = "1.19.5"
10
+ VERSION = "1.20.0"
11
11
  end
12
12
  end
data/readme.md CHANGED
@@ -18,6 +18,10 @@ Please see the [project documentation](https://socketry.github.io/io-event/) for
18
18
 
19
19
  Please see the [project releases](https://socketry.github.io/io-event/releases/index) for all releases.
20
20
 
21
+ ### v1.20.0
22
+
23
+ - Add compatibility with Ruby 4.1's fiber scheduler interface version 4. Buffered IO operations now use `(offset, length)`, perform a single transfer of at most `length` bytes, return short transfers directly, and report `-EAGAIN` without waiting. Earlier Ruby versions retain the existing minimum-progress behavior.
24
+
21
25
  ### v1.19.5
22
26
 
23
27
  - Preserve the original exception or non-local control flow when `IO::Event::WorkerPool` cancellation interrupts a blocked fiber, while still cancelling and draining the in-flight blocking operation before returning control to Ruby.
@@ -55,10 +59,6 @@ Please see the [project releases](https://socketry.github.io/io-event/releases/i
55
59
 
56
60
  - Correctly implement `Interrupt#signal` so that it is robust enough to be called by `Scheduler#unblock`.
57
61
 
58
- ### v1.16.3
59
-
60
- - Handle `IOError` raised while shutting down the pure Ruby interrupt pipe, so `IO::Event::Interrupt#close` does not leak expected shutdown errors from the interrupt fiber.
61
-
62
62
  ## Contributing
63
63
 
64
64
  We welcome contributions to this project.
data/releases.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Releases
2
2
 
3
+ ## v1.20.0
4
+
5
+ - Add compatibility with Ruby 4.1's fiber scheduler interface version 4. Buffered IO operations now use `(offset, length)`, perform a single transfer of at most `length` bytes, return short transfers directly, and report `-EAGAIN` without waiting. Earlier Ruby versions retain the existing minimum-progress behavior.
6
+
3
7
  ## v1.19.5
4
8
 
5
9
  - Preserve the original exception or non-local control flow when `IO::Event::WorkerPool` cancellation interrupts a blocked fiber, while still cancelling and draining the in-flight blocking operation before returning control to Ruby.
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: io-event
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.19.5
4
+ version: 1.20.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
metadata.gz.sig CHANGED
Binary file