io-event 1.18.0 → 1.19.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 +2 -3
- data/ext/io/event/selector/epoll.c +7 -2
- data/ext/io/event/selector/kqueue.c +7 -2
- data/ext/io/event/selector/selector.c +13 -1
- data/ext/io/event/selector/selector.h +10 -2
- data/ext/io/event/selector/uring.c +75 -4
- data/lib/io/event/interrupt.rb +1 -1
- data/lib/io/event/selector/select.rb +1 -3
- data/lib/io/event/selector.rb +18 -0
- data/lib/io/event/version.rb +1 -1
- data/license.md +1 -0
- data/readme.md +5 -4
- data/releases.md +5 -0
- data.tar.gz.sig +0 -0
- metadata +2 -1
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f4e37070c965d89037b3fac95c4b5de378273050ee28eb397a954c5fce84a6ae
|
|
4
|
+
data.tar.gz: 7571ee0edd25008cafe5cc9a81488969d5d269ec1e53793fa7cca393bbfd754c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 22cb0db38d55f3710fc36469c2f2e1162fd69c69c228a9be2c4310f83dbc5304c8cb4194fc7fe2ca4b4316f906ff17341bcceeb24ed1cf9ebe70cd076bb05ca2
|
|
7
|
+
data.tar.gz: d7e4a07d6acf309898b3df66e8e6b2b21f09af99a7f21ac03f30c68ad32024a1766320159f350c450df18aa46c911951c87c6f38abd06c55c9635f7eb1c86831
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/ext/extconf.rb
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
# Copyright, 2023, by Math Ieu.
|
|
7
7
|
# Copyright, 2025, by Stanislav (Stas) Katkov.
|
|
8
8
|
# Copyright, 2026, by Stan Hu.
|
|
9
|
+
# Copyright, 2026, by Sharon Rosner.
|
|
9
10
|
|
|
10
11
|
return if RUBY_DESCRIPTION =~ /jruby/
|
|
11
12
|
|
|
@@ -32,9 +33,7 @@ have_func("rb_ext_ractor_safe")
|
|
|
32
33
|
have_func("&rb_fiber_transfer")
|
|
33
34
|
|
|
34
35
|
if have_library("uring") and have_header("liburing.h")
|
|
35
|
-
|
|
36
|
-
# have_func("io_uring_submit_and_wait_timeout", "liburing.h")
|
|
37
|
-
|
|
36
|
+
have_func("io_uring_prep_waitid", "liburing.h")
|
|
38
37
|
$srcs << "io/event/selector/uring.c"
|
|
39
38
|
end
|
|
40
39
|
|
|
@@ -462,7 +462,7 @@ VALUE process_wait_transfer(VALUE _arguments) {
|
|
|
462
462
|
IO_Event_Selector_loop_yield(&arguments->selector->backend);
|
|
463
463
|
|
|
464
464
|
if (arguments->waiting->ready) {
|
|
465
|
-
return
|
|
465
|
+
return IO_Event_Selector_process_status_reap(arguments->pid, arguments->flags);
|
|
466
466
|
} else {
|
|
467
467
|
return Qfalse;
|
|
468
468
|
}
|
|
@@ -488,6 +488,11 @@ VALUE IO_Event_Selector_EPoll_process_wait(VALUE self, VALUE fiber, VALUE _pid,
|
|
|
488
488
|
pid_t pid = NUM2PIDT(_pid);
|
|
489
489
|
int flags = NUM2INT(_flags);
|
|
490
490
|
|
|
491
|
+
// `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:
|
|
492
|
+
if (pid <= 0) {
|
|
493
|
+
return IO_Event_Selector_process_wait(pid, flags);
|
|
494
|
+
}
|
|
495
|
+
|
|
491
496
|
int descriptor = pidfd_open(pid, 0);
|
|
492
497
|
|
|
493
498
|
if (descriptor == -1) {
|
|
@@ -497,7 +502,7 @@ VALUE IO_Event_Selector_EPoll_process_wait(VALUE self, VALUE fiber, VALUE _pid,
|
|
|
497
502
|
rb_update_max_fd(descriptor);
|
|
498
503
|
|
|
499
504
|
// `pidfd_open` (above) may be edge triggered, so we need to check if the process is already exited, and if so, return immediately, otherwise we will block indefinitely.
|
|
500
|
-
VALUE status =
|
|
505
|
+
VALUE status = IO_Event_Selector_process_status_reap(pid, flags);
|
|
501
506
|
if (status != Qnil) {
|
|
502
507
|
close(descriptor);
|
|
503
508
|
return status;
|
|
@@ -469,7 +469,7 @@ VALUE process_wait_transfer(VALUE _arguments) {
|
|
|
469
469
|
|
|
470
470
|
if (arguments->waiting->ready) {
|
|
471
471
|
process_prewait(arguments->pid);
|
|
472
|
-
return
|
|
472
|
+
return IO_Event_Selector_process_status_reap(arguments->pid, arguments->flags);
|
|
473
473
|
} else {
|
|
474
474
|
return Qfalse;
|
|
475
475
|
}
|
|
@@ -493,6 +493,11 @@ VALUE IO_Event_Selector_KQueue_process_wait(VALUE self, VALUE fiber, VALUE _pid,
|
|
|
493
493
|
pid_t pid = NUM2PIDT(_pid);
|
|
494
494
|
int flags = NUM2INT(_flags);
|
|
495
495
|
|
|
496
|
+
// `EVFILT_PROC` can only refer to a specific process, so waiting for any child or a process group (pid <= 0) is delegated to the threaded fallback:
|
|
497
|
+
if (pid <= 0) {
|
|
498
|
+
return IO_Event_Selector_process_wait(pid, flags);
|
|
499
|
+
}
|
|
500
|
+
|
|
496
501
|
struct IO_Event_Selector_KQueue_Waiting waiting = {
|
|
497
502
|
.list = {.type = &IO_Event_Selector_KQueue_process_wait_list_type},
|
|
498
503
|
.fiber = fiber,
|
|
@@ -514,7 +519,7 @@ VALUE IO_Event_Selector_KQueue_process_wait(VALUE self, VALUE fiber, VALUE _pid,
|
|
|
514
519
|
if (errno == ESRCH) {
|
|
515
520
|
process_prewait(pid);
|
|
516
521
|
|
|
517
|
-
return
|
|
522
|
+
return IO_Event_Selector_process_status_reap(pid, flags);
|
|
518
523
|
}
|
|
519
524
|
|
|
520
525
|
rb_sys_fail("IO_Event_Selector_KQueue_process_wait:IO_Event_Selector_KQueue_Waiting_register");
|
|
@@ -24,7 +24,7 @@ static VALUE rb_Process_Status = Qnil;
|
|
|
24
24
|
|
|
25
25
|
VALUE IO_Event_Selector_process_status_wait(rb_pid_t pid, int flags)
|
|
26
26
|
{
|
|
27
|
-
return rb_funcall(rb_Process_Status, id_wait, 2, PIDT2NUM(pid), INT2NUM(flags
|
|
27
|
+
return rb_funcall(rb_Process_Status, id_wait, 2, PIDT2NUM(pid), INT2NUM(flags));
|
|
28
28
|
}
|
|
29
29
|
#endif
|
|
30
30
|
|
|
@@ -80,9 +80,21 @@ static VALUE IO_Event_Selector_nonblock(VALUE class, VALUE io)
|
|
|
80
80
|
return rb_ensure(rb_yield, io, IO_Event_Selector_nonblock_ensure, (VALUE)&arguments);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
static VALUE rb_IO_Event_Selector = Qnil;
|
|
84
|
+
static ID id_process_wait;
|
|
85
|
+
|
|
86
|
+
// Wait for a process when the selector cannot do so natively (e.g. `pid <= 0`: any child, or a process group). Delegates to the pure-Ruby `IO::Event::Selector.process_wait`, which performs a blocking wait on a separate thread; joining it is fiber-scheduler aware, so the reactor keeps running.
|
|
87
|
+
VALUE IO_Event_Selector_process_wait(rb_pid_t pid, int flags) {
|
|
88
|
+
return rb_funcall(rb_IO_Event_Selector, id_process_wait, 2, PIDT2NUM(pid), INT2NUM(flags));
|
|
89
|
+
}
|
|
90
|
+
|
|
83
91
|
void Init_IO_Event_Selector(VALUE IO_Event_Selector) {
|
|
84
92
|
IO_Event_Selector_pending_interrupt_p_id = rb_intern("pending_interrupt?");
|
|
85
93
|
|
|
94
|
+
rb_IO_Event_Selector = IO_Event_Selector;
|
|
95
|
+
rb_gc_register_mark_object(rb_IO_Event_Selector);
|
|
96
|
+
id_process_wait = rb_intern("process_wait");
|
|
97
|
+
|
|
86
98
|
#ifndef HAVE_RB_IO_DESCRIPTOR
|
|
87
99
|
id_fileno = rb_intern("fileno");
|
|
88
100
|
#endif
|
|
@@ -52,13 +52,21 @@ static inline int IO_Event_Selector_pending_interrupt(void) {
|
|
|
52
52
|
int IO_Event_Selector_io_descriptor(VALUE io);
|
|
53
53
|
#endif
|
|
54
54
|
|
|
55
|
-
//
|
|
55
|
+
// Wait for a process to change state. This blocks until the process changes state, unless `WNOHANG` is given in `flags`.
|
|
56
56
|
#ifdef HAVE_RB_PROCESS_STATUS_WAIT
|
|
57
|
-
#define IO_Event_Selector_process_status_wait(pid, flags) rb_process_status_wait(pid, flags
|
|
57
|
+
#define IO_Event_Selector_process_status_wait(pid, flags) rb_process_status_wait(pid, flags)
|
|
58
58
|
#else
|
|
59
59
|
VALUE IO_Event_Selector_process_status_wait(rb_pid_t pid, int flags);
|
|
60
60
|
#endif
|
|
61
61
|
|
|
62
|
+
// Reap a process that is known to have changed state (e.g. after a readiness event), without blocking.
|
|
63
|
+
static inline VALUE IO_Event_Selector_process_status_reap(rb_pid_t pid, int flags) {
|
|
64
|
+
return IO_Event_Selector_process_status_wait(pid, flags | WNOHANG);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Wait for a process the selector cannot represent natively (e.g. `pid <= 0`: any child, or a process group), using a fiber-scheduler aware blocking wait on a separate thread.
|
|
68
|
+
VALUE IO_Event_Selector_process_wait(rb_pid_t pid, int flags);
|
|
69
|
+
|
|
62
70
|
int IO_Event_Selector_nonblock_set(int file_descriptor);
|
|
63
71
|
void IO_Event_Selector_nonblock_restore(int file_descriptor, int flags);
|
|
64
72
|
|
|
@@ -15,10 +15,17 @@
|
|
|
15
15
|
|
|
16
16
|
#include "../interrupt.h"
|
|
17
17
|
|
|
18
|
-
#include "pidfd.c"
|
|
19
|
-
|
|
20
18
|
#include <linux/version.h>
|
|
21
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
|
+
|
|
22
29
|
enum {
|
|
23
30
|
DEBUG = 0,
|
|
24
31
|
DEBUG_COMPLETION = 0,
|
|
@@ -500,13 +507,43 @@ struct io_uring_sqe * io_get_sqe(struct IO_Event_Selector_URing *selector) {
|
|
|
500
507
|
|
|
501
508
|
#pragma mark - Process.wait
|
|
502
509
|
|
|
510
|
+
#ifdef IO_EVENT_SELECTOR_URING_USE_WAITID
|
|
511
|
+
// Translate a Ruby/`waitpid`-style pid into the `waitid(2)` idtype and id, mirroring the semantics of `waitpid(2)`:
|
|
512
|
+
//
|
|
513
|
+
// pid == -1 -> any child (P_ALL)
|
|
514
|
+
// pid == 0 -> any child in the caller's process group (P_PGID, id 0; Linux >= 5.4)
|
|
515
|
+
// pid < -1 -> any child in process group |pid| (P_PGID)
|
|
516
|
+
// pid > 0 -> the specific child (P_PID)
|
|
517
|
+
//
|
|
518
|
+
static inline idtype_t process_waitid_type(pid_t pid, id_t *id) {
|
|
519
|
+
if (pid == -1) {
|
|
520
|
+
*id = 0;
|
|
521
|
+
return P_ALL;
|
|
522
|
+
} else if (pid == 0) {
|
|
523
|
+
*id = 0;
|
|
524
|
+
return P_PGID;
|
|
525
|
+
} else if (pid < -1) {
|
|
526
|
+
*id = (id_t)(-pid);
|
|
527
|
+
return P_PGID;
|
|
528
|
+
} else {
|
|
529
|
+
*id = (id_t)pid;
|
|
530
|
+
return P_PID;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
#endif
|
|
534
|
+
|
|
503
535
|
struct process_wait_arguments {
|
|
504
536
|
struct IO_Event_Selector_URing *selector;
|
|
505
537
|
struct IO_Event_Selector_URing_Waiting *waiting;
|
|
506
538
|
|
|
507
539
|
pid_t pid;
|
|
508
540
|
int flags;
|
|
541
|
+
|
|
542
|
+
#ifdef IO_EVENT_SELECTOR_URING_USE_WAITID
|
|
543
|
+
siginfo_t siginfo;
|
|
544
|
+
#else
|
|
509
545
|
int descriptor;
|
|
546
|
+
#endif
|
|
510
547
|
};
|
|
511
548
|
|
|
512
549
|
static
|
|
@@ -515,18 +552,32 @@ VALUE process_wait_transfer(VALUE _arguments) {
|
|
|
515
552
|
|
|
516
553
|
IO_Event_Selector_loop_yield(&arguments->selector->backend);
|
|
517
554
|
|
|
555
|
+
#ifdef IO_EVENT_SELECTOR_URING_USE_WAITID
|
|
556
|
+
int32_t result = arguments->waiting->result;
|
|
557
|
+
if (result < 0) {
|
|
558
|
+
rb_syserr_fail(-result, "IO_Event_Selector_URing_process_wait:io_uring_prep_waitid");
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
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);
|
|
562
|
+
|
|
563
|
+
// 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`:
|
|
564
|
+
return IO_Event_Selector_process_status_reap(arguments->siginfo.si_pid, arguments->flags);
|
|
565
|
+
#else
|
|
518
566
|
if (arguments->waiting->result) {
|
|
519
|
-
return
|
|
567
|
+
return IO_Event_Selector_process_status_reap(arguments->pid, arguments->flags);
|
|
520
568
|
} else {
|
|
521
569
|
return Qfalse;
|
|
522
570
|
}
|
|
571
|
+
#endif
|
|
523
572
|
}
|
|
524
573
|
|
|
525
574
|
static
|
|
526
575
|
VALUE process_wait_ensure(VALUE _arguments) {
|
|
527
576
|
struct process_wait_arguments *arguments = (struct process_wait_arguments *)_arguments;
|
|
528
577
|
|
|
578
|
+
#ifndef IO_EVENT_SELECTOR_URING_USE_WAITID
|
|
529
579
|
close(arguments->descriptor);
|
|
580
|
+
#endif
|
|
530
581
|
|
|
531
582
|
IO_Event_Selector_URing_Waiting_cancel(arguments->waiting);
|
|
532
583
|
|
|
@@ -540,11 +591,18 @@ VALUE IO_Event_Selector_URing_process_wait(VALUE self, VALUE fiber, VALUE _pid,
|
|
|
540
591
|
pid_t pid = NUM2PIDT(_pid);
|
|
541
592
|
int flags = NUM2INT(_flags);
|
|
542
593
|
|
|
594
|
+
#ifndef IO_EVENT_SELECTOR_URING_USE_WAITID
|
|
595
|
+
// `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:
|
|
596
|
+
if (pid <= 0) {
|
|
597
|
+
return IO_Event_Selector_process_wait(pid, flags);
|
|
598
|
+
}
|
|
599
|
+
|
|
543
600
|
int descriptor = pidfd_open(pid, 0);
|
|
544
601
|
if (descriptor < 0) {
|
|
545
602
|
rb_syserr_fail(errno, "IO_Event_Selector_URing_process_wait:pidfd_open");
|
|
546
603
|
}
|
|
547
604
|
rb_update_max_fd(descriptor);
|
|
605
|
+
#endif
|
|
548
606
|
|
|
549
607
|
struct IO_Event_Selector_URing_Waiting waiting = {
|
|
550
608
|
.fiber = fiber,
|
|
@@ -559,12 +617,25 @@ VALUE IO_Event_Selector_URing_process_wait(VALUE self, VALUE fiber, VALUE _pid,
|
|
|
559
617
|
.waiting = &waiting,
|
|
560
618
|
.pid = pid,
|
|
561
619
|
.flags = flags,
|
|
620
|
+
#ifdef IO_EVENT_SELECTOR_URING_USE_WAITID
|
|
621
|
+
.siginfo = {0},
|
|
622
|
+
#else
|
|
562
623
|
.descriptor = descriptor,
|
|
624
|
+
#endif
|
|
563
625
|
};
|
|
564
626
|
|
|
565
|
-
if (DEBUG) fprintf(stderr, "IO_Event_Selector_URing_process_wait:io_uring_prep_poll_add(%p)\n", (void*)fiber);
|
|
566
627
|
struct io_uring_sqe *sqe = io_get_sqe(selector);
|
|
628
|
+
|
|
629
|
+
#ifdef IO_EVENT_SELECTOR_URING_USE_WAITID
|
|
630
|
+
id_t id;
|
|
631
|
+
idtype_t idtype = process_waitid_type(pid, &id);
|
|
632
|
+
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);
|
|
633
|
+
// `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`:
|
|
634
|
+
io_uring_prep_waitid(sqe, idtype, id, &process_wait_arguments.siginfo, WEXITED | WNOWAIT, 0);
|
|
635
|
+
#else
|
|
636
|
+
if (DEBUG) fprintf(stderr, "IO_Event_Selector_URing_process_wait:io_uring_prep_poll_add(%p)\n", (void*)fiber);
|
|
567
637
|
io_uring_prep_poll_add(sqe, descriptor, POLLIN|POLLHUP|POLLERR);
|
|
638
|
+
#endif
|
|
568
639
|
io_uring_sqe_set_data(sqe, completion);
|
|
569
640
|
io_uring_submit_pending(selector);
|
|
570
641
|
|
data/lib/io/event/interrupt.rb
CHANGED
|
@@ -278,9 +278,7 @@ module IO::Event
|
|
|
278
278
|
# @parameter flags [Integer] Flags to pass to Process::Status.wait.
|
|
279
279
|
# @returns [Process::Status] The status of the waited process.
|
|
280
280
|
def process_wait(fiber, pid, flags)
|
|
281
|
-
|
|
282
|
-
Process::Status.wait(pid, flags)
|
|
283
|
-
end.value
|
|
281
|
+
Selector.process_wait(pid, flags)
|
|
284
282
|
end
|
|
285
283
|
|
|
286
284
|
private def pop_ready
|
data/lib/io/event/selector.rb
CHANGED
|
@@ -40,5 +40,23 @@ module IO::Event
|
|
|
40
40
|
|
|
41
41
|
return selector
|
|
42
42
|
end
|
|
43
|
+
|
|
44
|
+
# Wait for a process to change state, for the cases a selector cannot represent natively (e.g. `pid <= 0`: any child, or a process group). The native selectors integrate process waiting with the event loop using per-process primitives (`pidfd_open`, `EVFILT_PROC`) which can only refer to a single, specific process, and delegate here otherwise.
|
|
45
|
+
#
|
|
46
|
+
# The wait is performed on a separate thread, which has no fiber scheduler and therefore blocks. Joining it via `Thread#value` is fiber-scheduler aware, so the calling fiber yields to the event loop and the reactor keeps running other fibers.
|
|
47
|
+
#
|
|
48
|
+
# @parameter pid [Integer] The process ID (or process group) to wait for.
|
|
49
|
+
# @parameter flags [Integer] Flags to pass to `Process::Status.wait`.
|
|
50
|
+
# @returns [Process::Status] The status of the waited process.
|
|
51
|
+
def self.process_wait(pid, flags)
|
|
52
|
+
thread = ::Thread.new do
|
|
53
|
+
::Process::Status.wait(pid, flags)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
thread.value
|
|
57
|
+
ensure
|
|
58
|
+
# If the calling fiber was interrupted before the wait completed, don't leave the thread running:
|
|
59
|
+
thread&.kill
|
|
60
|
+
end
|
|
43
61
|
end
|
|
44
62
|
end
|
data/lib/io/event/version.rb
CHANGED
data/license.md
CHANGED
|
@@ -19,6 +19,7 @@ Copyright, 2026, by John Hawthorn.
|
|
|
19
19
|
Copyright, 2026, by Italo Brandão.
|
|
20
20
|
Copyright, 2026, by Fletcher Dares.
|
|
21
21
|
Copyright, 2026, by Tavian Barnes.
|
|
22
|
+
Copyright, 2026, by Sharon Rosner.
|
|
22
23
|
|
|
23
24
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
24
25
|
of this software and associated documentation files (the "Software"), to deal
|
data/readme.md
CHANGED
|
@@ -18,6 +18,11 @@ Please see the [project documentation](https://socketry.github.io/io-event/) for
|
|
|
18
18
|
|
|
19
19
|
Please see the [project releases](https://socketry.github.io/io-event/releases/index) for all releases.
|
|
20
20
|
|
|
21
|
+
### v1.19.0
|
|
22
|
+
|
|
23
|
+
- Use `io_uring_prep_waitid` for `process_wait` in the `URing` selector (Linux 6.7+), waiting for child exit directly in the ring instead of polling on a `pidfd`. The child is reaped via `rb_process_status_wait` (using `WEXITED | WNOWAIT`) to construct a correct `Process::Status`, and `process_wait(-1, ...)` / `process_wait(0, ...)` are now supported.
|
|
24
|
+
- Support waiting for any child or a process group (`pid <= 0`) on all selectors. The `EPoll` (`pidfd_open`) and `KQueue` (`EVFILT_PROC`) selectors can only watch a specific process, so these cases now fall back to a blocking wait on a dedicated thread; joining it is fiber-scheduler aware, so the reactor keeps running.
|
|
25
|
+
|
|
21
26
|
### v1.18.0
|
|
22
27
|
|
|
23
28
|
- **Fixed**: Avoid entering a blocking native selector wait when an interrupt is already pending for the current thread.
|
|
@@ -60,10 +65,6 @@ Please see the [project releases](https://socketry.github.io/io-event/releases/i
|
|
|
60
65
|
|
|
61
66
|
- Add bounds checks, in the unlikely event of a user providing an invalid offset that exceeds the buffer size. This prevents potential memory corruption and ensures safe operation when using buffered IO methods.
|
|
62
67
|
|
|
63
|
-
### v1.14.4
|
|
64
|
-
|
|
65
|
-
- Allow `epoll_pwait2` to be disabled via `--disable-epoll_pwait2`.
|
|
66
|
-
|
|
67
68
|
## Contributing
|
|
68
69
|
|
|
69
70
|
We welcome contributions to this project.
|
data/releases.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
+
## v1.19.0
|
|
4
|
+
|
|
5
|
+
- Use `io_uring_prep_waitid` for `process_wait` in the `URing` selector (Linux 6.7+), waiting for child exit directly in the ring instead of polling on a `pidfd`. The child is reaped via `rb_process_status_wait` (using `WEXITED | WNOWAIT`) to construct a correct `Process::Status`, and `process_wait(-1, ...)` / `process_wait(0, ...)` are now supported.
|
|
6
|
+
- Support waiting for any child or a process group (`pid <= 0`) on all selectors. The `EPoll` (`pidfd_open`) and `KQueue` (`EVFILT_PROC`) selectors can only watch a specific process, so these cases now fall back to a blocking wait on a dedicated thread; joining it is fiber-scheduler aware, so the reactor keeps running.
|
|
7
|
+
|
|
3
8
|
## v1.18.0
|
|
4
9
|
|
|
5
10
|
- **Fixed**: Avoid entering a blocking native selector wait when an interrupt is already pending for the current thread.
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: io-event
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.19.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Williams
|
|
@@ -20,6 +20,7 @@ authors:
|
|
|
20
20
|
- John Hawthorn
|
|
21
21
|
- Luke Gruber
|
|
22
22
|
- Pavel Rosický
|
|
23
|
+
- Sharon Rosner
|
|
23
24
|
- Stan Hu
|
|
24
25
|
- Stanislav (Stas) Katkov
|
|
25
26
|
- William T. Nelson
|
metadata.gz.sig
CHANGED
|
Binary file
|