io-event 1.6.5 → 1.9.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/ext/extconf.rb +26 -13
- data/ext/io/event/{selector/array.h → array.h} +74 -18
- data/ext/io/event/event.c +9 -26
- data/ext/io/event/event.h +2 -19
- data/ext/io/event/fiber.c +63 -0
- data/ext/io/event/fiber.h +23 -0
- data/ext/io/event/interrupt.c +19 -27
- data/ext/io/event/interrupt.h +2 -19
- data/ext/io/event/{selector/list.h → list.h} +20 -2
- data/ext/io/event/profiler.c +505 -0
- data/ext/io/event/profiler.h +8 -0
- data/ext/io/event/selector/epoll.c +41 -43
- data/ext/io/event/selector/epoll.h +2 -19
- data/ext/io/event/selector/kqueue.c +34 -41
- data/ext/io/event/selector/kqueue.h +2 -19
- data/ext/io/event/selector/pidfd.c +2 -19
- data/ext/io/event/selector/selector.c +65 -102
- data/ext/io/event/selector/selector.h +51 -46
- data/ext/io/event/selector/uring.c +130 -47
- data/ext/io/event/selector/uring.h +2 -19
- data/ext/io/event/time.c +35 -0
- data/ext/io/event/time.h +17 -0
- data/lib/io/event/debug/selector.rb +44 -4
- data/lib/io/event/interrupt.rb +2 -2
- data/lib/io/event/native.rb +11 -0
- data/lib/io/event/priority_heap.rb +13 -14
- data/lib/io/event/profiler.rb +18 -0
- 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 +4 -2
- data/lib/io/event.rb +5 -11
- data/license.md +4 -1
- data/readme.md +22 -4
- data/releases.md +57 -0
- data.tar.gz.sig +0 -0
- metadata +20 -11
- metadata.gz.sig +0 -0
|
@@ -1,27 +1,10 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
-
// of this software and associated documentation files (the "Software"), to deal
|
|
5
|
-
// in the Software without restriction, including without limitation the rights
|
|
6
|
-
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
-
// copies of the Software, and to permit persons to whom the Software is
|
|
8
|
-
// furnished to do so, subject to the following conditions:
|
|
9
|
-
//
|
|
10
|
-
// The above copyright notice and this permission notice shall be included in
|
|
11
|
-
// all copies or substantial portions of the Software.
|
|
12
|
-
//
|
|
13
|
-
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
-
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
-
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
-
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
-
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
-
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
19
|
-
// THE SOFTWARE.
|
|
1
|
+
// Released under the MIT License.
|
|
2
|
+
// Copyright, 2021-2025, by Samuel Williams.
|
|
20
3
|
|
|
21
4
|
#include "uring.h"
|
|
22
5
|
#include "selector.h"
|
|
23
|
-
#include "list.h"
|
|
24
|
-
#include "array.h"
|
|
6
|
+
#include "../list.h"
|
|
7
|
+
#include "../array.h"
|
|
25
8
|
|
|
26
9
|
#include <liburing.h>
|
|
27
10
|
#include <poll.h>
|
|
@@ -35,10 +18,9 @@
|
|
|
35
18
|
enum {
|
|
36
19
|
DEBUG = 0,
|
|
37
20
|
DEBUG_COMPLETION = 0,
|
|
21
|
+
DEBUG_IO_READ = 1,
|
|
38
22
|
};
|
|
39
23
|
|
|
40
|
-
static VALUE IO_Event_Selector_URing = Qnil;
|
|
41
|
-
|
|
42
24
|
enum {URING_ENTRIES = 64};
|
|
43
25
|
|
|
44
26
|
#pragma mark - Data Type
|
|
@@ -140,11 +122,12 @@ size_t IO_Event_Selector_URing_Type_size(const void *_selector)
|
|
|
140
122
|
|
|
141
123
|
return sizeof(struct IO_Event_Selector_URing)
|
|
142
124
|
+ IO_Event_Array_memory_size(&selector->completions)
|
|
125
|
+
+ IO_Event_List_memory_size(&selector->free_list)
|
|
143
126
|
;
|
|
144
127
|
}
|
|
145
128
|
|
|
146
129
|
static const rb_data_type_t IO_Event_Selector_URing_Type = {
|
|
147
|
-
.wrap_struct_name = "
|
|
130
|
+
.wrap_struct_name = "IO::Event::Backend::URing",
|
|
148
131
|
.function = {
|
|
149
132
|
.dmark = IO_Event_Selector_URing_Type_mark,
|
|
150
133
|
.dcompact = IO_Event_Selector_URing_Type_compact,
|
|
@@ -152,7 +135,7 @@ static const rb_data_type_t IO_Event_Selector_URing_Type = {
|
|
|
152
135
|
.dsize = IO_Event_Selector_URing_Type_size,
|
|
153
136
|
},
|
|
154
137
|
.data = NULL,
|
|
155
|
-
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
|
138
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
|
|
156
139
|
};
|
|
157
140
|
|
|
158
141
|
inline static
|
|
@@ -228,7 +211,7 @@ VALUE IO_Event_Selector_URing_allocate(VALUE self) {
|
|
|
228
211
|
struct IO_Event_Selector_URing *selector = NULL;
|
|
229
212
|
VALUE instance = TypedData_Make_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
|
|
230
213
|
|
|
231
|
-
IO_Event_Selector_initialize(&selector->backend, Qnil);
|
|
214
|
+
IO_Event_Selector_initialize(&selector->backend, self, Qnil);
|
|
232
215
|
selector->ring.ring_fd = -1;
|
|
233
216
|
|
|
234
217
|
selector->pending = 0;
|
|
@@ -238,7 +221,10 @@ VALUE IO_Event_Selector_URing_allocate(VALUE self) {
|
|
|
238
221
|
|
|
239
222
|
selector->completions.element_initialize = IO_Event_Selector_URing_Completion_initialize;
|
|
240
223
|
selector->completions.element_free = IO_Event_Selector_URing_Completion_free;
|
|
241
|
-
|
|
224
|
+
int result = IO_Event_Array_initialize(&selector->completions, IO_EVENT_ARRAY_DEFAULT_COUNT, sizeof(struct IO_Event_Selector_URing_Completion));
|
|
225
|
+
if (result < 0) {
|
|
226
|
+
rb_sys_fail("IO_Event_Selector_URing_allocate:IO_Event_Array_initialize");
|
|
227
|
+
}
|
|
242
228
|
|
|
243
229
|
return instance;
|
|
244
230
|
}
|
|
@@ -249,7 +235,7 @@ VALUE IO_Event_Selector_URing_initialize(VALUE self, VALUE loop) {
|
|
|
249
235
|
struct IO_Event_Selector_URing *selector = NULL;
|
|
250
236
|
TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
|
|
251
237
|
|
|
252
|
-
IO_Event_Selector_initialize(&selector->backend, loop);
|
|
238
|
+
IO_Event_Selector_initialize(&selector->backend, self, loop);
|
|
253
239
|
int result = io_uring_queue_init(URING_ENTRIES, &selector->ring, 0);
|
|
254
240
|
|
|
255
241
|
if (result < 0) {
|
|
@@ -291,7 +277,7 @@ VALUE IO_Event_Selector_URing_transfer(VALUE self)
|
|
|
291
277
|
struct IO_Event_Selector_URing *selector = NULL;
|
|
292
278
|
TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
|
|
293
279
|
|
|
294
|
-
return
|
|
280
|
+
return IO_Event_Selector_loop_yield(&selector->backend);
|
|
295
281
|
}
|
|
296
282
|
|
|
297
283
|
VALUE IO_Event_Selector_URing_resume(int argc, VALUE *argv, VALUE self)
|
|
@@ -315,7 +301,7 @@ VALUE IO_Event_Selector_URing_push(VALUE self, VALUE fiber)
|
|
|
315
301
|
struct IO_Event_Selector_URing *selector = NULL;
|
|
316
302
|
TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
|
|
317
303
|
|
|
318
|
-
|
|
304
|
+
IO_Event_Selector_ready_push(&selector->backend, fiber);
|
|
319
305
|
|
|
320
306
|
return Qnil;
|
|
321
307
|
}
|
|
@@ -447,7 +433,7 @@ static
|
|
|
447
433
|
VALUE process_wait_transfer(VALUE _arguments) {
|
|
448
434
|
struct process_wait_arguments *arguments = (struct process_wait_arguments *)_arguments;
|
|
449
435
|
|
|
450
|
-
|
|
436
|
+
IO_Event_Selector_loop_yield(&arguments->selector->backend);
|
|
451
437
|
|
|
452
438
|
if (arguments->waiting->result) {
|
|
453
439
|
return IO_Event_Selector_process_status_wait(arguments->pid, arguments->flags);
|
|
@@ -484,6 +470,8 @@ VALUE IO_Event_Selector_URing_process_wait(VALUE self, VALUE fiber, VALUE _pid,
|
|
|
484
470
|
.fiber = fiber,
|
|
485
471
|
};
|
|
486
472
|
|
|
473
|
+
RB_OBJ_WRITTEN(self, Qundef, fiber);
|
|
474
|
+
|
|
487
475
|
struct IO_Event_Selector_URing_Completion *completion = IO_Event_Selector_URing_Completion_acquire(selector, &waiting);
|
|
488
476
|
|
|
489
477
|
struct process_wait_arguments process_wait_arguments = {
|
|
@@ -560,7 +548,7 @@ VALUE io_wait_transfer(VALUE _arguments) {
|
|
|
560
548
|
struct io_wait_arguments *arguments = (struct io_wait_arguments *)_arguments;
|
|
561
549
|
struct IO_Event_Selector_URing *selector = arguments->selector;
|
|
562
550
|
|
|
563
|
-
|
|
551
|
+
IO_Event_Selector_loop_yield(&selector->backend);
|
|
564
552
|
|
|
565
553
|
if (DEBUG) fprintf(stderr, "io_wait_transfer:waiting=%p, result=%d\n", (void*)arguments->waiting, arguments->waiting->result);
|
|
566
554
|
|
|
@@ -587,6 +575,8 @@ VALUE IO_Event_Selector_URing_io_wait(VALUE self, VALUE fiber, VALUE io, VALUE e
|
|
|
587
575
|
.fiber = fiber,
|
|
588
576
|
};
|
|
589
577
|
|
|
578
|
+
RB_OBJ_WRITTEN(self, Qundef, fiber);
|
|
579
|
+
|
|
590
580
|
struct IO_Event_Selector_URing_Completion *completion = IO_Event_Selector_URing_Completion_acquire(selector, &waiting);
|
|
591
581
|
|
|
592
582
|
struct io_uring_sqe *sqe = io_get_sqe(selector);
|
|
@@ -630,6 +620,7 @@ struct io_read_arguments {
|
|
|
630
620
|
struct IO_Event_Selector_URing *selector;
|
|
631
621
|
struct IO_Event_Selector_URing_Waiting *waiting;
|
|
632
622
|
int descriptor;
|
|
623
|
+
off_t offset;
|
|
633
624
|
char *buffer;
|
|
634
625
|
size_t length;
|
|
635
626
|
};
|
|
@@ -643,11 +634,11 @@ io_read_submit(VALUE _arguments)
|
|
|
643
634
|
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
635
|
|
|
645
636
|
struct io_uring_sqe *sqe = io_get_sqe(selector);
|
|
646
|
-
io_uring_prep_read(sqe, arguments->descriptor, arguments->buffer, arguments->length,
|
|
637
|
+
io_uring_prep_read(sqe, arguments->descriptor, arguments->buffer, arguments->length, arguments->offset);
|
|
647
638
|
io_uring_sqe_set_data(sqe, arguments->waiting->completion);
|
|
648
639
|
io_uring_submit_now(selector);
|
|
649
640
|
|
|
650
|
-
|
|
641
|
+
IO_Event_Selector_loop_yield(&selector->backend);
|
|
651
642
|
|
|
652
643
|
return RB_INT2NUM(arguments->waiting->result);
|
|
653
644
|
}
|
|
@@ -673,18 +664,21 @@ io_read_ensure(VALUE _arguments)
|
|
|
673
664
|
}
|
|
674
665
|
|
|
675
666
|
static int
|
|
676
|
-
io_read(struct IO_Event_Selector_URing *selector, VALUE fiber, int descriptor, char *buffer, size_t length)
|
|
667
|
+
io_read(struct IO_Event_Selector_URing *selector, VALUE fiber, int descriptor, char *buffer, size_t length, off_t offset)
|
|
677
668
|
{
|
|
678
669
|
struct IO_Event_Selector_URing_Waiting waiting = {
|
|
679
670
|
.fiber = fiber,
|
|
680
671
|
};
|
|
681
672
|
|
|
673
|
+
RB_OBJ_WRITTEN(selector->backend.self, Qundef, fiber);
|
|
674
|
+
|
|
682
675
|
IO_Event_Selector_URing_Completion_acquire(selector, &waiting);
|
|
683
676
|
|
|
684
677
|
struct io_read_arguments io_read_arguments = {
|
|
685
678
|
.selector = selector,
|
|
686
679
|
.waiting = &waiting,
|
|
687
680
|
.descriptor = descriptor,
|
|
681
|
+
.offset = offset,
|
|
688
682
|
.buffer = buffer,
|
|
689
683
|
.length = length
|
|
690
684
|
};
|
|
@@ -707,10 +701,11 @@ VALUE IO_Event_Selector_URing_io_read(VALUE self, VALUE fiber, VALUE io, VALUE b
|
|
|
707
701
|
size_t length = NUM2SIZET(_length);
|
|
708
702
|
size_t offset = NUM2SIZET(_offset);
|
|
709
703
|
size_t total = 0;
|
|
704
|
+
off_t from = io_seekable(descriptor);
|
|
710
705
|
|
|
711
706
|
size_t maximum_size = size - offset;
|
|
712
707
|
while (maximum_size) {
|
|
713
|
-
int result = io_read(selector, fiber, descriptor, (char*)base+offset, maximum_size);
|
|
708
|
+
int result = io_read(selector, fiber, descriptor, (char*)base+offset, maximum_size, from);
|
|
714
709
|
|
|
715
710
|
if (result > 0) {
|
|
716
711
|
total += result;
|
|
@@ -744,12 +739,52 @@ static VALUE IO_Event_Selector_URing_io_read_compatible(int argc, VALUE *argv, V
|
|
|
744
739
|
return IO_Event_Selector_URing_io_read(self, argv[0], argv[1], argv[2], argv[3], _offset);
|
|
745
740
|
}
|
|
746
741
|
|
|
742
|
+
VALUE IO_Event_Selector_URing_io_pread(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _from, VALUE _length, VALUE _offset) {
|
|
743
|
+
struct IO_Event_Selector_URing *selector = NULL;
|
|
744
|
+
TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
|
|
745
|
+
|
|
746
|
+
int descriptor = IO_Event_Selector_io_descriptor(io);
|
|
747
|
+
|
|
748
|
+
void *base;
|
|
749
|
+
size_t size;
|
|
750
|
+
rb_io_buffer_get_bytes_for_writing(buffer, &base, &size);
|
|
751
|
+
|
|
752
|
+
size_t length = NUM2SIZET(_length);
|
|
753
|
+
size_t offset = NUM2SIZET(_offset);
|
|
754
|
+
size_t total = 0;
|
|
755
|
+
off_t from = NUM2OFFT(_from);
|
|
756
|
+
|
|
757
|
+
size_t maximum_size = size - offset;
|
|
758
|
+
while (maximum_size) {
|
|
759
|
+
int result = io_read(selector, fiber, descriptor, (char*)base+offset, maximum_size, from);
|
|
760
|
+
|
|
761
|
+
if (result > 0) {
|
|
762
|
+
total += result;
|
|
763
|
+
offset += result;
|
|
764
|
+
from += result;
|
|
765
|
+
if ((size_t)result >= length) break;
|
|
766
|
+
length -= result;
|
|
767
|
+
} else if (result == 0) {
|
|
768
|
+
break;
|
|
769
|
+
} else if (length > 0 && IO_Event_try_again(-result)) {
|
|
770
|
+
IO_Event_Selector_URing_io_wait(self, fiber, io, RB_INT2NUM(IO_EVENT_READABLE));
|
|
771
|
+
} else {
|
|
772
|
+
return rb_fiber_scheduler_io_result(-1, -result);
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
maximum_size = size - offset;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
return rb_fiber_scheduler_io_result(total, 0);
|
|
779
|
+
}
|
|
780
|
+
|
|
747
781
|
#pragma mark - IO#write
|
|
748
782
|
|
|
749
783
|
struct io_write_arguments {
|
|
750
784
|
struct IO_Event_Selector_URing *selector;
|
|
751
785
|
struct IO_Event_Selector_URing_Waiting *waiting;
|
|
752
786
|
int descriptor;
|
|
787
|
+
off_t offset;
|
|
753
788
|
char *buffer;
|
|
754
789
|
size_t length;
|
|
755
790
|
};
|
|
@@ -763,11 +798,11 @@ io_write_submit(VALUE _argument)
|
|
|
763
798
|
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
799
|
|
|
765
800
|
struct io_uring_sqe *sqe = io_get_sqe(selector);
|
|
766
|
-
io_uring_prep_write(sqe, arguments->descriptor, arguments->buffer, arguments->length,
|
|
801
|
+
io_uring_prep_write(sqe, arguments->descriptor, arguments->buffer, arguments->length, arguments->offset);
|
|
767
802
|
io_uring_sqe_set_data(sqe, arguments->waiting->completion);
|
|
768
803
|
io_uring_submit_pending(selector);
|
|
769
804
|
|
|
770
|
-
|
|
805
|
+
IO_Event_Selector_loop_yield(&selector->backend);
|
|
771
806
|
|
|
772
807
|
return RB_INT2NUM(arguments->waiting->result);
|
|
773
808
|
}
|
|
@@ -793,18 +828,21 @@ io_write_ensure(VALUE _argument)
|
|
|
793
828
|
}
|
|
794
829
|
|
|
795
830
|
static int
|
|
796
|
-
io_write(struct IO_Event_Selector_URing *selector, VALUE fiber, int descriptor, char *buffer, size_t length)
|
|
831
|
+
io_write(struct IO_Event_Selector_URing *selector, VALUE fiber, int descriptor, char *buffer, size_t length, off_t offset)
|
|
797
832
|
{
|
|
798
833
|
struct IO_Event_Selector_URing_Waiting waiting = {
|
|
799
834
|
.fiber = fiber,
|
|
800
835
|
};
|
|
801
836
|
|
|
837
|
+
RB_OBJ_WRITTEN(selector->backend.self, Qundef, fiber);
|
|
838
|
+
|
|
802
839
|
IO_Event_Selector_URing_Completion_acquire(selector, &waiting);
|
|
803
840
|
|
|
804
841
|
struct io_write_arguments arguments = {
|
|
805
842
|
.selector = selector,
|
|
806
843
|
.waiting = &waiting,
|
|
807
844
|
.descriptor = descriptor,
|
|
845
|
+
.offset = offset,
|
|
808
846
|
.buffer = buffer,
|
|
809
847
|
.length = length,
|
|
810
848
|
};
|
|
@@ -827,6 +865,7 @@ VALUE IO_Event_Selector_URing_io_write(VALUE self, VALUE fiber, VALUE io, VALUE
|
|
|
827
865
|
size_t length = NUM2SIZET(_length);
|
|
828
866
|
size_t offset = NUM2SIZET(_offset);
|
|
829
867
|
size_t total = 0;
|
|
868
|
+
off_t from = io_seekable(descriptor);
|
|
830
869
|
|
|
831
870
|
if (length > size) {
|
|
832
871
|
rb_raise(rb_eRuntimeError, "Length exceeds size of buffer!");
|
|
@@ -834,7 +873,7 @@ VALUE IO_Event_Selector_URing_io_write(VALUE self, VALUE fiber, VALUE io, VALUE
|
|
|
834
873
|
|
|
835
874
|
size_t maximum_size = size - offset;
|
|
836
875
|
while (maximum_size) {
|
|
837
|
-
int result = io_write(selector, fiber, descriptor, (char*)base+offset, maximum_size);
|
|
876
|
+
int result = io_write(selector, fiber, descriptor, (char*)base+offset, maximum_size, from);
|
|
838
877
|
|
|
839
878
|
if (result > 0) {
|
|
840
879
|
total += result;
|
|
@@ -868,6 +907,49 @@ static VALUE IO_Event_Selector_URing_io_write_compatible(int argc, VALUE *argv,
|
|
|
868
907
|
return IO_Event_Selector_URing_io_write(self, argv[0], argv[1], argv[2], argv[3], _offset);
|
|
869
908
|
}
|
|
870
909
|
|
|
910
|
+
VALUE IO_Event_Selector_URing_io_pwrite(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _from, VALUE _length, VALUE _offset) {
|
|
911
|
+
struct IO_Event_Selector_URing *selector = NULL;
|
|
912
|
+
TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
|
|
913
|
+
|
|
914
|
+
int descriptor = IO_Event_Selector_io_descriptor(io);
|
|
915
|
+
|
|
916
|
+
const void *base;
|
|
917
|
+
size_t size;
|
|
918
|
+
rb_io_buffer_get_bytes_for_reading(buffer, &base, &size);
|
|
919
|
+
|
|
920
|
+
size_t length = NUM2SIZET(_length);
|
|
921
|
+
size_t offset = NUM2SIZET(_offset);
|
|
922
|
+
size_t total = 0;
|
|
923
|
+
off_t from = NUM2OFFT(_from);
|
|
924
|
+
|
|
925
|
+
if (length > size) {
|
|
926
|
+
rb_raise(rb_eRuntimeError, "Length exceeds size of buffer!");
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
size_t maximum_size = size - offset;
|
|
930
|
+
while (maximum_size) {
|
|
931
|
+
int result = io_write(selector, fiber, descriptor, (char*)base+offset, maximum_size, from);
|
|
932
|
+
|
|
933
|
+
if (result > 0) {
|
|
934
|
+
total += result;
|
|
935
|
+
offset += result;
|
|
936
|
+
from += result;
|
|
937
|
+
if ((size_t)result >= length) break;
|
|
938
|
+
length -= result;
|
|
939
|
+
} else if (result == 0) {
|
|
940
|
+
break;
|
|
941
|
+
} else if (length > 0 && IO_Event_try_again(-result)) {
|
|
942
|
+
IO_Event_Selector_URing_io_wait(self, fiber, io, RB_INT2NUM(IO_EVENT_WRITABLE));
|
|
943
|
+
} else {
|
|
944
|
+
return rb_fiber_scheduler_io_result(-1, -result);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
maximum_size = size - offset;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
return rb_fiber_scheduler_io_result(total, 0);
|
|
951
|
+
}
|
|
952
|
+
|
|
871
953
|
#endif
|
|
872
954
|
|
|
873
955
|
#pragma mark - IO#close
|
|
@@ -1004,7 +1086,7 @@ unsigned select_process_completions(struct IO_Event_Selector_URing *selector) {
|
|
|
1004
1086
|
if (waiting && waiting->fiber) {
|
|
1005
1087
|
assert(waiting->result != -ECANCELED);
|
|
1006
1088
|
|
|
1007
|
-
|
|
1089
|
+
IO_Event_Selector_loop_resume(&selector->backend, waiting->fiber, 0, NULL);
|
|
1008
1090
|
}
|
|
1009
1091
|
}
|
|
1010
1092
|
|
|
@@ -1023,7 +1105,7 @@ VALUE IO_Event_Selector_URing_select(VALUE self, VALUE duration) {
|
|
|
1023
1105
|
// Flush any pending events:
|
|
1024
1106
|
io_uring_submit_flush(selector);
|
|
1025
1107
|
|
|
1026
|
-
int ready =
|
|
1108
|
+
int ready = IO_Event_Selector_ready_flush(&selector->backend);
|
|
1027
1109
|
|
|
1028
1110
|
int result = select_process_completions(selector);
|
|
1029
1111
|
|
|
@@ -1043,14 +1125,14 @@ VALUE IO_Event_Selector_URing_select(VALUE self, VALUE duration) {
|
|
|
1043
1125
|
|
|
1044
1126
|
if (!selector->backend.ready && !timeout_nonblocking(arguments.timeout)) {
|
|
1045
1127
|
struct timespec start_time;
|
|
1046
|
-
|
|
1128
|
+
IO_Event_Time_current(&start_time);
|
|
1047
1129
|
|
|
1048
1130
|
// This is a blocking operation, we wait for events:
|
|
1049
1131
|
result = select_internal_without_gvl(&arguments);
|
|
1050
1132
|
|
|
1051
1133
|
struct timespec end_time;
|
|
1052
|
-
|
|
1053
|
-
|
|
1134
|
+
IO_Event_Time_current(&end_time);
|
|
1135
|
+
IO_Event_Time_elapsed(&start_time, &end_time, &selector->idle_duration);
|
|
1054
1136
|
|
|
1055
1137
|
// After waiting/flushing the SQ, check if there are any completions:
|
|
1056
1138
|
if (result > 0) {
|
|
@@ -1094,8 +1176,7 @@ VALUE IO_Event_Selector_URing_wakeup(VALUE self) {
|
|
|
1094
1176
|
#pragma mark - Native Methods
|
|
1095
1177
|
|
|
1096
1178
|
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);
|
|
1179
|
+
VALUE IO_Event_Selector_URing = rb_define_class_under(IO_Event_Selector, "URing", rb_cObject);
|
|
1099
1180
|
|
|
1100
1181
|
rb_define_alloc_func(IO_Event_Selector_URing, IO_Event_Selector_URing_allocate);
|
|
1101
1182
|
rb_define_method(IO_Event_Selector_URing, "initialize", IO_Event_Selector_URing_initialize, 1);
|
|
@@ -1120,6 +1201,8 @@ void Init_IO_Event_Selector_URing(VALUE IO_Event_Selector) {
|
|
|
1120
1201
|
#ifdef HAVE_RUBY_IO_BUFFER_H
|
|
1121
1202
|
rb_define_method(IO_Event_Selector_URing, "io_read", IO_Event_Selector_URing_io_read_compatible, -1);
|
|
1122
1203
|
rb_define_method(IO_Event_Selector_URing, "io_write", IO_Event_Selector_URing_io_write_compatible, -1);
|
|
1204
|
+
rb_define_method(IO_Event_Selector_URing, "io_pread", IO_Event_Selector_URing_io_pread, 6);
|
|
1205
|
+
rb_define_method(IO_Event_Selector_URing, "io_pwrite", IO_Event_Selector_URing_io_pwrite, 6);
|
|
1123
1206
|
#endif
|
|
1124
1207
|
|
|
1125
1208
|
rb_define_method(IO_Event_Selector_URing, "io_close", IO_Event_Selector_URing_io_close, 1);
|
|
@@ -1,22 +1,5 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
-
// of this software and associated documentation files (the "Software"), to deal
|
|
5
|
-
// in the Software without restriction, including without limitation the rights
|
|
6
|
-
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
-
// copies of the Software, and to permit persons to whom the Software is
|
|
8
|
-
// furnished to do so, subject to the following conditions:
|
|
9
|
-
//
|
|
10
|
-
// The above copyright notice and this permission notice shall be included in
|
|
11
|
-
// all copies or substantial portions of the Software.
|
|
12
|
-
//
|
|
13
|
-
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
-
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
-
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
-
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
-
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
-
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
19
|
-
// THE SOFTWARE.
|
|
1
|
+
// Released under the MIT License.
|
|
2
|
+
// Copyright, 2021-2025, by Samuel Williams.
|
|
20
3
|
|
|
21
4
|
#pragma once
|
|
22
5
|
|
data/ext/io/event/time.c
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Released under the MIT License.
|
|
2
|
+
// Copyright, 2025, by Samuel Williams.
|
|
3
|
+
|
|
4
|
+
#include "time.h"
|
|
5
|
+
|
|
6
|
+
void IO_Event_Time_elapsed(const struct timespec* start, const struct timespec* stop, struct timespec *duration)
|
|
7
|
+
{
|
|
8
|
+
if ((stop->tv_nsec - start->tv_nsec) < 0) {
|
|
9
|
+
duration->tv_sec = stop->tv_sec - start->tv_sec - 1;
|
|
10
|
+
duration->tv_nsec = stop->tv_nsec - start->tv_nsec + 1000000000;
|
|
11
|
+
} else {
|
|
12
|
+
duration->tv_sec = stop->tv_sec - start->tv_sec;
|
|
13
|
+
duration->tv_nsec = stop->tv_nsec - start->tv_nsec;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
float IO_Event_Time_duration(const struct timespec *duration)
|
|
18
|
+
{
|
|
19
|
+
return duration->tv_sec + duration->tv_nsec / 1000000000.0;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
void IO_Event_Time_current(struct timespec *time) {
|
|
23
|
+
clock_gettime(CLOCK_MONOTONIC, time);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
float IO_Event_Time_proportion(const struct timespec *duration, const struct timespec *total_duration) {
|
|
27
|
+
return IO_Event_Time_duration(duration) / IO_Event_Time_duration(total_duration);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
float IO_Event_Time_delta(const struct timespec *start, const struct timespec *stop) {
|
|
31
|
+
struct timespec duration;
|
|
32
|
+
IO_Event_Time_elapsed(start, stop, &duration);
|
|
33
|
+
|
|
34
|
+
return IO_Event_Time_duration(&duration);
|
|
35
|
+
}
|
data/ext/io/event/time.h
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Released under the MIT License.
|
|
2
|
+
// Copyright, 2025, by Samuel Williams.
|
|
3
|
+
|
|
4
|
+
#pragma once
|
|
5
|
+
|
|
6
|
+
#include <ruby.h>
|
|
7
|
+
#include <time.h>
|
|
8
|
+
|
|
9
|
+
void IO_Event_Time_elapsed(const struct timespec* start, const struct timespec* stop, struct timespec *duration);
|
|
10
|
+
float IO_Event_Time_duration(const struct timespec *duration);
|
|
11
|
+
void IO_Event_Time_current(struct timespec *time);
|
|
12
|
+
|
|
13
|
+
float IO_Event_Time_delta(const struct timespec *start, const struct timespec *stop);
|
|
14
|
+
float IO_Event_Time_proportion(const struct timespec *duration, const struct timespec *total_duration);
|
|
15
|
+
|
|
16
|
+
#define IO_EVENT_TIME_PRINTF_TIMESPEC "%.3g"
|
|
17
|
+
#define IO_EVENT_TIME_PRINTF_TIMESPEC_ARGUMENTS(ts) ((double)(ts).tv_sec + (ts).tv_nsec / 1e9)
|
|
@@ -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.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Released under the MIT License.
|
|
4
|
+
# Copyright, 2025, by Samuel Williams.
|
|
5
|
+
|
|
6
|
+
begin
|
|
7
|
+
require "IO_Event"
|
|
8
|
+
rescue LoadError => error
|
|
9
|
+
warn "Could not load native event selector: #{error}"
|
|
10
|
+
require_relative "selector/nonblock"
|
|
11
|
+
end
|