io-event 1.6.5 → 1.7.5
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/ext/extconf.rb +25 -12
- data/ext/io/event/event.c +2 -8
- data/ext/io/event/interrupt.c +7 -0
- data/ext/io/event/selector/array.h +52 -18
- data/ext/io/event/selector/epoll.c +26 -11
- data/ext/io/event/selector/kqueue.c +20 -10
- data/ext/io/event/selector/list.h +19 -1
- data/ext/io/event/selector/selector.c +24 -2
- data/ext/io/event/selector/selector.h +11 -8
- data/ext/io/event/selector/uring.c +114 -14
- data/lib/io/event/debug/selector.rb +44 -4
- data/lib/io/event/interrupt.rb +2 -2
- data/lib/io/event/priority_heap.rb +13 -14
- data/lib/io/event/selector/nonblock.rb +6 -2
- data/lib/io/event/selector/select.rb +25 -5
- data/lib/io/event/selector.rb +15 -5
- data/lib/io/event/support.rb +23 -2
- data/lib/io/event/timers.rb +42 -4
- data/lib/io/event/version.rb +3 -1
- data/lib/io/event.rb +5 -5
- data/license.md +3 -0
- data/readme.md +14 -4
- data/releases.md +5 -0
- data.tar.gz.sig +0 -0
- metadata +8 -4
- metadata.gz.sig +0 -0
|
@@ -35,10 +35,9 @@
|
|
|
35
35
|
enum {
|
|
36
36
|
DEBUG = 0,
|
|
37
37
|
DEBUG_COMPLETION = 0,
|
|
38
|
+
DEBUG_IO_READ = 1,
|
|
38
39
|
};
|
|
39
40
|
|
|
40
|
-
static VALUE IO_Event_Selector_URing = Qnil;
|
|
41
|
-
|
|
42
41
|
enum {URING_ENTRIES = 64};
|
|
43
42
|
|
|
44
43
|
#pragma mark - Data Type
|
|
@@ -140,6 +139,7 @@ size_t IO_Event_Selector_URing_Type_size(const void *_selector)
|
|
|
140
139
|
|
|
141
140
|
return sizeof(struct IO_Event_Selector_URing)
|
|
142
141
|
+ IO_Event_Array_memory_size(&selector->completions)
|
|
142
|
+
+ IO_Event_List_memory_size(&selector->free_list)
|
|
143
143
|
;
|
|
144
144
|
}
|
|
145
145
|
|
|
@@ -152,7 +152,7 @@ static const rb_data_type_t IO_Event_Selector_URing_Type = {
|
|
|
152
152
|
.dsize = IO_Event_Selector_URing_Type_size,
|
|
153
153
|
},
|
|
154
154
|
.data = NULL,
|
|
155
|
-
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
|
155
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
|
|
156
156
|
};
|
|
157
157
|
|
|
158
158
|
inline static
|
|
@@ -228,7 +228,7 @@ VALUE IO_Event_Selector_URing_allocate(VALUE self) {
|
|
|
228
228
|
struct IO_Event_Selector_URing *selector = NULL;
|
|
229
229
|
VALUE instance = TypedData_Make_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
|
|
230
230
|
|
|
231
|
-
IO_Event_Selector_initialize(&selector->backend, Qnil);
|
|
231
|
+
IO_Event_Selector_initialize(&selector->backend, self, Qnil);
|
|
232
232
|
selector->ring.ring_fd = -1;
|
|
233
233
|
|
|
234
234
|
selector->pending = 0;
|
|
@@ -238,7 +238,10 @@ VALUE IO_Event_Selector_URing_allocate(VALUE self) {
|
|
|
238
238
|
|
|
239
239
|
selector->completions.element_initialize = IO_Event_Selector_URing_Completion_initialize;
|
|
240
240
|
selector->completions.element_free = IO_Event_Selector_URing_Completion_free;
|
|
241
|
-
IO_Event_Array_allocate(&selector->completions,
|
|
241
|
+
int result = IO_Event_Array_allocate(&selector->completions, IO_EVENT_ARRAY_DEFAULT_COUNT, sizeof(struct IO_Event_Selector_URing_Completion));
|
|
242
|
+
if (result < 0) {
|
|
243
|
+
rb_sys_fail("IO_Event_Selector_URing_allocate:IO_Event_Array_allocate");
|
|
244
|
+
}
|
|
242
245
|
|
|
243
246
|
return instance;
|
|
244
247
|
}
|
|
@@ -249,7 +252,7 @@ VALUE IO_Event_Selector_URing_initialize(VALUE self, VALUE loop) {
|
|
|
249
252
|
struct IO_Event_Selector_URing *selector = NULL;
|
|
250
253
|
TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
|
|
251
254
|
|
|
252
|
-
IO_Event_Selector_initialize(&selector->backend, loop);
|
|
255
|
+
IO_Event_Selector_initialize(&selector->backend, self, loop);
|
|
253
256
|
int result = io_uring_queue_init(URING_ENTRIES, &selector->ring, 0);
|
|
254
257
|
|
|
255
258
|
if (result < 0) {
|
|
@@ -484,6 +487,8 @@ VALUE IO_Event_Selector_URing_process_wait(VALUE self, VALUE fiber, VALUE _pid,
|
|
|
484
487
|
.fiber = fiber,
|
|
485
488
|
};
|
|
486
489
|
|
|
490
|
+
RB_OBJ_WRITTEN(self, Qundef, fiber);
|
|
491
|
+
|
|
487
492
|
struct IO_Event_Selector_URing_Completion *completion = IO_Event_Selector_URing_Completion_acquire(selector, &waiting);
|
|
488
493
|
|
|
489
494
|
struct process_wait_arguments process_wait_arguments = {
|
|
@@ -587,6 +592,8 @@ VALUE IO_Event_Selector_URing_io_wait(VALUE self, VALUE fiber, VALUE io, VALUE e
|
|
|
587
592
|
.fiber = fiber,
|
|
588
593
|
};
|
|
589
594
|
|
|
595
|
+
RB_OBJ_WRITTEN(self, Qundef, fiber);
|
|
596
|
+
|
|
590
597
|
struct IO_Event_Selector_URing_Completion *completion = IO_Event_Selector_URing_Completion_acquire(selector, &waiting);
|
|
591
598
|
|
|
592
599
|
struct io_uring_sqe *sqe = io_get_sqe(selector);
|
|
@@ -630,6 +637,7 @@ struct io_read_arguments {
|
|
|
630
637
|
struct IO_Event_Selector_URing *selector;
|
|
631
638
|
struct IO_Event_Selector_URing_Waiting *waiting;
|
|
632
639
|
int descriptor;
|
|
640
|
+
off_t offset;
|
|
633
641
|
char *buffer;
|
|
634
642
|
size_t length;
|
|
635
643
|
};
|
|
@@ -643,7 +651,7 @@ io_read_submit(VALUE _arguments)
|
|
|
643
651
|
if (DEBUG) fprintf(stderr, "io_read_submit:io_uring_prep_read(waiting=%p, completion=%p, descriptor=%d, buffer=%p, length=%ld)\n", (void*)arguments->waiting, (void*)arguments->waiting->completion, arguments->descriptor, arguments->buffer, arguments->length);
|
|
644
652
|
|
|
645
653
|
struct io_uring_sqe *sqe = io_get_sqe(selector);
|
|
646
|
-
io_uring_prep_read(sqe, arguments->descriptor, arguments->buffer, arguments->length,
|
|
654
|
+
io_uring_prep_read(sqe, arguments->descriptor, arguments->buffer, arguments->length, arguments->offset);
|
|
647
655
|
io_uring_sqe_set_data(sqe, arguments->waiting->completion);
|
|
648
656
|
io_uring_submit_now(selector);
|
|
649
657
|
|
|
@@ -673,18 +681,21 @@ io_read_ensure(VALUE _arguments)
|
|
|
673
681
|
}
|
|
674
682
|
|
|
675
683
|
static int
|
|
676
|
-
io_read(struct IO_Event_Selector_URing *selector, VALUE fiber, int descriptor, char *buffer, size_t length)
|
|
684
|
+
io_read(struct IO_Event_Selector_URing *selector, VALUE fiber, int descriptor, char *buffer, size_t length, off_t offset)
|
|
677
685
|
{
|
|
678
686
|
struct IO_Event_Selector_URing_Waiting waiting = {
|
|
679
687
|
.fiber = fiber,
|
|
680
688
|
};
|
|
681
689
|
|
|
690
|
+
RB_OBJ_WRITTEN(selector->backend.self, Qundef, fiber);
|
|
691
|
+
|
|
682
692
|
IO_Event_Selector_URing_Completion_acquire(selector, &waiting);
|
|
683
693
|
|
|
684
694
|
struct io_read_arguments io_read_arguments = {
|
|
685
695
|
.selector = selector,
|
|
686
696
|
.waiting = &waiting,
|
|
687
697
|
.descriptor = descriptor,
|
|
698
|
+
.offset = offset,
|
|
688
699
|
.buffer = buffer,
|
|
689
700
|
.length = length
|
|
690
701
|
};
|
|
@@ -707,10 +718,11 @@ VALUE IO_Event_Selector_URing_io_read(VALUE self, VALUE fiber, VALUE io, VALUE b
|
|
|
707
718
|
size_t length = NUM2SIZET(_length);
|
|
708
719
|
size_t offset = NUM2SIZET(_offset);
|
|
709
720
|
size_t total = 0;
|
|
721
|
+
off_t from = io_seekable(descriptor);
|
|
710
722
|
|
|
711
723
|
size_t maximum_size = size - offset;
|
|
712
724
|
while (maximum_size) {
|
|
713
|
-
int result = io_read(selector, fiber, descriptor, (char*)base+offset, maximum_size);
|
|
725
|
+
int result = io_read(selector, fiber, descriptor, (char*)base+offset, maximum_size, from);
|
|
714
726
|
|
|
715
727
|
if (result > 0) {
|
|
716
728
|
total += result;
|
|
@@ -744,12 +756,52 @@ static VALUE IO_Event_Selector_URing_io_read_compatible(int argc, VALUE *argv, V
|
|
|
744
756
|
return IO_Event_Selector_URing_io_read(self, argv[0], argv[1], argv[2], argv[3], _offset);
|
|
745
757
|
}
|
|
746
758
|
|
|
759
|
+
VALUE IO_Event_Selector_URing_io_pread(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _from, VALUE _length, VALUE _offset) {
|
|
760
|
+
struct IO_Event_Selector_URing *selector = NULL;
|
|
761
|
+
TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
|
|
762
|
+
|
|
763
|
+
int descriptor = IO_Event_Selector_io_descriptor(io);
|
|
764
|
+
|
|
765
|
+
void *base;
|
|
766
|
+
size_t size;
|
|
767
|
+
rb_io_buffer_get_bytes_for_writing(buffer, &base, &size);
|
|
768
|
+
|
|
769
|
+
size_t length = NUM2SIZET(_length);
|
|
770
|
+
size_t offset = NUM2SIZET(_offset);
|
|
771
|
+
size_t total = 0;
|
|
772
|
+
off_t from = NUM2OFFT(_from);
|
|
773
|
+
|
|
774
|
+
size_t maximum_size = size - offset;
|
|
775
|
+
while (maximum_size) {
|
|
776
|
+
int result = io_read(selector, fiber, descriptor, (char*)base+offset, maximum_size, from);
|
|
777
|
+
|
|
778
|
+
if (result > 0) {
|
|
779
|
+
total += result;
|
|
780
|
+
offset += result;
|
|
781
|
+
from += result;
|
|
782
|
+
if ((size_t)result >= length) break;
|
|
783
|
+
length -= result;
|
|
784
|
+
} else if (result == 0) {
|
|
785
|
+
break;
|
|
786
|
+
} else if (length > 0 && IO_Event_try_again(-result)) {
|
|
787
|
+
IO_Event_Selector_URing_io_wait(self, fiber, io, RB_INT2NUM(IO_EVENT_READABLE));
|
|
788
|
+
} else {
|
|
789
|
+
return rb_fiber_scheduler_io_result(-1, -result);
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
maximum_size = size - offset;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
return rb_fiber_scheduler_io_result(total, 0);
|
|
796
|
+
}
|
|
797
|
+
|
|
747
798
|
#pragma mark - IO#write
|
|
748
799
|
|
|
749
800
|
struct io_write_arguments {
|
|
750
801
|
struct IO_Event_Selector_URing *selector;
|
|
751
802
|
struct IO_Event_Selector_URing_Waiting *waiting;
|
|
752
803
|
int descriptor;
|
|
804
|
+
off_t offset;
|
|
753
805
|
char *buffer;
|
|
754
806
|
size_t length;
|
|
755
807
|
};
|
|
@@ -763,7 +815,7 @@ io_write_submit(VALUE _argument)
|
|
|
763
815
|
if (DEBUG) fprintf(stderr, "io_write_submit:io_uring_prep_write(waiting=%p, completion=%p, descriptor=%d, buffer=%p, length=%ld)\n", (void*)arguments->waiting, (void*)arguments->waiting->completion, arguments->descriptor, arguments->buffer, arguments->length);
|
|
764
816
|
|
|
765
817
|
struct io_uring_sqe *sqe = io_get_sqe(selector);
|
|
766
|
-
io_uring_prep_write(sqe, arguments->descriptor, arguments->buffer, arguments->length,
|
|
818
|
+
io_uring_prep_write(sqe, arguments->descriptor, arguments->buffer, arguments->length, arguments->offset);
|
|
767
819
|
io_uring_sqe_set_data(sqe, arguments->waiting->completion);
|
|
768
820
|
io_uring_submit_pending(selector);
|
|
769
821
|
|
|
@@ -793,18 +845,21 @@ io_write_ensure(VALUE _argument)
|
|
|
793
845
|
}
|
|
794
846
|
|
|
795
847
|
static int
|
|
796
|
-
io_write(struct IO_Event_Selector_URing *selector, VALUE fiber, int descriptor, char *buffer, size_t length)
|
|
848
|
+
io_write(struct IO_Event_Selector_URing *selector, VALUE fiber, int descriptor, char *buffer, size_t length, off_t offset)
|
|
797
849
|
{
|
|
798
850
|
struct IO_Event_Selector_URing_Waiting waiting = {
|
|
799
851
|
.fiber = fiber,
|
|
800
852
|
};
|
|
801
853
|
|
|
854
|
+
RB_OBJ_WRITTEN(selector->backend.self, Qundef, fiber);
|
|
855
|
+
|
|
802
856
|
IO_Event_Selector_URing_Completion_acquire(selector, &waiting);
|
|
803
857
|
|
|
804
858
|
struct io_write_arguments arguments = {
|
|
805
859
|
.selector = selector,
|
|
806
860
|
.waiting = &waiting,
|
|
807
861
|
.descriptor = descriptor,
|
|
862
|
+
.offset = offset,
|
|
808
863
|
.buffer = buffer,
|
|
809
864
|
.length = length,
|
|
810
865
|
};
|
|
@@ -827,6 +882,7 @@ VALUE IO_Event_Selector_URing_io_write(VALUE self, VALUE fiber, VALUE io, VALUE
|
|
|
827
882
|
size_t length = NUM2SIZET(_length);
|
|
828
883
|
size_t offset = NUM2SIZET(_offset);
|
|
829
884
|
size_t total = 0;
|
|
885
|
+
off_t from = io_seekable(descriptor);
|
|
830
886
|
|
|
831
887
|
if (length > size) {
|
|
832
888
|
rb_raise(rb_eRuntimeError, "Length exceeds size of buffer!");
|
|
@@ -834,7 +890,7 @@ VALUE IO_Event_Selector_URing_io_write(VALUE self, VALUE fiber, VALUE io, VALUE
|
|
|
834
890
|
|
|
835
891
|
size_t maximum_size = size - offset;
|
|
836
892
|
while (maximum_size) {
|
|
837
|
-
int result = io_write(selector, fiber, descriptor, (char*)base+offset, maximum_size);
|
|
893
|
+
int result = io_write(selector, fiber, descriptor, (char*)base+offset, maximum_size, from);
|
|
838
894
|
|
|
839
895
|
if (result > 0) {
|
|
840
896
|
total += result;
|
|
@@ -868,6 +924,49 @@ static VALUE IO_Event_Selector_URing_io_write_compatible(int argc, VALUE *argv,
|
|
|
868
924
|
return IO_Event_Selector_URing_io_write(self, argv[0], argv[1], argv[2], argv[3], _offset);
|
|
869
925
|
}
|
|
870
926
|
|
|
927
|
+
VALUE IO_Event_Selector_URing_io_pwrite(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _from, VALUE _length, VALUE _offset) {
|
|
928
|
+
struct IO_Event_Selector_URing *selector = NULL;
|
|
929
|
+
TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
|
|
930
|
+
|
|
931
|
+
int descriptor = IO_Event_Selector_io_descriptor(io);
|
|
932
|
+
|
|
933
|
+
const void *base;
|
|
934
|
+
size_t size;
|
|
935
|
+
rb_io_buffer_get_bytes_for_reading(buffer, &base, &size);
|
|
936
|
+
|
|
937
|
+
size_t length = NUM2SIZET(_length);
|
|
938
|
+
size_t offset = NUM2SIZET(_offset);
|
|
939
|
+
size_t total = 0;
|
|
940
|
+
off_t from = NUM2OFFT(_from);
|
|
941
|
+
|
|
942
|
+
if (length > size) {
|
|
943
|
+
rb_raise(rb_eRuntimeError, "Length exceeds size of buffer!");
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
size_t maximum_size = size - offset;
|
|
947
|
+
while (maximum_size) {
|
|
948
|
+
int result = io_write(selector, fiber, descriptor, (char*)base+offset, maximum_size, from);
|
|
949
|
+
|
|
950
|
+
if (result > 0) {
|
|
951
|
+
total += result;
|
|
952
|
+
offset += result;
|
|
953
|
+
from += result;
|
|
954
|
+
if ((size_t)result >= length) break;
|
|
955
|
+
length -= result;
|
|
956
|
+
} else if (result == 0) {
|
|
957
|
+
break;
|
|
958
|
+
} else if (length > 0 && IO_Event_try_again(-result)) {
|
|
959
|
+
IO_Event_Selector_URing_io_wait(self, fiber, io, RB_INT2NUM(IO_EVENT_WRITABLE));
|
|
960
|
+
} else {
|
|
961
|
+
return rb_fiber_scheduler_io_result(-1, -result);
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
maximum_size = size - offset;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
return rb_fiber_scheduler_io_result(total, 0);
|
|
968
|
+
}
|
|
969
|
+
|
|
871
970
|
#endif
|
|
872
971
|
|
|
873
972
|
#pragma mark - IO#close
|
|
@@ -1094,8 +1193,7 @@ VALUE IO_Event_Selector_URing_wakeup(VALUE self) {
|
|
|
1094
1193
|
#pragma mark - Native Methods
|
|
1095
1194
|
|
|
1096
1195
|
void Init_IO_Event_Selector_URing(VALUE IO_Event_Selector) {
|
|
1097
|
-
IO_Event_Selector_URing = rb_define_class_under(IO_Event_Selector, "URing", rb_cObject);
|
|
1098
|
-
rb_gc_register_mark_object(IO_Event_Selector_URing);
|
|
1196
|
+
VALUE IO_Event_Selector_URing = rb_define_class_under(IO_Event_Selector, "URing", rb_cObject);
|
|
1099
1197
|
|
|
1100
1198
|
rb_define_alloc_func(IO_Event_Selector_URing, IO_Event_Selector_URing_allocate);
|
|
1101
1199
|
rb_define_method(IO_Event_Selector_URing, "initialize", IO_Event_Selector_URing_initialize, 1);
|
|
@@ -1120,6 +1218,8 @@ void Init_IO_Event_Selector_URing(VALUE IO_Event_Selector) {
|
|
|
1120
1218
|
#ifdef HAVE_RUBY_IO_BUFFER_H
|
|
1121
1219
|
rb_define_method(IO_Event_Selector_URing, "io_read", IO_Event_Selector_URing_io_read_compatible, -1);
|
|
1122
1220
|
rb_define_method(IO_Event_Selector_URing, "io_write", IO_Event_Selector_URing_io_write_compatible, -1);
|
|
1221
|
+
rb_define_method(IO_Event_Selector_URing, "io_pread", IO_Event_Selector_URing_io_pread, 6);
|
|
1222
|
+
rb_define_method(IO_Event_Selector_URing, "io_pwrite", IO_Event_Selector_URing_io_pwrite, 6);
|
|
1123
1223
|
#endif
|
|
1124
1224
|
|
|
1125
1225
|
rb_define_method(IO_Event_Selector_URing, "io_close", IO_Event_Selector_URing_io_close, 1);
|
|
@@ -3,22 +3,33 @@
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
4
|
# Copyright, 2021-2024, by Samuel Williams.
|
|
5
5
|
|
|
6
|
-
require_relative
|
|
6
|
+
require_relative "../support"
|
|
7
7
|
|
|
8
8
|
module IO::Event
|
|
9
|
+
# @namespace
|
|
9
10
|
module Debug
|
|
10
11
|
# Enforces the selector interface and delegates operations to a wrapped selector instance.
|
|
12
|
+
#
|
|
13
|
+
# You can enable this in the default selector by setting the `IO_EVENT_DEBUG_SELECTOR` environment variable. In addition, you can log all selector operations to a file by setting the `IO_EVENT_DEBUG_SELECTOR_LOG` environment variable. This is useful for debugging and understanding the behavior of the event loop.
|
|
11
14
|
class Selector
|
|
15
|
+
# Wrap the given selector with debugging.
|
|
16
|
+
#
|
|
17
|
+
# @parameter selector [Selector] The selector to wrap.
|
|
18
|
+
# @parameter env [Hash] The environment to read configuration from.
|
|
12
19
|
def self.wrap(selector, env = ENV)
|
|
13
20
|
log = nil
|
|
14
21
|
|
|
15
|
-
if log_path = env[
|
|
16
|
-
log = File.open(log_path,
|
|
22
|
+
if log_path = env["IO_EVENT_DEBUG_SELECTOR_LOG"]
|
|
23
|
+
log = File.open(log_path, "w")
|
|
17
24
|
end
|
|
18
25
|
|
|
19
26
|
return self.new(selector, log: log)
|
|
20
27
|
end
|
|
21
28
|
|
|
29
|
+
# Initialize the debug selector with the given selector and optional log.
|
|
30
|
+
#
|
|
31
|
+
# @parameter selector [Selector] The selector to wrap.
|
|
32
|
+
# @parameter log [IO] The log to write debug messages to.
|
|
22
33
|
def initialize(selector, log: nil)
|
|
23
34
|
@selector = selector
|
|
24
35
|
|
|
@@ -33,14 +44,23 @@ module IO::Event
|
|
|
33
44
|
@log = log
|
|
34
45
|
end
|
|
35
46
|
|
|
47
|
+
# The idle duration of the underlying selector.
|
|
48
|
+
#
|
|
49
|
+
# @returns [Numeric] The idle duration.
|
|
36
50
|
def idle_duration
|
|
37
51
|
@selector.idle_duration
|
|
38
52
|
end
|
|
39
53
|
|
|
54
|
+
# The current time.
|
|
55
|
+
#
|
|
56
|
+
# @returns [Numeric] The current time.
|
|
40
57
|
def now
|
|
41
58
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
42
59
|
end
|
|
43
60
|
|
|
61
|
+
# Log the given message.
|
|
62
|
+
#
|
|
63
|
+
# @asynchronous Will block the calling fiber and the entire event loop.
|
|
44
64
|
def log(message)
|
|
45
65
|
return unless @log
|
|
46
66
|
|
|
@@ -49,10 +69,12 @@ module IO::Event
|
|
|
49
69
|
end
|
|
50
70
|
end
|
|
51
71
|
|
|
72
|
+
# Wakeup the the selector.
|
|
52
73
|
def wakeup
|
|
53
74
|
@selector.wakeup
|
|
54
75
|
end
|
|
55
76
|
|
|
77
|
+
# Close the selector.
|
|
56
78
|
def close
|
|
57
79
|
log("Closing selector")
|
|
58
80
|
|
|
@@ -64,60 +86,78 @@ module IO::Event
|
|
|
64
86
|
@selector = nil
|
|
65
87
|
end
|
|
66
88
|
|
|
67
|
-
# Transfer from the calling fiber to the
|
|
89
|
+
# Transfer from the calling fiber to the selector.
|
|
68
90
|
def transfer
|
|
69
91
|
log("Transfering to event loop")
|
|
70
92
|
@selector.transfer
|
|
71
93
|
end
|
|
72
94
|
|
|
95
|
+
# Resume the given fiber with the given arguments.
|
|
73
96
|
def resume(*arguments)
|
|
74
97
|
log("Resuming fiber with #{arguments.inspect}")
|
|
75
98
|
@selector.resume(*arguments)
|
|
76
99
|
end
|
|
77
100
|
|
|
101
|
+
# Yield to the selector.
|
|
78
102
|
def yield
|
|
79
103
|
log("Yielding to event loop")
|
|
80
104
|
@selector.yield
|
|
81
105
|
end
|
|
82
106
|
|
|
107
|
+
# Push the given fiber to the selector ready list, such that it will be resumed on the next call to {select}.
|
|
108
|
+
#
|
|
109
|
+
# @parameter fiber [Fiber] The fiber that is ready.
|
|
83
110
|
def push(fiber)
|
|
84
111
|
log("Pushing fiber #{fiber.inspect} to ready list")
|
|
85
112
|
@selector.push(fiber)
|
|
86
113
|
end
|
|
87
114
|
|
|
115
|
+
# Raise the given exception on the given fiber.
|
|
116
|
+
#
|
|
117
|
+
# @parameter fiber [Fiber] The fiber to raise the exception on.
|
|
118
|
+
# @parameter arguments [Array] The arguments to use when raising the exception.
|
|
88
119
|
def raise(fiber, *arguments)
|
|
89
120
|
log("Raising exception on fiber #{fiber.inspect} with #{arguments.inspect}")
|
|
90
121
|
@selector.raise(fiber, *arguments)
|
|
91
122
|
end
|
|
92
123
|
|
|
124
|
+
# Check if the selector is ready.
|
|
125
|
+
#
|
|
126
|
+
# @returns [Boolean] Whether the selector is ready.
|
|
93
127
|
def ready?
|
|
94
128
|
@selector.ready?
|
|
95
129
|
end
|
|
96
130
|
|
|
131
|
+
# Wait for the given process, forwarded to the underlying selector.
|
|
97
132
|
def process_wait(*arguments)
|
|
98
133
|
log("Waiting for process with #{arguments.inspect}")
|
|
99
134
|
@selector.process_wait(*arguments)
|
|
100
135
|
end
|
|
101
136
|
|
|
137
|
+
# Wait for the given IO, forwarded to the underlying selector.
|
|
102
138
|
def io_wait(fiber, io, events)
|
|
103
139
|
log("Waiting for IO #{io.inspect} for events #{events.inspect}")
|
|
104
140
|
@selector.io_wait(fiber, io, events)
|
|
105
141
|
end
|
|
106
142
|
|
|
143
|
+
# Read from the given IO, forwarded to the underlying selector.
|
|
107
144
|
def io_read(fiber, io, buffer, length, offset = 0)
|
|
108
145
|
log("Reading from IO #{io.inspect} with buffer #{buffer}; length #{length} offset #{offset}")
|
|
109
146
|
@selector.io_read(fiber, io, buffer, length, offset)
|
|
110
147
|
end
|
|
111
148
|
|
|
149
|
+
# Write to the given IO, forwarded to the underlying selector.
|
|
112
150
|
def io_write(fiber, io, buffer, length, offset = 0)
|
|
113
151
|
log("Writing to IO #{io.inspect} with buffer #{buffer}; length #{length} offset #{offset}")
|
|
114
152
|
@selector.io_write(fiber, io, buffer, length, offset)
|
|
115
153
|
end
|
|
116
154
|
|
|
155
|
+
# Forward the given method to the underlying selector.
|
|
117
156
|
def respond_to?(name, include_private = false)
|
|
118
157
|
@selector.respond_to?(name, include_private)
|
|
119
158
|
end
|
|
120
159
|
|
|
160
|
+
# Select for the given duration, forwarded to the underlying selector.
|
|
121
161
|
def select(duration = nil)
|
|
122
162
|
log("Selecting for #{duration.inspect}")
|
|
123
163
|
unless Fiber.current == @selector.loop
|
data/lib/io/event/interrupt.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2021-
|
|
4
|
+
# Copyright, 2021-2024, by Samuel Williams.
|
|
5
5
|
|
|
6
6
|
module IO::Event
|
|
7
7
|
# A thread safe synchronisation primative.
|
|
@@ -27,7 +27,7 @@ module IO::Event
|
|
|
27
27
|
|
|
28
28
|
# Send a sigle byte interrupt.
|
|
29
29
|
def signal
|
|
30
|
-
@output.write(
|
|
30
|
+
@output.write(".")
|
|
31
31
|
@output.flush
|
|
32
32
|
rescue IOError
|
|
33
33
|
# Ignore.
|
|
@@ -10,6 +10,7 @@ class IO
|
|
|
10
10
|
# of its contents to determine priority.
|
|
11
11
|
# See <https://en.wikipedia.org/wiki/Binary_heap> for explanations of the main methods.
|
|
12
12
|
class PriorityHeap
|
|
13
|
+
# Initializes the heap.
|
|
13
14
|
def initialize
|
|
14
15
|
# The heap is represented with an array containing a binary tree. See
|
|
15
16
|
# https://en.wikipedia.org/wiki/Binary_heap#Heap_implementation for how this array
|
|
@@ -17,18 +18,19 @@ class IO
|
|
|
17
18
|
@contents = []
|
|
18
19
|
end
|
|
19
20
|
|
|
20
|
-
#
|
|
21
|
+
# @returns [Object | Nil] the smallest element in the heap without removing it, or nil if the heap is empty.
|
|
21
22
|
def peek
|
|
22
23
|
@contents[0]
|
|
23
24
|
end
|
|
24
25
|
|
|
25
|
-
#
|
|
26
|
+
# @returns [Integer] the number of elements in the heap.
|
|
26
27
|
def size
|
|
27
28
|
@contents.size
|
|
28
29
|
end
|
|
29
30
|
|
|
30
|
-
#
|
|
31
|
-
#
|
|
31
|
+
# Removes and returns the smallest element in the heap, or nil if the heap is empty.
|
|
32
|
+
#
|
|
33
|
+
# @returns [Object | Nil] The smallest element in the heap, or nil if the heap is empty.
|
|
32
34
|
def pop
|
|
33
35
|
# If the heap is empty:
|
|
34
36
|
if @contents.empty?
|
|
@@ -57,7 +59,9 @@ class IO
|
|
|
57
59
|
return value
|
|
58
60
|
end
|
|
59
61
|
|
|
60
|
-
#
|
|
62
|
+
# Add a new element to the heap, then rearrange elements until the heap invariant is true again.
|
|
63
|
+
#
|
|
64
|
+
# @parameter element [Object] The element to add to the heap.
|
|
61
65
|
def push(element)
|
|
62
66
|
# Insert the item at the end of the heap:
|
|
63
67
|
@contents.push(element)
|
|
@@ -75,10 +79,9 @@ class IO
|
|
|
75
79
|
@contents = []
|
|
76
80
|
end
|
|
77
81
|
|
|
78
|
-
# Validate the heap invariant. Every element except the root must not be smaller than
|
|
79
|
-
# its parent element. Note that it MAY be equal.
|
|
82
|
+
# Validate the heap invariant. Every element except the root must not be smaller than its parent element. Note that it MAY be equal.
|
|
80
83
|
def valid?
|
|
81
|
-
#
|
|
84
|
+
# Notice we skip index 0 on purpose, because it has no parent
|
|
82
85
|
(1..(@contents.size - 1)).all? { |e| @contents[e] >= @contents[(e - 1) / 2] }
|
|
83
86
|
end
|
|
84
87
|
|
|
@@ -93,10 +96,7 @@ class IO
|
|
|
93
96
|
parent_index = (index - 1) / 2 # watch out, integer division!
|
|
94
97
|
|
|
95
98
|
while index > 0 && @contents[index] < @contents[parent_index]
|
|
96
|
-
#
|
|
97
|
-
# to uphold the minheap invariant and update the index of the 'current'
|
|
98
|
-
# node. If the node is already at index 0, we can also stop because that
|
|
99
|
-
# is the root of the heap.
|
|
99
|
+
# If the node has a smaller value than its parent, swap these nodes to uphold the minheap invariant and update the index of the 'current' node. If the node is already at index 0, we can also stop because that is the root of the heap.
|
|
100
100
|
# swap(index, parent_index)
|
|
101
101
|
@contents[index], @contents[parent_index] = @contents[parent_index], @contents[index]
|
|
102
102
|
|
|
@@ -114,8 +114,7 @@ class IO
|
|
|
114
114
|
left_value = @contents[left_index]
|
|
115
115
|
|
|
116
116
|
if left_value.nil?
|
|
117
|
-
# This node has no children so it can't bubble down any further.
|
|
118
|
-
# We're done here!
|
|
117
|
+
# This node has no children so it can't bubble down any further. We're done here!
|
|
119
118
|
return
|
|
120
119
|
end
|
|
121
120
|
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2022-
|
|
4
|
+
# Copyright, 2022-2024, by Samuel Williams.
|
|
5
5
|
|
|
6
|
-
require
|
|
6
|
+
require "io/nonblock"
|
|
7
7
|
|
|
8
8
|
module IO::Event
|
|
9
9
|
module Selector
|
|
10
|
+
# Execute the given block in non-blocking mode.
|
|
11
|
+
#
|
|
12
|
+
# @parameter io [IO] The IO object to operate on.
|
|
13
|
+
# @yields {...} The block to execute.
|
|
10
14
|
def self.nonblock(io, &block)
|
|
11
15
|
io.nonblock(&block)
|
|
12
16
|
rescue Errno::EBADF
|
|
@@ -4,12 +4,14 @@
|
|
|
4
4
|
# Copyright, 2021-2024, by Samuel Williams.
|
|
5
5
|
# Copyright, 2023, by Math Ieu.
|
|
6
6
|
|
|
7
|
-
require_relative
|
|
8
|
-
require_relative
|
|
7
|
+
require_relative "../interrupt"
|
|
8
|
+
require_relative "../support"
|
|
9
9
|
|
|
10
10
|
module IO::Event
|
|
11
11
|
module Selector
|
|
12
|
+
# A pure-Ruby implementation of the event selector.
|
|
12
13
|
class Select
|
|
14
|
+
# Initialize the selector with the given event loop fiber.
|
|
13
15
|
def initialize(loop)
|
|
14
16
|
@loop = loop
|
|
15
17
|
|
|
@@ -23,12 +25,13 @@ module IO::Event
|
|
|
23
25
|
@idle_duration = 0.0
|
|
24
26
|
end
|
|
25
27
|
|
|
28
|
+
# @attribute [Fiber] The event loop fiber.
|
|
26
29
|
attr :loop
|
|
27
30
|
|
|
28
|
-
# This is the amount of time the event loop was idle during the last select call.
|
|
31
|
+
# @attribute [Float] This is the amount of time the event loop was idle during the last select call.
|
|
29
32
|
attr :idle_duration
|
|
30
33
|
|
|
31
|
-
#
|
|
34
|
+
# Wake up the event loop if it is currently sleeping.
|
|
32
35
|
def wakeup
|
|
33
36
|
if @blocked
|
|
34
37
|
@interrupt.signal
|
|
@@ -39,6 +42,7 @@ module IO::Event
|
|
|
39
42
|
return false
|
|
40
43
|
end
|
|
41
44
|
|
|
45
|
+
# Close the selector and release any resources.
|
|
42
46
|
def close
|
|
43
47
|
@interrupt.close
|
|
44
48
|
|
|
@@ -100,6 +104,7 @@ module IO::Event
|
|
|
100
104
|
optional.nullify
|
|
101
105
|
end
|
|
102
106
|
|
|
107
|
+
# @returns [Boolean] Whether the ready list is not empty, i.e. there are fibers ready to be resumed.
|
|
103
108
|
def ready?
|
|
104
109
|
!@ready.empty?
|
|
105
110
|
end
|
|
@@ -144,6 +149,11 @@ module IO::Event
|
|
|
144
149
|
end
|
|
145
150
|
end
|
|
146
151
|
|
|
152
|
+
# Wait for the given IO to become readable or writable.
|
|
153
|
+
#
|
|
154
|
+
# @parameter fiber [Fiber] The fiber that is waiting.
|
|
155
|
+
# @parameter io [IO] The IO object to wait on.
|
|
156
|
+
# @parameter events [Integer] The events to wait for.
|
|
147
157
|
def io_wait(fiber, io, events)
|
|
148
158
|
waiter = @waiting[io] = Waiter.new(fiber, events, @waiting[io])
|
|
149
159
|
|
|
@@ -152,6 +162,11 @@ module IO::Event
|
|
|
152
162
|
waiter&.invalidate
|
|
153
163
|
end
|
|
154
164
|
|
|
165
|
+
# Wait for multiple IO objects to become readable or writable.
|
|
166
|
+
#
|
|
167
|
+
# @parameter readable [Array(IO)] The list of IO objects to wait for readability.
|
|
168
|
+
# @parameter writable [Array(IO)] The list of IO objects to wait for writability.
|
|
169
|
+
# @parameter priority [Array(IO)] The list of IO objects to wait for priority events.
|
|
155
170
|
def io_select(readable, writable, priority, timeout)
|
|
156
171
|
Thread.new do
|
|
157
172
|
IO.select(readable, writable, priority, timeout)
|
|
@@ -161,13 +176,16 @@ module IO::Event
|
|
|
161
176
|
EAGAIN = -Errno::EAGAIN::Errno
|
|
162
177
|
EWOULDBLOCK = -Errno::EWOULDBLOCK::Errno
|
|
163
178
|
|
|
164
|
-
|
|
179
|
+
# Whether the given error code indicates that the operation should be retried.
|
|
180
|
+
protected def again?(errno)
|
|
165
181
|
errno == EAGAIN or errno == EWOULDBLOCK
|
|
166
182
|
end
|
|
167
183
|
|
|
168
184
|
if Support.fiber_scheduler_v3?
|
|
169
185
|
# Ruby 3.3+, full IO::Buffer support.
|
|
170
186
|
|
|
187
|
+
# Read from the given IO to the buffer.
|
|
188
|
+
#
|
|
171
189
|
# @parameter length [Integer] The minimum number of bytes to read.
|
|
172
190
|
# @parameter offset [Integer] The offset into the buffer to read to.
|
|
173
191
|
def io_read(fiber, io, buffer, length, offset = 0)
|
|
@@ -196,6 +214,8 @@ module IO::Event
|
|
|
196
214
|
return total
|
|
197
215
|
end
|
|
198
216
|
|
|
217
|
+
# Write to the given IO from the buffer.
|
|
218
|
+
#
|
|
199
219
|
# @parameter length [Integer] The minimum number of bytes to write.
|
|
200
220
|
# @parameter offset [Integer] The offset into the buffer to write from.
|
|
201
221
|
def io_write(fiber, io, buffer, length, offset = 0)
|