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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/context/getting-started.md +3 -3
- data/ext/io/event/selector/epoll.c +71 -12
- data/ext/io/event/selector/kqueue.c +71 -8
- data/ext/io/event/selector/selector.h +4 -0
- data/ext/io/event/selector/uring.c +296 -53
- data/lib/io/event/debug/selector.rb +6 -6
- data/lib/io/event/selector/select.rb +93 -59
- data/lib/io/event/version.rb +1 -1
- data/readme.md +4 -4
- data/releases.md +4 -0
- data.tar.gz.sig +0 -0
- metadata +1 -1
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 648484dee2c6698568b24073b4fcf90a32629dcfa2119c450df16cae77f52c49
|
|
4
|
+
data.tar.gz: 895241950306f5dbc0f1c0b31f6981ec78096c6f90160c018a56dccc6102b799
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ff589133ec2d19d400c0c80daf0dc1cbf448005d26ca2c2df1d893c08371387cc12d37e8d1133379b17ed599cbe947197e0765e59c9c30c603802794283d9226
|
|
7
|
+
data.tar.gz: ec3c7a5d210053cf1e66d22eb4673c208184a044cb3b55404f34d645fcca0267711fe36ee12b7e8a7ef360d5ff6ed70a3da5ad0d64765406ec52ea2295119c40
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/context/getting-started.md
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
230
|
+
void IO_Event_Selector_URing_Completion_recycle(struct IO_Event_Selector_URing *selector, struct IO_Event_Selector_URing_Completion *completion)
|
|
210
231
|
{
|
|
211
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
888
|
-
size_t offset = NUM2SIZET(
|
|
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
|
|
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(
|
|
960
|
-
size_t offset = NUM2SIZET(
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
1080
|
-
size_t offset = NUM2SIZET(
|
|
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
|
|
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(
|
|
1142
|
-
size_t offset = NUM2SIZET(
|
|
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
|
-
|
|
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,
|
|
181
|
-
log("Reading from IO #{io.inspect} with buffer #{buffer};
|
|
182
|
-
@selector.io_read(fiber, io, buffer,
|
|
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,
|
|
187
|
-
log("Writing to IO #{io.inspect} with buffer #{buffer};
|
|
188
|
-
@selector.io_write(fiber, io, buffer,
|
|
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
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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
|
-
|
|
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
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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
|
-
|
|
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.
|
data/lib/io/event/version.rb
CHANGED
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
metadata.gz.sig
CHANGED
|
Binary file
|