diff options
| author | Stan Hu <stanhu@gmail.com> | 2024-05-17 17:05:48 +0900 |
|---|---|---|
| committer | nagachika <nagachika@ruby-lang.org> | 2024-08-18 18:02:09 +0900 |
| commit | 65fed1c3e439bc47bcf6ec884431a86cb9ebd1dc (patch) | |
| tree | 236e877de69232fc2b0adc30bf959d680b8eac46 /process.c | |
| parent | 50399eebd96c76ce808ea4d84fe39693f585a531 (diff) | |
Allow waitpid(-1, Process::WNOHANG) to be woken if a waitpid(pid) is
...pending
If two threads are running, with one calling waitpid(-1, Process::WNOHANG),
and another calling waitpid($some_pid), and then
$some_other_pid exits, we would expect the waitpid(-1,
Process::WNOHANG) call to retrieve that exit status. However, it
cannot actually do so until $some_pid _also_ exits.
This patch fixes the issue by calling do_waitpid unconditionally in
waitpid_wait; this will ensure that a waitpid -1 actually reaps
something (after first checking that no PID-directed call wants the
process).
[Bug #20490]
Diffstat (limited to 'process.c')
| -rw-r--r-- | process.c | 24 |
1 files changed, 16 insertions, 8 deletions
@@ -1270,6 +1270,7 @@ waitpid_cleanup(VALUE x) return Qfalse; } +#if RUBY_SIGCHLD static void waitpid_wait(struct waitpid_state *w) { @@ -1283,10 +1284,17 @@ waitpid_wait(struct waitpid_state *w) */ rb_native_mutex_lock(&vm->waitpid_lock); - if (w->pid > 0 || ccan_list_empty(&vm->waiting_pids)) { - w->ret = do_waitpid(w->pid, &w->status, w->options | WNOHANG); + if (w->options & WNOHANG && w->pid <= 0) { + /* In the case of WNOHANG wait for a group, make sure there isn't a zombie child + * whose PID we are directly waiting for in another call to waitpid. If there is, + * we will reap it via a call to waitpid($pid) with this call to waitpid_each. */ + waitpid_each(vm, &vm->waiting_pids); + /* _now_ it's safe to call do_waitpid, without risk of stealing the wait from + * another directed call. */ } + w->ret = do_waitpid(w->pid, &w->status, w->options | WNOHANG); + if (w->ret) { if (w->ret == -1) w->errnum = errno; } @@ -1308,6 +1316,7 @@ waitpid_wait(struct waitpid_state *w) rb_ensure(waitpid_sleep, (VALUE)w, waitpid_cleanup, (VALUE)w); } } +#endif static void * waitpid_blocking_no_SIGCHLD(void *x) @@ -1350,12 +1359,11 @@ rb_process_status_wait(rb_pid_t pid, int flags) waitpid_state_init(&waitpid_state, pid, flags); waitpid_state.ec = GET_EC(); - if (WAITPID_USE_SIGCHLD) { - waitpid_wait(&waitpid_state); - } - else { - waitpid_no_SIGCHLD(&waitpid_state); - } +#if WAITPID_USE_SIGCHLD + waitpid_wait(&waitpid_state); +#else + waitpid_no_SIGCHLD(&waitpid_state); +#endif if (waitpid_state.ret == 0) return Qnil; |
