io-event 1.19.1 → 1.19.2
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 +1 -0
- data/ext/io/event/selector/epoll.c +4 -4
- data/ext/io/event/selector/kqueue.c +4 -4
- data/ext/io/event/selector/uring.c +41 -4
- data/lib/io/event/version.rb +1 -1
- data/readme.md +4 -4
- data/releases.md +4 -0
- data.tar.gz.sig +0 -0
- metadata +1 -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: ce297a0d80038a28c3814c5a1bb227453ad27b30cdf253cd20c9abfaed90dd59
|
|
4
|
+
data.tar.gz: 7017435d4b51d770524b289dedad72f39a4f166290af2767ea7a32123396f471
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d214fc9c5cd13f627e1f1be6676e1b86daa013ee27d360323df85b4f7f33d267f2f61bd2db7f8dcbbe46f8b3e448d0aca995a1b3f23cd8275048066f3c03ef05
|
|
7
|
+
data.tar.gz: 6b7f4d739d1b5bb13648575fcb0d57b2dc5965b29ae136f91cd03a200cfceb08a191476f528a487807f37b178b8e1a25021dba9b00929cc68ca7166cf081c034
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/ext/extconf.rb
CHANGED
|
@@ -847,8 +847,8 @@ int select_blocking_allowed(struct timespec * timespec) {
|
|
|
847
847
|
return 0;
|
|
848
848
|
}
|
|
849
849
|
|
|
850
|
-
#ifndef
|
|
851
|
-
// On Rubies without `
|
|
850
|
+
#ifndef RB_NOGVL_PENDING_INTR_FAIL
|
|
851
|
+
// On Rubies without `RB_NOGVL_PENDING_INTR_FAIL`, `rb_thread_call_without_gvl2` can enter an indefinite native wait even if a masked interrupt is already pending for this thread. This is the last safe point to avoid that wait: we still hold the GVL, so the pending interrupt queue cannot change concurrently before we decide whether to enter the blocking path.
|
|
852
852
|
if (IO_Event_Selector_pending_interrupt()) {
|
|
853
853
|
return 0;
|
|
854
854
|
}
|
|
@@ -919,8 +919,8 @@ static
|
|
|
919
919
|
int select_internal_without_gvl(struct select_arguments *arguments) {
|
|
920
920
|
arguments->result = -1;
|
|
921
921
|
arguments->selector->blocked = 1;
|
|
922
|
-
#ifdef
|
|
923
|
-
rb_nogvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0, RB_NOGVL_INTR_FAIL |
|
|
922
|
+
#ifdef RB_NOGVL_PENDING_INTR_FAIL
|
|
923
|
+
rb_nogvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0, RB_NOGVL_INTR_FAIL | RB_NOGVL_PENDING_INTR_FAIL);
|
|
924
924
|
#else
|
|
925
925
|
rb_thread_call_without_gvl2(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
|
|
926
926
|
#endif
|
|
@@ -851,8 +851,8 @@ int select_blocking_allowed(struct timespec * timespec) {
|
|
|
851
851
|
return 0;
|
|
852
852
|
}
|
|
853
853
|
|
|
854
|
-
#ifndef
|
|
855
|
-
// On Rubies without `
|
|
854
|
+
#ifndef RB_NOGVL_PENDING_INTR_FAIL
|
|
855
|
+
// On Rubies without `RB_NOGVL_PENDING_INTR_FAIL`, `rb_thread_call_without_gvl2` can enter an indefinite native wait even if a masked interrupt is already pending for this thread. This is the last safe point to avoid that wait: we still hold the GVL, so the pending interrupt queue cannot change concurrently before we decide whether to enter the blocking path.
|
|
856
856
|
if (IO_Event_Selector_pending_interrupt()) {
|
|
857
857
|
return 0;
|
|
858
858
|
}
|
|
@@ -888,8 +888,8 @@ int select_internal_without_gvl(struct select_arguments *arguments) {
|
|
|
888
888
|
arguments->result = -1;
|
|
889
889
|
arguments->selector->blocked = 1;
|
|
890
890
|
|
|
891
|
-
#ifdef
|
|
892
|
-
rb_nogvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0, RB_NOGVL_INTR_FAIL |
|
|
891
|
+
#ifdef RB_NOGVL_PENDING_INTR_FAIL
|
|
892
|
+
rb_nogvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0, RB_NOGVL_INTR_FAIL | RB_NOGVL_PENDING_INTR_FAIL);
|
|
893
893
|
#else
|
|
894
894
|
rb_thread_call_without_gvl2(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
|
|
895
895
|
#endif
|
|
@@ -546,6 +546,29 @@ struct process_wait_arguments {
|
|
|
546
546
|
#endif
|
|
547
547
|
};
|
|
548
548
|
|
|
549
|
+
#if defined(IO_EVENT_SELECTOR_URING_USE_WAITID) && defined(HAVE_RB_PROCESS_STATUS_FOR)
|
|
550
|
+
static inline int process_wait_status_exited(int exit_status) {
|
|
551
|
+
return (exit_status & 0xff) << 8;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
static inline int process_wait_status_signaled(int term_signal) {
|
|
555
|
+
return term_signal & 0x7f;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
static inline int process_wait_status_from_siginfo(const siginfo_t *siginfo) {
|
|
559
|
+
switch (siginfo->si_code) {
|
|
560
|
+
case CLD_EXITED:
|
|
561
|
+
return process_wait_status_exited(siginfo->si_status);
|
|
562
|
+
case CLD_KILLED:
|
|
563
|
+
return process_wait_status_signaled(siginfo->si_status);
|
|
564
|
+
case CLD_DUMPED:
|
|
565
|
+
return process_wait_status_signaled(siginfo->si_status) | 0x80;
|
|
566
|
+
default:
|
|
567
|
+
return 0;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
#endif
|
|
571
|
+
|
|
549
572
|
static
|
|
550
573
|
VALUE process_wait_transfer(VALUE _arguments) {
|
|
551
574
|
struct process_wait_arguments *arguments = (struct process_wait_arguments *)_arguments;
|
|
@@ -559,11 +582,20 @@ VALUE process_wait_transfer(VALUE _arguments) {
|
|
|
559
582
|
|
|
560
583
|
if (result < 0) {
|
|
561
584
|
// 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:
|
|
585
|
+
#ifdef HAVE_RB_PROCESS_STATUS_FOR
|
|
586
|
+
return rb_process_status_for(-1, 0, -result);
|
|
587
|
+
#else
|
|
562
588
|
return IO_Event_Selector_process_status_reap(arguments->pid, arguments->flags);
|
|
589
|
+
#endif
|
|
563
590
|
}
|
|
564
591
|
|
|
592
|
+
#ifdef HAVE_RB_PROCESS_STATUS_FOR
|
|
593
|
+
// The `waitid` operation already reaped the child. Convert the `siginfo_t` result directly into the Ruby process status value:
|
|
594
|
+
return rb_process_status_for(arguments->siginfo.si_pid, process_wait_status_from_siginfo(&arguments->siginfo), 0);
|
|
595
|
+
#else
|
|
565
596
|
// 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`:
|
|
566
597
|
return IO_Event_Selector_process_status_reap(arguments->siginfo.si_pid, arguments->flags);
|
|
598
|
+
#endif
|
|
567
599
|
#else
|
|
568
600
|
if (arguments->waiting->result) {
|
|
569
601
|
return IO_Event_Selector_process_status_reap(arguments->pid, arguments->flags);
|
|
@@ -632,8 +664,13 @@ VALUE IO_Event_Selector_URing_process_wait(VALUE self, VALUE fiber, VALUE _pid,
|
|
|
632
664
|
id_t id;
|
|
633
665
|
idtype_t idtype = process_waitid_type(pid, &id);
|
|
634
666
|
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);
|
|
667
|
+
#ifdef HAVE_RB_PROCESS_STATUS_FOR
|
|
668
|
+
// Reap the child directly; the completion contains enough information to construct the Ruby process status value:
|
|
669
|
+
io_uring_prep_waitid(sqe, idtype, id, &process_wait_arguments.siginfo, WEXITED, 0);
|
|
670
|
+
#else
|
|
635
671
|
// `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`:
|
|
636
672
|
io_uring_prep_waitid(sqe, idtype, id, &process_wait_arguments.siginfo, WEXITED | WNOWAIT, 0);
|
|
673
|
+
#endif
|
|
637
674
|
#else
|
|
638
675
|
if (DEBUG) fprintf(stderr, "IO_Event_Selector_URing_process_wait:io_uring_prep_poll_add(%p)\n", (void*)fiber);
|
|
639
676
|
io_uring_prep_poll_add(sqe, descriptor, POLLIN|POLLHUP|POLLERR);
|
|
@@ -1211,8 +1248,8 @@ int select_blocking_allowed(struct __kernel_timespec *timespec) {
|
|
|
1211
1248
|
return 0;
|
|
1212
1249
|
}
|
|
1213
1250
|
|
|
1214
|
-
#ifndef
|
|
1215
|
-
// On Rubies without `
|
|
1251
|
+
#ifndef RB_NOGVL_PENDING_INTR_FAIL
|
|
1252
|
+
// On Rubies without `RB_NOGVL_PENDING_INTR_FAIL`, `rb_thread_call_without_gvl2` can enter an indefinite native wait even if a masked interrupt is already pending for this thread. This is the last safe point to avoid that wait: we still hold the GVL, so the pending interrupt queue cannot change concurrently before we decide whether to enter the blocking path.
|
|
1216
1253
|
if (IO_Event_Selector_pending_interrupt()) {
|
|
1217
1254
|
return 0;
|
|
1218
1255
|
}
|
|
@@ -1259,8 +1296,8 @@ int select_internal_without_gvl(struct select_arguments *arguments) {
|
|
|
1259
1296
|
|
|
1260
1297
|
arguments->result = -EINTR;
|
|
1261
1298
|
selector->blocked = 1;
|
|
1262
|
-
#ifdef
|
|
1263
|
-
rb_nogvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0, RB_NOGVL_INTR_FAIL |
|
|
1299
|
+
#ifdef RB_NOGVL_PENDING_INTR_FAIL
|
|
1300
|
+
rb_nogvl(select_internal, (void *)arguments, RUBY_UBF_IO, 0, RB_NOGVL_INTR_FAIL | RB_NOGVL_PENDING_INTR_FAIL);
|
|
1264
1301
|
#else
|
|
1265
1302
|
rb_thread_call_without_gvl2(select_internal, (void *)arguments, RUBY_UBF_IO, 0);
|
|
1266
1303
|
#endif
|
data/lib/io/event/version.rb
CHANGED
data/readme.md
CHANGED
|
@@ -18,6 +18,10 @@ 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.2
|
|
22
|
+
|
|
23
|
+
- Use `rb_process_status_for` when available to construct `URing` `process_wait` results directly from `waitid`, avoiding the extra reap syscall previously needed to build a `Process::Status`.
|
|
24
|
+
|
|
21
25
|
### v1.19.1
|
|
22
26
|
|
|
23
27
|
- Fix `Process.waitall` / `Process.detach` under the `URing` selector: when `io_uring`'s `waitid` reported an error (e.g. `ECHILD` when there are no more children), the `process_wait` hook raised instead of returning the error as a `Process::Status`, so callers that expect `waitpid` to *report* "no more children" rather than raise would fail.
|
|
@@ -61,10 +65,6 @@ Please see the [project releases](https://socketry.github.io/io-event/releases/i
|
|
|
61
65
|
- Correctly handle short `io_uring_submit()` results in the `URing` selector. `io_uring_submit()` returns the number of SQEs actually accepted by the kernel and can be short (SQE prep errors, `ENOMEM`, transient `EAGAIN`); the old accounting reset `pending = 0` on any success and silently lost track of unsubmitted SQEs.
|
|
62
66
|
- Enable `IORING_SETUP_SUBMIT_ALL` (kernel 5.18+) on the `URing` selector so the kernel keeps processing the rest of an SQE batch past individual errors, reducing the frequency of short submits in practice.
|
|
63
67
|
|
|
64
|
-
### v1.15.1
|
|
65
|
-
|
|
66
|
-
- Simplify closed-IO handling in the `Select` selector: rely on Ruby 4's `rb_thread_io_close_interrupt` to wake fibers waiting on a descriptor that's been closed, removing a custom error-recovery path that could mis-attribute `IOError` / `Errno::EBADF` to the wrong waiter.
|
|
67
|
-
|
|
68
68
|
## Contributing
|
|
69
69
|
|
|
70
70
|
We welcome contributions to this project.
|
data/releases.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
+
## v1.19.2
|
|
4
|
+
|
|
5
|
+
- Use `rb_process_status_for` when available to construct `URing` `process_wait` results directly from `waitid`, avoiding the extra reap syscall previously needed to build a `Process::Status`.
|
|
6
|
+
|
|
3
7
|
## v1.19.1
|
|
4
8
|
|
|
5
9
|
- Fix `Process.waitall` / `Process.detach` under the `URing` selector: when `io_uring`'s `waitid` reported an error (e.g. `ECHILD` when there are no more children), the `process_wait` hook raised instead of returning the error as a `Process::Status`, so callers that expect `waitpid` to *report* "no more children" rather than raise would fail.
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
metadata.gz.sig
CHANGED
|
Binary file
|