io-event 1.16.2 → 1.21.1
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/extconf.rb +4 -4
- data/ext/io/event/list.h +1 -1
- data/ext/io/event/selector/epoll.c +196 -97
- data/ext/io/event/selector/kqueue.c +195 -101
- data/ext/io/event/selector/selector.c +77 -1
- data/ext/io/event/selector/selector.h +24 -2
- data/ext/io/event/selector/uring.c +508 -88
- data/ext/io/event/worker_pool.c +45 -34
- data/lib/io/event/debug/selector.rb +13 -6
- data/lib/io/event/interrupt.rb +12 -7
- data/lib/io/event/selector/select.rb +108 -58
- data/lib/io/event/selector.rb +24 -9
- data/lib/io/event/timers.rb +1 -1
- data/lib/io/event/version.rb +1 -1
- data/license.md +1 -0
- data/readme.md +22 -26
- data/releases.md +50 -0
- data.tar.gz.sig +0 -0
- metadata +3 -2
- metadata.gz.sig +0 -0
|
@@ -11,13 +11,21 @@
|
|
|
11
11
|
#include <stdbool.h>
|
|
12
12
|
#include <stdint.h>
|
|
13
13
|
#include <time.h>
|
|
14
|
+
#include <unistd.h>
|
|
14
15
|
|
|
15
16
|
#include "../interrupt.h"
|
|
16
17
|
|
|
17
|
-
#include "pidfd.c"
|
|
18
|
-
|
|
19
18
|
#include <linux/version.h>
|
|
20
19
|
|
|
20
|
+
// `io_uring` support for `IORING_OP_WAITID` was introduced in Linux 6.7. When available, we use it to wait for process exit directly in the ring, instead of polling on a pidfd.
|
|
21
|
+
#if defined(HAVE_IO_URING_PREP_WAITID) && (LINUX_VERSION_CODE >= KERNEL_VERSION(6,7,0))
|
|
22
|
+
#define IO_EVENT_SELECTOR_URING_USE_WAITID
|
|
23
|
+
#endif
|
|
24
|
+
|
|
25
|
+
#ifndef IO_EVENT_SELECTOR_URING_USE_WAITID
|
|
26
|
+
#include "pidfd.c"
|
|
27
|
+
#endif
|
|
28
|
+
|
|
21
29
|
enum {
|
|
22
30
|
DEBUG = 0,
|
|
23
31
|
DEBUG_COMPLETION = 0,
|
|
@@ -32,10 +40,7 @@ struct IO_Event_Selector_URing
|
|
|
32
40
|
{
|
|
33
41
|
struct IO_Event_Selector backend;
|
|
34
42
|
struct io_uring ring;
|
|
35
|
-
|
|
36
|
-
// Flag indicating whether the selector is currently blocked in a system call.
|
|
37
|
-
// Set to 1 when blocked in io_uring_wait_cqe_timeout() without GVL, 0 otherwise.
|
|
38
|
-
int blocked;
|
|
43
|
+
pid_t owner;
|
|
39
44
|
|
|
40
45
|
// Interrupt used to wake the selector from another thread without touching the ring's SQ.
|
|
41
46
|
// This allows IORING_SETUP_SINGLE_ISSUER: only the owner thread ever submits SQEs.
|
|
@@ -76,8 +81,27 @@ struct IO_Event_Selector_URing_Completion
|
|
|
76
81
|
struct IO_Event_List list;
|
|
77
82
|
|
|
78
83
|
struct IO_Event_Selector_URing_Waiting *waiting;
|
|
84
|
+
bool operation_pending;
|
|
85
|
+
bool cancellation_pending;
|
|
79
86
|
};
|
|
80
87
|
|
|
88
|
+
// Cancellation completions use the low bit to distinguish them from the
|
|
89
|
+
// completion of the operation being cancelled. Completion records are aligned,
|
|
90
|
+
// so the low bit is otherwise always clear.
|
|
91
|
+
#define IO_EVENT_SELECTOR_URING_CANCELLATION_TAG ((uint64_t)1)
|
|
92
|
+
|
|
93
|
+
static inline uint64_t
|
|
94
|
+
IO_Event_Selector_URing_Completion_cancellation_data(struct IO_Event_Selector_URing_Completion *completion)
|
|
95
|
+
{
|
|
96
|
+
return ((uint64_t)(uintptr_t)completion) | IO_EVENT_SELECTOR_URING_CANCELLATION_TAG;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
static inline struct IO_Event_Selector_URing_Completion *
|
|
100
|
+
IO_Event_Selector_URing_Completion_from_cancellation_data(uint64_t data)
|
|
101
|
+
{
|
|
102
|
+
return (struct IO_Event_Selector_URing_Completion *)(uintptr_t)(data & ~IO_EVENT_SELECTOR_URING_CANCELLATION_TAG);
|
|
103
|
+
}
|
|
104
|
+
|
|
81
105
|
static
|
|
82
106
|
void IO_Event_Selector_URing_Completion_mark(void *_completion)
|
|
83
107
|
{
|
|
@@ -115,14 +139,20 @@ void IO_Event_Selector_URing_Type_compact(void *_selector)
|
|
|
115
139
|
static
|
|
116
140
|
void close_internal(struct IO_Event_Selector_URing *selector)
|
|
117
141
|
{
|
|
118
|
-
if (selector->
|
|
119
|
-
|
|
142
|
+
if (selector->owner == getpid()) {
|
|
143
|
+
if (selector->interrupt.descriptor >= 0) {
|
|
144
|
+
IO_Event_Interrupt_close(&selector->interrupt);
|
|
145
|
+
selector->interrupt.descriptor = -1;
|
|
146
|
+
selector->wakeup_registered = 0;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (selector->ring.ring_fd >= 0) {
|
|
150
|
+
io_uring_queue_exit(&selector->ring);
|
|
151
|
+
selector->ring.ring_fd = -1;
|
|
152
|
+
}
|
|
153
|
+
} else {
|
|
120
154
|
selector->interrupt.descriptor = -1;
|
|
121
155
|
selector->wakeup_registered = 0;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (selector->ring.ring_fd >= 0) {
|
|
125
|
-
io_uring_queue_exit(&selector->ring);
|
|
126
156
|
selector->ring.ring_fd = -1;
|
|
127
157
|
}
|
|
128
158
|
}
|
|
@@ -177,8 +207,13 @@ struct IO_Event_Selector_URing_Completion * IO_Event_Selector_URing_Completion_a
|
|
|
177
207
|
|
|
178
208
|
if (DEBUG_COMPLETION) fprintf(stderr, "IO_Event_Selector_URing_Completion_acquire(%p, limit=%ld)\n", (void*)completion, selector->completions.limit);
|
|
179
209
|
|
|
210
|
+
assert(completion->waiting == NULL);
|
|
211
|
+
assert(!completion->operation_pending);
|
|
212
|
+
assert(!completion->cancellation_pending);
|
|
213
|
+
|
|
180
214
|
waiting->completion = completion;
|
|
181
215
|
completion->waiting = waiting;
|
|
216
|
+
completion->operation_pending = true;
|
|
182
217
|
|
|
183
218
|
return completion;
|
|
184
219
|
}
|
|
@@ -195,14 +230,45 @@ void IO_Event_Selector_URing_Completion_cancel(struct IO_Event_Selector_URing_Co
|
|
|
195
230
|
}
|
|
196
231
|
|
|
197
232
|
inline static
|
|
198
|
-
void
|
|
233
|
+
void IO_Event_Selector_URing_Completion_recycle(struct IO_Event_Selector_URing *selector, struct IO_Event_Selector_URing_Completion *completion)
|
|
199
234
|
{
|
|
200
|
-
|
|
235
|
+
assert(completion->waiting == NULL);
|
|
236
|
+
assert(!completion->operation_pending);
|
|
237
|
+
assert(!completion->cancellation_pending);
|
|
201
238
|
|
|
202
|
-
IO_Event_Selector_URing_Completion_cancel(completion);
|
|
203
239
|
IO_Event_List_prepend(&selector->free_list, &completion->list);
|
|
204
240
|
}
|
|
205
241
|
|
|
242
|
+
inline static
|
|
243
|
+
void IO_Event_Selector_URing_Completion_complete(struct IO_Event_Selector_URing *selector, struct IO_Event_Selector_URing_Completion *completion)
|
|
244
|
+
{
|
|
245
|
+
if (DEBUG_COMPLETION) fprintf(stderr, "IO_Event_Selector_URing_Completion_complete(%p)\n", (void*)completion);
|
|
246
|
+
|
|
247
|
+
assert(completion->operation_pending);
|
|
248
|
+
completion->operation_pending = false;
|
|
249
|
+
|
|
250
|
+
IO_Event_Selector_URing_Completion_cancel(completion);
|
|
251
|
+
|
|
252
|
+
// A cancellation SQE still refers to this completion record. Keep it out of
|
|
253
|
+
// the free list until that CQE has also been observed, avoiding ABA reuse.
|
|
254
|
+
if (!completion->cancellation_pending) {
|
|
255
|
+
IO_Event_Selector_URing_Completion_recycle(selector, completion);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
inline static
|
|
260
|
+
void IO_Event_Selector_URing_Completion_cancellation_complete(struct IO_Event_Selector_URing *selector, struct IO_Event_Selector_URing_Completion *completion)
|
|
261
|
+
{
|
|
262
|
+
if (DEBUG_COMPLETION) fprintf(stderr, "IO_Event_Selector_URing_Completion_cancellation_complete(%p)\n", (void*)completion);
|
|
263
|
+
|
|
264
|
+
assert(completion->cancellation_pending);
|
|
265
|
+
completion->cancellation_pending = false;
|
|
266
|
+
|
|
267
|
+
if (!completion->operation_pending) {
|
|
268
|
+
IO_Event_Selector_URing_Completion_recycle(selector, completion);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
206
272
|
inline static
|
|
207
273
|
void IO_Event_Selector_URing_Waiting_cancel(struct IO_Event_Selector_URing_Waiting *waiting)
|
|
208
274
|
{
|
|
@@ -223,6 +289,9 @@ void IO_Event_Selector_URing_Completion_initialize(void *element)
|
|
|
223
289
|
struct IO_Event_Selector_URing_Completion *completion = element;
|
|
224
290
|
IO_Event_List_initialize(&completion->list);
|
|
225
291
|
completion->list.type = &IO_Event_Selector_URing_Completion_Type;
|
|
292
|
+
completion->waiting = NULL;
|
|
293
|
+
completion->operation_pending = false;
|
|
294
|
+
completion->cancellation_pending = false;
|
|
226
295
|
}
|
|
227
296
|
|
|
228
297
|
void IO_Event_Selector_URing_Completion_free(void *element)
|
|
@@ -237,8 +306,8 @@ VALUE IO_Event_Selector_URing_allocate(VALUE self) {
|
|
|
237
306
|
|
|
238
307
|
IO_Event_Selector_initialize(&selector->backend, self, Qnil);
|
|
239
308
|
selector->ring.ring_fd = -1;
|
|
309
|
+
selector->owner = 0;
|
|
240
310
|
|
|
241
|
-
selector->blocked = 0;
|
|
242
311
|
selector->interrupt.descriptor = -1;
|
|
243
312
|
selector->wakeup_registered = 0;
|
|
244
313
|
|
|
@@ -299,6 +368,8 @@ VALUE IO_Event_Selector_URing_initialize(VALUE self, VALUE loop) {
|
|
|
299
368
|
rb_syserr_fail(-result, "IO_Event_Selector_URing_initialize:io_uring_queue_init");
|
|
300
369
|
}
|
|
301
370
|
|
|
371
|
+
selector->owner = getpid();
|
|
372
|
+
|
|
302
373
|
rb_update_max_fd(selector->ring.ring_fd);
|
|
303
374
|
|
|
304
375
|
// Interrupt for cross-thread wakeup: another thread calls signal(); the owner
|
|
@@ -339,6 +410,13 @@ VALUE IO_Event_Selector_URing_close(VALUE self) {
|
|
|
339
410
|
return Qnil;
|
|
340
411
|
}
|
|
341
412
|
|
|
413
|
+
VALUE IO_Event_Selector_URing_closed_p(VALUE self) {
|
|
414
|
+
struct IO_Event_Selector_URing *selector = NULL;
|
|
415
|
+
TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
|
|
416
|
+
|
|
417
|
+
return selector->ring.ring_fd < 0 || selector->owner != getpid() ? Qtrue : Qfalse;
|
|
418
|
+
}
|
|
419
|
+
|
|
342
420
|
VALUE IO_Event_Selector_URing_transfer(VALUE self)
|
|
343
421
|
{
|
|
344
422
|
struct IO_Event_Selector_URing *selector = NULL;
|
|
@@ -480,37 +558,185 @@ struct io_uring_sqe * io_get_sqe(struct IO_Event_Selector_URing *selector) {
|
|
|
480
558
|
return sqe;
|
|
481
559
|
}
|
|
482
560
|
|
|
561
|
+
static
|
|
562
|
+
void IO_Event_Selector_URing_Completion_cancel_async(struct IO_Event_Selector_URing *selector, struct IO_Event_Selector_URing_Completion *completion)
|
|
563
|
+
{
|
|
564
|
+
if (completion->cancellation_pending) return;
|
|
565
|
+
|
|
566
|
+
completion->cancellation_pending = true;
|
|
567
|
+
|
|
568
|
+
struct io_uring_sqe *sqe = io_get_sqe(selector);
|
|
569
|
+
io_uring_prep_cancel(sqe, completion, 0);
|
|
570
|
+
io_uring_sqe_set_data64(sqe, IO_Event_Selector_URing_Completion_cancellation_data(completion));
|
|
571
|
+
io_uring_submit_pending(selector);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
static
|
|
575
|
+
VALUE IO_Event_Selector_URing_Waiting_wait_yield(VALUE _selector)
|
|
576
|
+
{
|
|
577
|
+
struct IO_Event_Selector_URing *selector = (struct IO_Event_Selector_URing *)_selector;
|
|
578
|
+
|
|
579
|
+
return IO_Event_Selector_loop_yield(&selector->backend);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
static
|
|
583
|
+
void IO_Event_Selector_URing_Waiting_cancel_and_wait(struct IO_Event_Selector_URing *selector, struct IO_Event_Selector_URing_Waiting *waiting)
|
|
584
|
+
{
|
|
585
|
+
int state = 0;
|
|
586
|
+
VALUE error = Qnil;
|
|
587
|
+
|
|
588
|
+
if (waiting->completion) {
|
|
589
|
+
IO_Event_Selector_URing_Completion_cancel_async(selector, waiting->completion);
|
|
590
|
+
|
|
591
|
+
// The kernel may still be reading from or writing to the supplied buffer.
|
|
592
|
+
// Keep the C frame, buffer lock and completion record alive until the
|
|
593
|
+
// original operation CQE confirms that it can no longer access memory.
|
|
594
|
+
while (waiting->completion) {
|
|
595
|
+
int current_state = 0;
|
|
596
|
+
rb_protect(IO_Event_Selector_URing_Waiting_wait_yield, (VALUE)selector, ¤t_state);
|
|
597
|
+
|
|
598
|
+
if (current_state && !state) {
|
|
599
|
+
state = current_state;
|
|
600
|
+
error = rb_errinfo();
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
rb_set_errinfo(Qnil);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
IO_Event_Selector_URing_Waiting_cancel(waiting);
|
|
608
|
+
|
|
609
|
+
if (state) {
|
|
610
|
+
rb_set_errinfo(error);
|
|
611
|
+
rb_jump_tag(state);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
483
615
|
#pragma mark - Process.wait
|
|
484
616
|
|
|
617
|
+
#ifdef IO_EVENT_SELECTOR_URING_USE_WAITID
|
|
618
|
+
// Translate a Ruby/`waitpid`-style pid into the `waitid(2)` idtype and id, mirroring the semantics of `waitpid(2)`:
|
|
619
|
+
//
|
|
620
|
+
// pid == -1 -> any child (P_ALL)
|
|
621
|
+
// pid == 0 -> any child in the caller's process group (P_PGID, id 0; Linux >= 5.4)
|
|
622
|
+
// pid < -1 -> any child in process group |pid| (P_PGID)
|
|
623
|
+
// pid > 0 -> the specific child (P_PID)
|
|
624
|
+
//
|
|
625
|
+
static inline idtype_t process_waitid_type(pid_t pid, id_t *id) {
|
|
626
|
+
if (pid == -1) {
|
|
627
|
+
*id = 0;
|
|
628
|
+
return P_ALL;
|
|
629
|
+
} else if (pid == 0) {
|
|
630
|
+
*id = 0;
|
|
631
|
+
return P_PGID;
|
|
632
|
+
} else if (pid < -1) {
|
|
633
|
+
*id = (id_t)(-pid);
|
|
634
|
+
return P_PGID;
|
|
635
|
+
} else {
|
|
636
|
+
*id = (id_t)pid;
|
|
637
|
+
return P_PID;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
#endif
|
|
641
|
+
|
|
485
642
|
struct process_wait_arguments {
|
|
486
643
|
struct IO_Event_Selector_URing *selector;
|
|
487
644
|
struct IO_Event_Selector_URing_Waiting *waiting;
|
|
488
645
|
|
|
489
646
|
pid_t pid;
|
|
490
647
|
int flags;
|
|
648
|
+
|
|
649
|
+
#ifdef IO_EVENT_SELECTOR_URING_USE_WAITID
|
|
650
|
+
siginfo_t siginfo;
|
|
651
|
+
#else
|
|
491
652
|
int descriptor;
|
|
653
|
+
#endif
|
|
492
654
|
};
|
|
493
655
|
|
|
656
|
+
#if defined(IO_EVENT_SELECTOR_URING_USE_WAITID) && defined(HAVE_RB_PROCESS_STATUS_FOR)
|
|
657
|
+
static inline int process_wait_status_exited(int exit_status) {
|
|
658
|
+
return (exit_status & 0xff) << 8;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
static inline int process_wait_status_signaled(int term_signal) {
|
|
662
|
+
return term_signal & 0x7f;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
static inline int process_wait_status_from_siginfo(const siginfo_t *siginfo) {
|
|
666
|
+
switch (siginfo->si_code) {
|
|
667
|
+
case CLD_EXITED:
|
|
668
|
+
return process_wait_status_exited(siginfo->si_status);
|
|
669
|
+
case CLD_KILLED:
|
|
670
|
+
return process_wait_status_signaled(siginfo->si_status);
|
|
671
|
+
case CLD_DUMPED:
|
|
672
|
+
return process_wait_status_signaled(siginfo->si_status) | 0x80;
|
|
673
|
+
default:
|
|
674
|
+
return 0;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
#endif
|
|
678
|
+
|
|
494
679
|
static
|
|
495
680
|
VALUE process_wait_transfer(VALUE _arguments) {
|
|
496
681
|
struct process_wait_arguments *arguments = (struct process_wait_arguments *)_arguments;
|
|
497
682
|
|
|
683
|
+
#ifdef IO_EVENT_SELECTOR_URING_USE_WAITID
|
|
684
|
+
IO_Event_Selector_loop_yield(&arguments->selector->backend);
|
|
685
|
+
|
|
686
|
+
if (arguments->waiting->completion) {
|
|
687
|
+
// The kernel may still write to siginfo or reap the child. Keep the C frame alive until the original operation completes, then distinguish cancellation from process completion:
|
|
688
|
+
IO_Event_Selector_URing_Waiting_cancel_and_wait(arguments->selector, arguments->waiting);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
int32_t result = arguments->waiting->result;
|
|
692
|
+
if (result == -ECANCELED) {
|
|
693
|
+
return Qfalse;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
if (DEBUG) fprintf(stderr, "waitid result=%d pid=%d code=%d status=%d\n", result, arguments->siginfo.si_pid, arguments->siginfo.si_code, arguments->siginfo.si_status);
|
|
697
|
+
|
|
698
|
+
if (result < 0) {
|
|
699
|
+
// The `waitid` failed (e.g. `ECHILD` when there are no children). Reproduce the failure as a `Process::Status` carrying the error, rather than raising, so callers like `Process.waitall` / `Process.detach` (which expect `waitpid` to report the error, not raise) behave correctly:
|
|
700
|
+
#ifdef HAVE_RB_PROCESS_STATUS_FOR
|
|
701
|
+
return rb_process_status_for(-1, 0, -result);
|
|
702
|
+
#else
|
|
703
|
+
return IO_Event_Selector_process_status_reap(arguments->pid, arguments->flags);
|
|
704
|
+
#endif
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
#ifdef HAVE_RB_PROCESS_STATUS_FOR
|
|
708
|
+
// The `waitid` operation already reaped the child. Convert the `siginfo_t` result directly into the Ruby process status value:
|
|
709
|
+
return rb_process_status_for(arguments->siginfo.si_pid, process_wait_status_from_siginfo(&arguments->siginfo), 0);
|
|
710
|
+
#else
|
|
711
|
+
// We waited with `WNOWAIT`, so the child has not been reaped yet. `si_pid` tells us exactly which child changed state (important when waiting for any child, e.g. pid -1). Reap it to obtain a correct `Process::Status`:
|
|
712
|
+
return IO_Event_Selector_process_status_reap(arguments->siginfo.si_pid, arguments->flags);
|
|
713
|
+
#endif
|
|
714
|
+
#else
|
|
498
715
|
IO_Event_Selector_loop_yield(&arguments->selector->backend);
|
|
499
716
|
|
|
500
717
|
if (arguments->waiting->result) {
|
|
501
|
-
return
|
|
718
|
+
return IO_Event_Selector_process_status_reap(arguments->pid, arguments->flags);
|
|
502
719
|
} else {
|
|
503
720
|
return Qfalse;
|
|
504
721
|
}
|
|
722
|
+
#endif
|
|
505
723
|
}
|
|
506
724
|
|
|
507
725
|
static
|
|
508
726
|
VALUE process_wait_ensure(VALUE _arguments) {
|
|
509
727
|
struct process_wait_arguments *arguments = (struct process_wait_arguments *)_arguments;
|
|
510
728
|
|
|
511
|
-
|
|
729
|
+
#ifdef IO_EVENT_SELECTOR_URING_USE_WAITID
|
|
730
|
+
// `waitid` may write to stack-backed siginfo while cancellation is pending:
|
|
731
|
+
IO_Event_Selector_URing_Waiting_cancel_and_wait(arguments->selector, arguments->waiting);
|
|
732
|
+
#else
|
|
733
|
+
if (arguments->waiting->completion) {
|
|
734
|
+
IO_Event_Selector_URing_Completion_cancel_async(arguments->selector, arguments->waiting->completion);
|
|
735
|
+
}
|
|
512
736
|
|
|
737
|
+
close(arguments->descriptor);
|
|
513
738
|
IO_Event_Selector_URing_Waiting_cancel(arguments->waiting);
|
|
739
|
+
#endif
|
|
514
740
|
|
|
515
741
|
return Qnil;
|
|
516
742
|
}
|
|
@@ -522,11 +748,18 @@ VALUE IO_Event_Selector_URing_process_wait(VALUE self, VALUE fiber, VALUE _pid,
|
|
|
522
748
|
pid_t pid = NUM2PIDT(_pid);
|
|
523
749
|
int flags = NUM2INT(_flags);
|
|
524
750
|
|
|
751
|
+
#ifndef IO_EVENT_SELECTOR_URING_USE_WAITID
|
|
752
|
+
// `pidfd_open` can only refer to a specific process, so waiting for any child or a process group (pid <= 0) is delegated to the threaded fallback:
|
|
753
|
+
if (pid <= 0) {
|
|
754
|
+
return IO_Event_Selector_process_wait(pid, flags);
|
|
755
|
+
}
|
|
756
|
+
|
|
525
757
|
int descriptor = pidfd_open(pid, 0);
|
|
526
758
|
if (descriptor < 0) {
|
|
527
759
|
rb_syserr_fail(errno, "IO_Event_Selector_URing_process_wait:pidfd_open");
|
|
528
760
|
}
|
|
529
761
|
rb_update_max_fd(descriptor);
|
|
762
|
+
#endif
|
|
530
763
|
|
|
531
764
|
struct IO_Event_Selector_URing_Waiting waiting = {
|
|
532
765
|
.fiber = fiber,
|
|
@@ -541,12 +774,30 @@ VALUE IO_Event_Selector_URing_process_wait(VALUE self, VALUE fiber, VALUE _pid,
|
|
|
541
774
|
.waiting = &waiting,
|
|
542
775
|
.pid = pid,
|
|
543
776
|
.flags = flags,
|
|
777
|
+
#ifdef IO_EVENT_SELECTOR_URING_USE_WAITID
|
|
778
|
+
.siginfo = {0},
|
|
779
|
+
#else
|
|
544
780
|
.descriptor = descriptor,
|
|
781
|
+
#endif
|
|
545
782
|
};
|
|
546
783
|
|
|
547
|
-
if (DEBUG) fprintf(stderr, "IO_Event_Selector_URing_process_wait:io_uring_prep_poll_add(%p)\n", (void*)fiber);
|
|
548
784
|
struct io_uring_sqe *sqe = io_get_sqe(selector);
|
|
785
|
+
|
|
786
|
+
#ifdef IO_EVENT_SELECTOR_URING_USE_WAITID
|
|
787
|
+
id_t id;
|
|
788
|
+
idtype_t idtype = process_waitid_type(pid, &id);
|
|
789
|
+
if (DEBUG) fprintf(stderr, "IO_Event_Selector_URing_process_wait:io_uring_prep_waitid(fiber=%p, idtype=%d, id=%d, flags=%d)\n", (void*)fiber, idtype, (int)id, flags);
|
|
790
|
+
#ifdef HAVE_RB_PROCESS_STATUS_FOR
|
|
791
|
+
// Reap the child directly; the completion contains enough information to construct the Ruby process status value:
|
|
792
|
+
io_uring_prep_waitid(sqe, idtype, id, &process_wait_arguments.siginfo, WEXITED, 0);
|
|
793
|
+
#else
|
|
794
|
+
// `WNOWAIT` leaves the child in a waitable state so we can reap it with `rb_process_status_wait` afterwards and build a correct `Process::Status`:
|
|
795
|
+
io_uring_prep_waitid(sqe, idtype, id, &process_wait_arguments.siginfo, WEXITED | WNOWAIT, 0);
|
|
796
|
+
#endif
|
|
797
|
+
#else
|
|
798
|
+
if (DEBUG) fprintf(stderr, "IO_Event_Selector_URing_process_wait:io_uring_prep_poll_add(%p)\n", (void*)fiber);
|
|
549
799
|
io_uring_prep_poll_add(sqe, descriptor, POLLIN|POLLHUP|POLLERR);
|
|
800
|
+
#endif
|
|
550
801
|
io_uring_sqe_set_data(sqe, completion);
|
|
551
802
|
io_uring_submit_pending(selector);
|
|
552
803
|
|
|
@@ -591,13 +842,8 @@ static
|
|
|
591
842
|
VALUE io_wait_ensure(VALUE _arguments) {
|
|
592
843
|
struct io_wait_arguments *arguments = (struct io_wait_arguments *)_arguments;
|
|
593
844
|
|
|
594
|
-
// If the operation is still in progress, cancel it:
|
|
595
845
|
if (arguments->waiting->completion) {
|
|
596
|
-
|
|
597
|
-
struct io_uring_sqe *sqe = io_get_sqe(arguments->selector);
|
|
598
|
-
io_uring_prep_cancel(sqe, (void*)arguments->waiting->completion, 0);
|
|
599
|
-
io_uring_sqe_set_data(sqe, NULL);
|
|
600
|
-
io_uring_submit_now(arguments->selector);
|
|
846
|
+
IO_Event_Selector_URing_Completion_cancel_async(arguments->selector, arguments->waiting->completion);
|
|
601
847
|
}
|
|
602
848
|
|
|
603
849
|
IO_Event_Selector_URing_Waiting_cancel(arguments->waiting);
|
|
@@ -701,7 +947,7 @@ io_read_submit(VALUE _arguments)
|
|
|
701
947
|
struct io_uring_sqe *sqe = io_get_sqe(selector);
|
|
702
948
|
io_uring_prep_read(sqe, arguments->descriptor, arguments->buffer, arguments->length, arguments->offset);
|
|
703
949
|
io_uring_sqe_set_data(sqe, arguments->waiting->completion);
|
|
704
|
-
|
|
950
|
+
io_uring_submit_pending(selector);
|
|
705
951
|
|
|
706
952
|
IO_Event_Selector_loop_yield(&selector->backend);
|
|
707
953
|
|
|
@@ -714,16 +960,7 @@ io_read_ensure(VALUE _arguments)
|
|
|
714
960
|
struct io_read_arguments *arguments = (struct io_read_arguments *)_arguments;
|
|
715
961
|
struct IO_Event_Selector_URing *selector = arguments->selector;
|
|
716
962
|
|
|
717
|
-
|
|
718
|
-
if (arguments->waiting->completion) {
|
|
719
|
-
if (DEBUG) fprintf(stderr, "io_read_ensure:io_uring_prep_cancel(waiting=%p, completion=%p)\n", (void*)arguments->waiting, (void*)arguments->waiting->completion);
|
|
720
|
-
struct io_uring_sqe *sqe = io_get_sqe(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(selector);
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
IO_Event_Selector_URing_Waiting_cancel(arguments->waiting);
|
|
963
|
+
IO_Event_Selector_URing_Waiting_cancel_and_wait(selector, arguments->waiting);
|
|
727
964
|
|
|
728
965
|
return Qnil;
|
|
729
966
|
}
|
|
@@ -753,26 +990,97 @@ io_read(struct IO_Event_Selector_URing *selector, VALUE fiber, int descriptor, c
|
|
|
753
990
|
);
|
|
754
991
|
}
|
|
755
992
|
|
|
756
|
-
|
|
993
|
+
#if RUBY_FIBER_SCHEDULER_VERSION >= 4
|
|
994
|
+
struct io_read_locked_arguments {
|
|
995
|
+
struct IO_Event_Selector_URing *selector;
|
|
996
|
+
VALUE fiber;
|
|
997
|
+
VALUE io;
|
|
998
|
+
off_t from;
|
|
999
|
+
size_t offset;
|
|
1000
|
+
size_t length;
|
|
1001
|
+
bool positional;
|
|
1002
|
+
};
|
|
1003
|
+
|
|
1004
|
+
static VALUE
|
|
1005
|
+
io_read_locked(void *base, size_t size, VALUE _arguments)
|
|
1006
|
+
{
|
|
1007
|
+
struct io_read_locked_arguments *arguments = (struct io_read_locked_arguments *)_arguments;
|
|
1008
|
+
|
|
1009
|
+
if (!IO_Event_Selector_valid_buffer_range(size, arguments->offset, arguments->length)) {
|
|
1010
|
+
return rb_fiber_scheduler_io_result(-1, EINVAL);
|
|
1011
|
+
} else if (arguments->length == 0) {
|
|
1012
|
+
return rb_fiber_scheduler_io_result(0, 0);
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
int descriptor = IO_Event_Selector_io_descriptor(arguments->io);
|
|
1016
|
+
char *buffer = (char*)base + arguments->offset;
|
|
1017
|
+
|
|
1018
|
+
// Avoid the submission and suspension overhead when the operation can
|
|
1019
|
+
// complete immediately. The descriptor must be non-blocking for this
|
|
1020
|
+
// optimistic syscall; if it would block, restore its original mode and
|
|
1021
|
+
// submit the operation to io_uring instead.
|
|
1022
|
+
int flags = IO_Event_Selector_nonblock_set(descriptor);
|
|
1023
|
+
ssize_t result;
|
|
1024
|
+
|
|
1025
|
+
if (arguments->positional) {
|
|
1026
|
+
result = pread(descriptor, buffer, arguments->length, arguments->from);
|
|
1027
|
+
} else {
|
|
1028
|
+
result = read(descriptor, buffer, arguments->length);
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
int error = errno;
|
|
1032
|
+
IO_Event_Selector_nonblock_restore(descriptor, flags);
|
|
1033
|
+
|
|
1034
|
+
if (result >= 0) {
|
|
1035
|
+
return rb_fiber_scheduler_io_result(result, 0);
|
|
1036
|
+
} else if (!IO_Event_try_again(error)) {
|
|
1037
|
+
return rb_fiber_scheduler_io_result(-1, error);
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
off_t from = arguments->positional ? arguments->from : io_seekable(descriptor);
|
|
1041
|
+
|
|
1042
|
+
int completion = io_read(arguments->selector, arguments->fiber, descriptor, buffer, arguments->length, from);
|
|
1043
|
+
if (completion < 0) {
|
|
1044
|
+
return rb_fiber_scheduler_io_result(-1, -completion);
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
return rb_fiber_scheduler_io_result(completion, 0);
|
|
1048
|
+
}
|
|
1049
|
+
#endif
|
|
1050
|
+
|
|
1051
|
+
VALUE IO_Event_Selector_URing_io_read(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _first, VALUE _second) {
|
|
757
1052
|
struct IO_Event_Selector_URing *selector = NULL;
|
|
758
1053
|
TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
|
|
759
1054
|
|
|
760
|
-
|
|
1055
|
+
#if RUBY_FIBER_SCHEDULER_VERSION >= 4
|
|
1056
|
+
struct io_read_locked_arguments arguments = {
|
|
1057
|
+
.selector = selector,
|
|
1058
|
+
.fiber = fiber,
|
|
1059
|
+
.io = io,
|
|
1060
|
+
.offset = NUM2SIZET(_first),
|
|
1061
|
+
.length = NUM2SIZET(_second),
|
|
1062
|
+
.positional = false,
|
|
1063
|
+
};
|
|
761
1064
|
|
|
1065
|
+
return rb_io_buffer_locked_for_writing(buffer, io_read_locked, (VALUE)&arguments);
|
|
1066
|
+
#else
|
|
762
1067
|
void *base;
|
|
763
1068
|
size_t size;
|
|
764
1069
|
rb_io_buffer_get_bytes_for_writing(buffer, &base, &size);
|
|
765
1070
|
|
|
766
|
-
size_t length = NUM2SIZET(
|
|
767
|
-
size_t offset = NUM2SIZET(
|
|
1071
|
+
size_t length = NUM2SIZET(_first);
|
|
1072
|
+
size_t offset = NUM2SIZET(_second);
|
|
768
1073
|
size_t total = 0;
|
|
769
|
-
off_t from = io_seekable(descriptor);
|
|
770
1074
|
|
|
771
1075
|
// Ensure offset is within the bounds of the buffer to avoid size_t underflow and out-of-bounds pointer arithmetic on (char *)base + offset.
|
|
772
1076
|
if (offset > size) {
|
|
773
1077
|
return rb_fiber_scheduler_io_result(-1, EINVAL);
|
|
1078
|
+
} else if (offset == size) {
|
|
1079
|
+
return rb_fiber_scheduler_io_result(0, 0);
|
|
774
1080
|
}
|
|
775
1081
|
|
|
1082
|
+
int descriptor = IO_Event_Selector_io_descriptor(io);
|
|
1083
|
+
off_t from = io_seekable(descriptor);
|
|
776
1084
|
size_t maximum_size = size - offset;
|
|
777
1085
|
|
|
778
1086
|
// Are we performing a non-blocking read?
|
|
@@ -808,8 +1116,10 @@ VALUE IO_Event_Selector_URing_io_read(VALUE self, VALUE fiber, VALUE io, VALUE b
|
|
|
808
1116
|
}
|
|
809
1117
|
|
|
810
1118
|
return rb_fiber_scheduler_io_result(total, 0);
|
|
1119
|
+
#endif
|
|
811
1120
|
}
|
|
812
1121
|
|
|
1122
|
+
#if RUBY_FIBER_SCHEDULER_VERSION < 4
|
|
813
1123
|
static VALUE IO_Event_Selector_URing_io_read_compatible(int argc, VALUE *argv, VALUE self)
|
|
814
1124
|
{
|
|
815
1125
|
rb_check_arity(argc, 4, 5);
|
|
@@ -822,27 +1132,41 @@ static VALUE IO_Event_Selector_URing_io_read_compatible(int argc, VALUE *argv, V
|
|
|
822
1132
|
|
|
823
1133
|
return IO_Event_Selector_URing_io_read(self, argv[0], argv[1], argv[2], argv[3], _offset);
|
|
824
1134
|
}
|
|
1135
|
+
#endif
|
|
825
1136
|
|
|
826
|
-
VALUE IO_Event_Selector_URing_io_pread(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _from, VALUE
|
|
1137
|
+
VALUE IO_Event_Selector_URing_io_pread(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _from, VALUE _first, VALUE _second) {
|
|
827
1138
|
struct IO_Event_Selector_URing *selector = NULL;
|
|
828
1139
|
TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
|
|
829
1140
|
|
|
830
|
-
|
|
1141
|
+
#if RUBY_FIBER_SCHEDULER_VERSION >= 4
|
|
1142
|
+
struct io_read_locked_arguments arguments = {
|
|
1143
|
+
.selector = selector,
|
|
1144
|
+
.fiber = fiber,
|
|
1145
|
+
.io = io,
|
|
1146
|
+
.from = NUM2OFFT(_from),
|
|
1147
|
+
.offset = NUM2SIZET(_first),
|
|
1148
|
+
.length = NUM2SIZET(_second),
|
|
1149
|
+
.positional = true,
|
|
1150
|
+
};
|
|
831
1151
|
|
|
1152
|
+
return rb_io_buffer_locked_for_writing(buffer, io_read_locked, (VALUE)&arguments);
|
|
1153
|
+
#else
|
|
832
1154
|
void *base;
|
|
833
1155
|
size_t size;
|
|
834
1156
|
rb_io_buffer_get_bytes_for_writing(buffer, &base, &size);
|
|
835
1157
|
|
|
836
|
-
size_t length = NUM2SIZET(
|
|
837
|
-
size_t offset = NUM2SIZET(
|
|
1158
|
+
size_t length = NUM2SIZET(_first);
|
|
1159
|
+
size_t offset = NUM2SIZET(_second);
|
|
838
1160
|
size_t total = 0;
|
|
839
|
-
off_t from = NUM2OFFT(_from);
|
|
840
1161
|
|
|
841
1162
|
// Ensure offset is within the bounds of the buffer to avoid size_t underflow and out-of-bounds pointer arithmetic on (char *)base + offset.
|
|
842
1163
|
if (offset > size) {
|
|
843
1164
|
return rb_fiber_scheduler_io_result(-1, EINVAL);
|
|
1165
|
+
} else if (offset == size) {
|
|
1166
|
+
return rb_fiber_scheduler_io_result(0, 0);
|
|
844
1167
|
}
|
|
845
|
-
|
|
1168
|
+
off_t from = NUM2OFFT(_from);
|
|
1169
|
+
int descriptor = IO_Event_Selector_io_descriptor(io);
|
|
846
1170
|
size_t maximum_size = size - offset;
|
|
847
1171
|
while (maximum_size) {
|
|
848
1172
|
int result = io_read(selector, fiber, descriptor, (char*)base+offset, maximum_size, from);
|
|
@@ -865,6 +1189,7 @@ VALUE IO_Event_Selector_URing_io_pread(VALUE self, VALUE fiber, VALUE io, VALUE
|
|
|
865
1189
|
}
|
|
866
1190
|
|
|
867
1191
|
return rb_fiber_scheduler_io_result(total, 0);
|
|
1192
|
+
#endif
|
|
868
1193
|
}
|
|
869
1194
|
|
|
870
1195
|
#pragma mark - IO#write
|
|
@@ -902,16 +1227,7 @@ io_write_ensure(VALUE _argument)
|
|
|
902
1227
|
struct io_write_arguments *arguments = (struct io_write_arguments*)_argument;
|
|
903
1228
|
struct IO_Event_Selector_URing *selector = arguments->selector;
|
|
904
1229
|
|
|
905
|
-
|
|
906
|
-
if (arguments->waiting->completion) {
|
|
907
|
-
if (DEBUG) fprintf(stderr, "io_write_ensure:io_uring_prep_cancel(waiting=%p, completion=%p)\n", (void*)arguments->waiting, (void*)arguments->waiting->completion);
|
|
908
|
-
struct io_uring_sqe *sqe = io_get_sqe(selector);
|
|
909
|
-
io_uring_prep_cancel(sqe, (void*)arguments->waiting->completion, 0);
|
|
910
|
-
io_uring_sqe_set_data(sqe, NULL);
|
|
911
|
-
io_uring_submit_now(selector);
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
IO_Event_Selector_URing_Waiting_cancel(arguments->waiting);
|
|
1230
|
+
IO_Event_Selector_URing_Waiting_cancel_and_wait(selector, arguments->waiting);
|
|
915
1231
|
|
|
916
1232
|
return Qnil;
|
|
917
1233
|
}
|
|
@@ -941,20 +1257,87 @@ io_write(struct IO_Event_Selector_URing *selector, VALUE fiber, int descriptor,
|
|
|
941
1257
|
);
|
|
942
1258
|
}
|
|
943
1259
|
|
|
944
|
-
|
|
1260
|
+
#if RUBY_FIBER_SCHEDULER_VERSION >= 4
|
|
1261
|
+
struct io_write_locked_arguments {
|
|
1262
|
+
struct IO_Event_Selector_URing *selector;
|
|
1263
|
+
VALUE fiber;
|
|
1264
|
+
VALUE io;
|
|
1265
|
+
off_t from;
|
|
1266
|
+
size_t offset;
|
|
1267
|
+
size_t length;
|
|
1268
|
+
bool positional;
|
|
1269
|
+
};
|
|
1270
|
+
|
|
1271
|
+
static VALUE
|
|
1272
|
+
io_write_locked(const void *base, size_t size, VALUE _arguments)
|
|
1273
|
+
{
|
|
1274
|
+
struct io_write_locked_arguments *arguments = (struct io_write_locked_arguments *)_arguments;
|
|
1275
|
+
|
|
1276
|
+
if (!IO_Event_Selector_valid_buffer_range(size, arguments->offset, arguments->length)) {
|
|
1277
|
+
return rb_fiber_scheduler_io_result(-1, EINVAL);
|
|
1278
|
+
} else if (arguments->length == 0) {
|
|
1279
|
+
return rb_fiber_scheduler_io_result(0, 0);
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
int descriptor = IO_Event_Selector_io_descriptor(arguments->io);
|
|
1283
|
+
const char *buffer = (const char*)base + arguments->offset;
|
|
1284
|
+
|
|
1285
|
+
// Avoid the submission and suspension overhead when the operation can
|
|
1286
|
+
// complete immediately. The descriptor must be non-blocking for this
|
|
1287
|
+
// optimistic syscall; if it would block, restore its original mode and
|
|
1288
|
+
// submit the operation to io_uring instead.
|
|
1289
|
+
int flags = IO_Event_Selector_nonblock_set(descriptor);
|
|
1290
|
+
ssize_t result;
|
|
1291
|
+
|
|
1292
|
+
if (arguments->positional) {
|
|
1293
|
+
result = pwrite(descriptor, buffer, arguments->length, arguments->from);
|
|
1294
|
+
} else {
|
|
1295
|
+
result = write(descriptor, buffer, arguments->length);
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
int error = errno;
|
|
1299
|
+
IO_Event_Selector_nonblock_restore(descriptor, flags);
|
|
1300
|
+
|
|
1301
|
+
if (result >= 0) {
|
|
1302
|
+
return rb_fiber_scheduler_io_result(result, 0);
|
|
1303
|
+
} else if (!IO_Event_try_again(error)) {
|
|
1304
|
+
return rb_fiber_scheduler_io_result(-1, error);
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
off_t from = arguments->positional ? arguments->from : io_seekable(descriptor);
|
|
1308
|
+
|
|
1309
|
+
int completion = io_write(arguments->selector, arguments->fiber, descriptor, (char*)buffer, arguments->length, from);
|
|
1310
|
+
if (completion < 0) {
|
|
1311
|
+
return rb_fiber_scheduler_io_result(-1, -completion);
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
return rb_fiber_scheduler_io_result(completion, 0);
|
|
1315
|
+
}
|
|
1316
|
+
#endif
|
|
1317
|
+
|
|
1318
|
+
VALUE IO_Event_Selector_URing_io_write(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _first, VALUE _second) {
|
|
945
1319
|
struct IO_Event_Selector_URing *selector = NULL;
|
|
946
1320
|
TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
|
|
947
1321
|
|
|
948
|
-
|
|
1322
|
+
#if RUBY_FIBER_SCHEDULER_VERSION >= 4
|
|
1323
|
+
struct io_write_locked_arguments arguments = {
|
|
1324
|
+
.selector = selector,
|
|
1325
|
+
.fiber = fiber,
|
|
1326
|
+
.io = io,
|
|
1327
|
+
.offset = NUM2SIZET(_first),
|
|
1328
|
+
.length = NUM2SIZET(_second),
|
|
1329
|
+
.positional = false,
|
|
1330
|
+
};
|
|
949
1331
|
|
|
1332
|
+
return rb_io_buffer_locked_for_reading(buffer, io_write_locked, (VALUE)&arguments);
|
|
1333
|
+
#else
|
|
950
1334
|
const void *base;
|
|
951
1335
|
size_t size;
|
|
952
1336
|
rb_io_buffer_get_bytes_for_reading(buffer, &base, &size);
|
|
953
1337
|
|
|
954
|
-
size_t length = NUM2SIZET(
|
|
955
|
-
size_t offset = NUM2SIZET(
|
|
1338
|
+
size_t length = NUM2SIZET(_first);
|
|
1339
|
+
size_t offset = NUM2SIZET(_second);
|
|
956
1340
|
size_t total = 0;
|
|
957
|
-
off_t from = io_seekable(descriptor);
|
|
958
1341
|
|
|
959
1342
|
if (length > size) {
|
|
960
1343
|
rb_raise(rb_eRuntimeError, "Length exceeds size of buffer!");
|
|
@@ -963,8 +1346,12 @@ VALUE IO_Event_Selector_URing_io_write(VALUE self, VALUE fiber, VALUE io, VALUE
|
|
|
963
1346
|
// Ensure offset is within the bounds of the buffer to avoid size_t underflow and out-of-bounds pointer arithmetic on (char *)base + offset.
|
|
964
1347
|
if (offset > size) {
|
|
965
1348
|
return rb_fiber_scheduler_io_result(-1, EINVAL);
|
|
1349
|
+
} else if (offset == size) {
|
|
1350
|
+
return rb_fiber_scheduler_io_result(0, 0);
|
|
966
1351
|
}
|
|
967
|
-
|
|
1352
|
+
|
|
1353
|
+
int descriptor = IO_Event_Selector_io_descriptor(io);
|
|
1354
|
+
off_t from = io_seekable(descriptor);
|
|
968
1355
|
size_t maximum_size = size - offset;
|
|
969
1356
|
while (maximum_size) {
|
|
970
1357
|
int result = io_write(selector, fiber, descriptor, (char*)base+offset, maximum_size, from);
|
|
@@ -986,8 +1373,10 @@ VALUE IO_Event_Selector_URing_io_write(VALUE self, VALUE fiber, VALUE io, VALUE
|
|
|
986
1373
|
}
|
|
987
1374
|
|
|
988
1375
|
return rb_fiber_scheduler_io_result(total, 0);
|
|
1376
|
+
#endif
|
|
989
1377
|
}
|
|
990
1378
|
|
|
1379
|
+
#if RUBY_FIBER_SCHEDULER_VERSION < 4
|
|
991
1380
|
static VALUE IO_Event_Selector_URing_io_write_compatible(int argc, VALUE *argv, VALUE self)
|
|
992
1381
|
{
|
|
993
1382
|
rb_check_arity(argc, 4, 5);
|
|
@@ -1000,21 +1389,32 @@ static VALUE IO_Event_Selector_URing_io_write_compatible(int argc, VALUE *argv,
|
|
|
1000
1389
|
|
|
1001
1390
|
return IO_Event_Selector_URing_io_write(self, argv[0], argv[1], argv[2], argv[3], _offset);
|
|
1002
1391
|
}
|
|
1392
|
+
#endif
|
|
1003
1393
|
|
|
1004
|
-
VALUE IO_Event_Selector_URing_io_pwrite(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _from, VALUE
|
|
1394
|
+
VALUE IO_Event_Selector_URing_io_pwrite(VALUE self, VALUE fiber, VALUE io, VALUE buffer, VALUE _from, VALUE _first, VALUE _second) {
|
|
1005
1395
|
struct IO_Event_Selector_URing *selector = NULL;
|
|
1006
1396
|
TypedData_Get_Struct(self, struct IO_Event_Selector_URing, &IO_Event_Selector_URing_Type, selector);
|
|
1007
1397
|
|
|
1008
|
-
|
|
1398
|
+
#if RUBY_FIBER_SCHEDULER_VERSION >= 4
|
|
1399
|
+
struct io_write_locked_arguments arguments = {
|
|
1400
|
+
.selector = selector,
|
|
1401
|
+
.fiber = fiber,
|
|
1402
|
+
.io = io,
|
|
1403
|
+
.from = NUM2OFFT(_from),
|
|
1404
|
+
.offset = NUM2SIZET(_first),
|
|
1405
|
+
.length = NUM2SIZET(_second),
|
|
1406
|
+
.positional = true,
|
|
1407
|
+
};
|
|
1009
1408
|
|
|
1409
|
+
return rb_io_buffer_locked_for_reading(buffer, io_write_locked, (VALUE)&arguments);
|
|
1410
|
+
#else
|
|
1010
1411
|
const void *base;
|
|
1011
1412
|
size_t size;
|
|
1012
1413
|
rb_io_buffer_get_bytes_for_reading(buffer, &base, &size);
|
|
1013
1414
|
|
|
1014
|
-
size_t length = NUM2SIZET(
|
|
1015
|
-
size_t offset = NUM2SIZET(
|
|
1415
|
+
size_t length = NUM2SIZET(_first);
|
|
1416
|
+
size_t offset = NUM2SIZET(_second);
|
|
1016
1417
|
size_t total = 0;
|
|
1017
|
-
off_t from = NUM2OFFT(_from);
|
|
1018
1418
|
|
|
1019
1419
|
if (length > size) {
|
|
1020
1420
|
rb_raise(rb_eRuntimeError, "Length exceeds size of buffer!");
|
|
@@ -1023,8 +1423,11 @@ VALUE IO_Event_Selector_URing_io_pwrite(VALUE self, VALUE fiber, VALUE io, VALUE
|
|
|
1023
1423
|
// Ensure offset is within the bounds of the buffer to avoid size_t underflow and out-of-bounds pointer arithmetic on (char *)base + offset.
|
|
1024
1424
|
if (offset > size) {
|
|
1025
1425
|
return rb_fiber_scheduler_io_result(-1, EINVAL);
|
|
1426
|
+
} else if (offset == size) {
|
|
1427
|
+
return rb_fiber_scheduler_io_result(0, 0);
|
|
1026
1428
|
}
|
|
1027
|
-
|
|
1429
|
+
off_t from = NUM2OFFT(_from);
|
|
1430
|
+
int descriptor = IO_Event_Selector_io_descriptor(io);
|
|
1028
1431
|
size_t maximum_size = size - offset;
|
|
1029
1432
|
while (maximum_size) {
|
|
1030
1433
|
int result = io_write(selector, fiber, descriptor, (char*)base+offset, maximum_size, from);
|
|
@@ -1047,6 +1450,7 @@ VALUE IO_Event_Selector_URing_io_pwrite(VALUE self, VALUE fiber, VALUE io, VALUE
|
|
|
1047
1450
|
}
|
|
1048
1451
|
|
|
1049
1452
|
return rb_fiber_scheduler_io_result(total, 0);
|
|
1453
|
+
#endif
|
|
1050
1454
|
}
|
|
1051
1455
|
|
|
1052
1456
|
#endif
|
|
@@ -1104,9 +1508,14 @@ struct __kernel_timespec * make_timeout(VALUE duration, struct __kernel_timespec
|
|
|
1104
1508
|
return storage;
|
|
1105
1509
|
}
|
|
1106
1510
|
|
|
1511
|
+
// Return true when it is safe and useful to enter the blocking selector wait.
|
|
1107
1512
|
static
|
|
1108
|
-
int
|
|
1109
|
-
|
|
1513
|
+
int select_blocking_allowed(struct __kernel_timespec *timespec) {
|
|
1514
|
+
// A `0` timeout is already a poll. The selector has already performed the immediate poll previously, so there is no useful blocking wait to enter here.
|
|
1515
|
+
if (timespec && timespec->tv_sec == 0 && timespec->tv_nsec == 0) {
|
|
1516
|
+
return 0;
|
|
1517
|
+
}
|
|
1518
|
+
return 1;
|
|
1110
1519
|
}
|
|
1111
1520
|
|
|
1112
1521
|
struct select_arguments {
|
|
@@ -1145,22 +1554,21 @@ int select_internal_without_gvl(struct select_arguments *arguments) {
|
|
|
1145
1554
|
|
|
1146
1555
|
io_uring_submit_flush(selector);
|
|
1147
1556
|
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
selector->blocked = 0;
|
|
1557
|
+
arguments->result = -EINTR;
|
|
1558
|
+
IO_Event_Selector_blocking_operation(&selector->backend, select_internal, (void *)arguments, RUBY_UBF_IO, 0);
|
|
1151
1559
|
|
|
1152
1560
|
if (arguments->result == -ETIME) {
|
|
1153
|
-
|
|
1561
|
+
return 0;
|
|
1154
1562
|
} else if (arguments->result == -EINTR) {
|
|
1155
|
-
|
|
1563
|
+
return 0;
|
|
1156
1564
|
} else if (arguments->result < 0) {
|
|
1157
1565
|
rb_syserr_fail(-arguments->result, "select_internal_without_gvl:io_uring_wait_cqe_timeout");
|
|
1158
1566
|
} else {
|
|
1159
1567
|
// At least 1 event is waiting:
|
|
1160
|
-
|
|
1568
|
+
return 1;
|
|
1161
1569
|
}
|
|
1162
1570
|
|
|
1163
|
-
return
|
|
1571
|
+
return 0;
|
|
1164
1572
|
}
|
|
1165
1573
|
|
|
1166
1574
|
static inline
|
|
@@ -1194,6 +1602,13 @@ unsigned select_process_completions(struct IO_Event_Selector_URing *selector) {
|
|
|
1194
1602
|
continue;
|
|
1195
1603
|
}
|
|
1196
1604
|
|
|
1605
|
+
if (cqe->user_data & IO_EVENT_SELECTOR_URING_CANCELLATION_TAG) {
|
|
1606
|
+
struct IO_Event_Selector_URing_Completion *completion = IO_Event_Selector_URing_Completion_from_cancellation_data(cqe->user_data);
|
|
1607
|
+
IO_Event_Selector_URing_Completion_cancellation_complete(selector, completion);
|
|
1608
|
+
io_uring_cq_advance(ring, 1);
|
|
1609
|
+
continue;
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1197
1612
|
struct IO_Event_Selector_URing_Completion *completion = (void*)cqe->user_data;
|
|
1198
1613
|
struct IO_Event_Selector_URing_Waiting *waiting = completion->waiting;
|
|
1199
1614
|
|
|
@@ -1208,13 +1623,11 @@ unsigned select_process_completions(struct IO_Event_Selector_URing *selector) {
|
|
|
1208
1623
|
|
|
1209
1624
|
VALUE fiber = 0;
|
|
1210
1625
|
if (waiting && waiting->fiber) {
|
|
1211
|
-
assert(waiting->result != -ECANCELED);
|
|
1212
|
-
|
|
1213
1626
|
fiber = waiting->fiber;
|
|
1214
1627
|
}
|
|
1215
1628
|
|
|
1216
1629
|
// This marks the waiting operation as "complete":
|
|
1217
|
-
|
|
1630
|
+
IO_Event_Selector_URing_Completion_complete(selector, completion);
|
|
1218
1631
|
|
|
1219
1632
|
if (fiber) {
|
|
1220
1633
|
IO_Event_Selector_loop_resume(&selector->backend, fiber, 0, NULL);
|
|
@@ -1257,28 +1670,29 @@ VALUE IO_Event_Selector_URing_select(VALUE self, VALUE duration) {
|
|
|
1257
1670
|
|
|
1258
1671
|
int ready = IO_Event_Selector_ready_flush(&selector->backend);
|
|
1259
1672
|
|
|
1260
|
-
int
|
|
1673
|
+
int completed = select_process_completions(selector);
|
|
1261
1674
|
|
|
1262
1675
|
// If we:
|
|
1263
1676
|
// 1. Didn't process any ready fibers, and
|
|
1264
1677
|
// 2. Didn't process any events from non-blocking select (above), and
|
|
1265
1678
|
// 3. There are no items in the ready list,
|
|
1266
1679
|
// then we can perform a blocking select.
|
|
1267
|
-
if (!ready && !
|
|
1680
|
+
if (!ready && !completed && !selector->backend.ready) {
|
|
1268
1681
|
// We might need to wait for events:
|
|
1269
1682
|
struct select_arguments arguments = {
|
|
1270
1683
|
.selector = selector,
|
|
1684
|
+
.result = 0,
|
|
1271
1685
|
.timeout = NULL,
|
|
1272
1686
|
};
|
|
1273
1687
|
|
|
1274
1688
|
arguments.timeout = make_timeout(duration, &arguments.storage);
|
|
1275
1689
|
|
|
1276
|
-
if (!selector->backend.ready &&
|
|
1690
|
+
if (!selector->backend.ready && select_blocking_allowed(arguments.timeout)) {
|
|
1277
1691
|
struct timespec start_time;
|
|
1278
1692
|
IO_Event_Time_current(&start_time);
|
|
1279
1693
|
|
|
1280
1694
|
// This is a blocking operation, we wait for events:
|
|
1281
|
-
result = select_internal_without_gvl(&arguments);
|
|
1695
|
+
int result = select_internal_without_gvl(&arguments);
|
|
1282
1696
|
|
|
1283
1697
|
struct timespec end_time;
|
|
1284
1698
|
IO_Event_Time_current(&end_time);
|
|
@@ -1286,12 +1700,12 @@ VALUE IO_Event_Selector_URing_select(VALUE self, VALUE duration) {
|
|
|
1286
1700
|
|
|
1287
1701
|
// After waiting/flushing the SQ, check if there are any completions:
|
|
1288
1702
|
if (result > 0) {
|
|
1289
|
-
|
|
1703
|
+
completed = select_process_completions(selector);
|
|
1290
1704
|
}
|
|
1291
1705
|
}
|
|
1292
1706
|
}
|
|
1293
1707
|
|
|
1294
|
-
return RB_INT2NUM(
|
|
1708
|
+
return RB_INT2NUM(completed);
|
|
1295
1709
|
}
|
|
1296
1710
|
|
|
1297
1711
|
VALUE IO_Event_Selector_URing_wakeup(VALUE self) {
|
|
@@ -1300,7 +1714,7 @@ VALUE IO_Event_Selector_URing_wakeup(VALUE self) {
|
|
|
1300
1714
|
|
|
1301
1715
|
// Wake the selector by signalling the interrupt. This is safe from any thread
|
|
1302
1716
|
// and never touches the ring's SQ, which is required for IORING_SETUP_SINGLE_ISSUER.
|
|
1303
|
-
if (selector->blocked) {
|
|
1717
|
+
if (selector->backend.blocked) {
|
|
1304
1718
|
IO_Event_Interrupt_signal(&selector->interrupt);
|
|
1305
1719
|
return Qtrue;
|
|
1306
1720
|
}
|
|
@@ -1370,12 +1784,18 @@ void Init_IO_Event_Selector_URing(VALUE IO_Event_Selector) {
|
|
|
1370
1784
|
rb_define_method(IO_Event_Selector_URing, "select", IO_Event_Selector_URing_select, 1);
|
|
1371
1785
|
rb_define_method(IO_Event_Selector_URing, "wakeup", IO_Event_Selector_URing_wakeup, 0);
|
|
1372
1786
|
rb_define_method(IO_Event_Selector_URing, "close", IO_Event_Selector_URing_close, 0);
|
|
1787
|
+
rb_define_method(IO_Event_Selector_URing, "closed?", IO_Event_Selector_URing_closed_p, 0);
|
|
1373
1788
|
|
|
1374
1789
|
rb_define_method(IO_Event_Selector_URing, "io_wait", IO_Event_Selector_URing_io_wait, 3);
|
|
1375
1790
|
|
|
1376
1791
|
#ifdef HAVE_RUBY_IO_BUFFER_H
|
|
1792
|
+
#if RUBY_FIBER_SCHEDULER_VERSION >= 4
|
|
1793
|
+
rb_define_method(IO_Event_Selector_URing, "io_read", IO_Event_Selector_URing_io_read, 5);
|
|
1794
|
+
rb_define_method(IO_Event_Selector_URing, "io_write", IO_Event_Selector_URing_io_write, 5);
|
|
1795
|
+
#else
|
|
1377
1796
|
rb_define_method(IO_Event_Selector_URing, "io_read", IO_Event_Selector_URing_io_read_compatible, -1);
|
|
1378
1797
|
rb_define_method(IO_Event_Selector_URing, "io_write", IO_Event_Selector_URing_io_write_compatible, -1);
|
|
1798
|
+
#endif
|
|
1379
1799
|
rb_define_method(IO_Event_Selector_URing, "io_pread", IO_Event_Selector_URing_io_pread, 6);
|
|
1380
1800
|
rb_define_method(IO_Event_Selector_URing, "io_pwrite", IO_Event_Selector_URing_io_pwrite, 6);
|
|
1381
1801
|
#endif
|