diff options
| author | KJ Tsanaktsidis <kj@kjtsanaktsidis.id.au> | 2023-08-18 23:19:21 +1000 |
|---|---|---|
| committer | nagachika <nagachika@ruby-lang.org> | 2023-09-09 18:51:25 +0900 |
| commit | 0b7a4fbaa9c56d2c67d00d86c69f9e5c71803267 (patch) | |
| tree | c5cd8199be8058accd4405b70f6c2f50a7ddbf73 /process.c | |
| parent | bbdffef63c55a594e604bd1ec5e90bffc6ea4c12 (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.c | 20 |
1 files changed, 15 insertions, 5 deletions
@@ -1168,7 +1168,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 ccan_list_head *head) +waitpid_each(rb_vm_t *vm, struct ccan_list_head *head) { struct waitpid_state *w = 0, *next; @@ -1178,6 +1178,18 @@ waitpid_each(struct ccan_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; + ccan_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; ccan_list_del_init(&w->wnode); waitpid_signal(w); @@ -1192,10 +1204,8 @@ ruby_waitpid_all(rb_vm_t *vm) { #if RUBY_SIGCHLD rb_native_mutex_lock(&vm->waitpid_lock); - waitpid_each(&vm->waiting_pids); - if (ccan_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 (ccan_list_empty(&vm->waiting_pids) && ccan_list_empty(&vm->waiting_grps)) { while (ruby_nocldwait && do_waitpid(-1, 0, WNOHANG) > 0) |
