summaryrefslogtreecommitdiff
path: root/process.c
diff options
context:
space:
mode:
authorKJ Tsanaktsidis <kj@kjtsanaktsidis.id.au>2023-08-18 23:19:21 +1000
committerusa <usa@garbagecollect.jp>2023-09-05 20:19:55 +0900
commitc08fdc68383ee368c18e15e298502e6ee0089e18 (patch)
tree65739d00f132bcb6ad9ef118a29fedce8941aeb7 /process.c
parenta8670865c0c15f03b56cb09cbcc0ffc91c12807f (diff)
Allow waitpid(-1) to be woken if a waitpid(pid) call is pending
If two threads are running, with one calling waitpid(-1), and another calling waitpid($some_pid), and then $some_other_pid exits, we would expect the waitpid(-1) call to retrieve that exit status; however, it cannot actually do so until $some_pid _also_ exits. This patch fixes the issue by unconditionally checking for pending process group waits on SIGCHLD, and then allowing pending pid-only waits to "steal" the notification. [Fixes #19387]
Diffstat (limited to 'process.c')
-rw-r--r--process.c20
1 files changed, 15 insertions, 5 deletions
diff --git a/process.c b/process.c
index 399f0d7532..97fa336b56 100644
--- a/process.c
+++ b/process.c
@@ -1120,7 +1120,7 @@ rb_sigwait_fd_migrate(rb_vm_t *vm)
extern volatile unsigned int ruby_nocldwait; /* signal.c */
/* called by timer thread or thread which acquired sigwait_fd */
static void
-waitpid_each(struct list_head *head)
+waitpid_each(rb_vm_t *vm, struct list_head *head)
{
struct waitpid_state *w = 0, *next;
@@ -1130,6 +1130,18 @@ waitpid_each(struct list_head *head)
if (!ret) continue;
if (ret == -1) w->errnum = errno;
+ if (w->pid <= 0) {
+ /* when waiting for a group of processes, make sure a waiter for a
+ * specific pid is given that event in preference */
+ struct waitpid_state *w_inner = 0, *next_inner;
+ list_for_each_safe(&vm->waiting_pids, w_inner, next_inner, wnode) {
+ if (w_inner->pid == ret) {
+ /* signal this one instead */
+ w = w_inner;
+ }
+ }
+ }
+
w->ret = ret;
list_del_init(&w->wnode);
waitpid_signal(w);
@@ -1144,10 +1156,8 @@ ruby_waitpid_all(rb_vm_t *vm)
{
#if RUBY_SIGCHLD
rb_native_mutex_lock(&vm->waitpid_lock);
- waitpid_each(&vm->waiting_pids);
- if (list_empty(&vm->waiting_pids)) {
- waitpid_each(&vm->waiting_grps);
- }
+ waitpid_each(vm, &vm->waiting_pids);
+ waitpid_each(vm, &vm->waiting_grps);
/* emulate SA_NOCLDWAIT */
if (list_empty(&vm->waiting_pids) && list_empty(&vm->waiting_grps)) {
while (ruby_nocldwait && do_waitpid(-1, 0, WNOHANG) > 0)