summaryrefslogtreecommitdiff
path: root/process.c
diff options
context:
space:
mode:
authorKJ Tsanaktsidis <kj@kjtsanaktsidis.id.au>2023-08-18 23:19:21 +1000
committernagachika <nagachika@ruby-lang.org>2023-09-09 18:51:25 +0900
commit0b7a4fbaa9c56d2c67d00d86c69f9e5c71803267 (patch)
treec5cd8199be8058accd4405b70f6c2f50a7ddbf73 /process.c
parentbbdffef63c55a594e604bd1ec5e90bffc6ea4c12 (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 dbe2be5b56..4786f52deb 100644
--- a/process.c
+++ b/process.c
@@ -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)