io-event 1.19.4 → 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 +31 -5
- 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/ext/io/event/worker_pool.c +45 -34
- 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 +8 -8
- data/releases.md +8 -0
- data.tar.gz.sig +0 -0
- metadata +1 -1
- metadata.gz.sig +0 -0
|
@@ -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
|
data/ext/io/event/worker_pool.c
CHANGED
|
@@ -306,15 +306,54 @@ static VALUE worker_pool_initialize(int argc, VALUE *argv, VALUE self) {
|
|
|
306
306
|
return self;
|
|
307
307
|
}
|
|
308
308
|
|
|
309
|
-
static VALUE
|
|
309
|
+
static VALUE worker_pool_work_block(VALUE _work) {
|
|
310
310
|
struct IO_Event_WorkerPool_Work *work = (void*)_work;
|
|
311
311
|
|
|
312
|
-
if (DEBUG) fprintf(stderr, "
|
|
312
|
+
if (DEBUG) fprintf(stderr, "worker_pool_work_block:rb_fiber_scheduler_block work=%p\n", work);
|
|
313
313
|
rb_fiber_scheduler_block(work->scheduler, work->blocker, Qnil);
|
|
314
314
|
|
|
315
315
|
return Qnil;
|
|
316
316
|
}
|
|
317
317
|
|
|
318
|
+
static VALUE worker_pool_work_wait(VALUE _work) {
|
|
319
|
+
struct IO_Event_WorkerPool_Work *work = (void*)_work;
|
|
320
|
+
|
|
321
|
+
while (true) {
|
|
322
|
+
worker_pool_work_block(_work);
|
|
323
|
+
if (DEBUG) fprintf(stderr, "-- worker_pool_work_wait:work completed=%d\n", work->completed);
|
|
324
|
+
|
|
325
|
+
if (work->completed) {
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (DEBUG) fprintf(stderr, "worker_pool_work_wait:rb_fiber_scheduler_blocking_operation_cancel\n");
|
|
330
|
+
rb_fiber_scheduler_blocking_operation_cancel(work->blocking_operation);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return Qtrue;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
static VALUE worker_pool_work_ensure(VALUE _work) {
|
|
337
|
+
struct IO_Event_WorkerPool_Work *work = (void*)_work;
|
|
338
|
+
|
|
339
|
+
while (!work->completed) {
|
|
340
|
+
if (DEBUG) fprintf(stderr, "worker_pool_work_ensure:rb_fiber_scheduler_blocking_operation_cancel\n");
|
|
341
|
+
rb_fiber_scheduler_blocking_operation_cancel(work->blocking_operation);
|
|
342
|
+
|
|
343
|
+
int state = 0;
|
|
344
|
+
rb_protect(worker_pool_work_block, _work, &state);
|
|
345
|
+
if (DEBUG) fprintf(stderr, "-- worker_pool_work_ensure:work completed=%d, state=%d\n", work->completed, state);
|
|
346
|
+
|
|
347
|
+
if (state) {
|
|
348
|
+
// Ignore errors raised while waiting for cancellation to complete. rb_ensure
|
|
349
|
+
// will restore and rethrow the original control-flow state after this returns.
|
|
350
|
+
rb_set_errinfo(Qnil);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return Qnil;
|
|
355
|
+
}
|
|
356
|
+
|
|
318
357
|
// Ruby method to submit work and wait for completion
|
|
319
358
|
static VALUE worker_pool_call(VALUE self, VALUE _blocking_operation) {
|
|
320
359
|
struct IO_Event_WorkerPool *pool;
|
|
@@ -357,38 +396,10 @@ static VALUE worker_pool_call(VALUE self, VALUE _blocking_operation) {
|
|
|
357
396
|
pthread_cond_signal(&pool->work_available);
|
|
358
397
|
pthread_mutex_unlock(&pool->mutex);
|
|
359
398
|
|
|
360
|
-
// Block the current fiber until work is completed
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
rb_protect(worker_pool_work_begin, (VALUE)&work, ¤t_state);
|
|
365
|
-
if (DEBUG) fprintf(stderr, "-- worker_pool_call:work completed=%d, current_state=%d, state=%d\n", work.completed, current_state, state);
|
|
366
|
-
|
|
367
|
-
// Store the first exception state:
|
|
368
|
-
if (!state) {
|
|
369
|
-
state = current_state;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// If the work is still in the queue, we must wait for a worker to complete it (even if cancelled):
|
|
373
|
-
if (work.completed) {
|
|
374
|
-
// The work was completed, we can exit the loop:
|
|
375
|
-
break;
|
|
376
|
-
} else {
|
|
377
|
-
if (DEBUG) fprintf(stderr, "worker_pool_call:rb_fiber_scheduler_blocking_operation_cancel\n");
|
|
378
|
-
// Ensure the blocking operation is cancelled:
|
|
379
|
-
rb_fiber_scheduler_blocking_operation_cancel(blocking_operation);
|
|
380
|
-
|
|
381
|
-
// The work was not completed, we need to wait for it to be completed, so we go around the loop again.
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
if (DEBUG) fprintf(stderr, "<- worker_pool_call:work completed=%d, state=%d\n", work.completed, state);
|
|
386
|
-
|
|
387
|
-
if (state) {
|
|
388
|
-
rb_jump_tag(state);
|
|
389
|
-
} else {
|
|
390
|
-
return Qtrue;
|
|
391
|
-
}
|
|
399
|
+
// Block the current fiber until work is completed. If blocking exits via an
|
|
400
|
+
// exception or another non-local jump, ensure the work is cancelled and fully
|
|
401
|
+
// drained before Ruby restores and rethrows the original control-flow state.
|
|
402
|
+
return rb_ensure(worker_pool_work_wait, (VALUE)&work, worker_pool_work_ensure, (VALUE)&work);
|
|
392
403
|
}
|
|
393
404
|
|
|
394
405
|
static VALUE worker_pool_allocate(VALUE klass) {
|
|
@@ -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.
|