diff --git a/ext/pcntl/pcntl.c b/ext/pcntl/pcntl.c index 481949ce93016..19460ad8b53fd 100644 --- a/ext/pcntl/pcntl.c +++ b/ext/pcntl/pcntl.c @@ -1048,68 +1048,26 @@ static void pcntl_signal_handler(int signo, siginfo_t *siginfo, void *context) static void pcntl_signal_handler(int signo) #endif { - struct php_pcntl_pending_signal *psig_first = PCNTL_G(spares); - if (!psig_first) { + struct php_pcntl_pending_signal *psig = PCNTL_G(spares); + if (!psig) { /* oops, too many signals for us to track, so we'll forget about this one */ return; } + PCNTL_G(spares) = psig->next; - struct php_pcntl_pending_signal *psig = NULL; - - /* Standard signals may be merged into a single one. - * POSIX specifies that SIGCHLD has the si_pid field (https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html), - * so we'll handle the merging for that signal. - * See also: https://www.gnu.org/software/libc/manual/html_node/Merged-Signals.html */ - if (signo == SIGCHLD) { - /* Note: The first waitpid result is not necessarily the pid that was passed above! - * We therefore cannot avoid the first waitpid() call. */ - int status; - pid_t pid; - while (true) { - do { - errno = 0; - /* Although Linux specifies that WNOHANG will never result in EINTR, POSIX doesn't say so: - * https://pubs.opengroup.org/onlinepubs/9699919799/functions/waitpid.html */ - pid = waitpid(-1, &status, WNOHANG | WUNTRACED); - } while (pid <= 0 && errno == EINTR); - if (pid <= 0) { - if (UNEXPECTED(!psig)) { - /* The child might've been consumed by another thread and will be handled there. */ - return; - } - break; - } - - psig = psig ? psig->next : psig_first; - psig->signo = signo; - -#ifdef HAVE_STRUCT_SIGINFO_T - psig->siginfo = *siginfo; - psig->siginfo.si_pid = pid; -#endif - - if (UNEXPECTED(!psig->next)) { - break; - } - } - } else { - psig = psig_first; - psig->signo = signo; + psig->signo = signo; + psig->next = NULL; #ifdef HAVE_STRUCT_SIGINFO_T - psig->siginfo = *siginfo; + psig->siginfo = *siginfo; #endif - } - - PCNTL_G(spares) = psig->next; - psig->next = NULL; /* the head check is important, as the tick handler cannot atomically clear both * the head and tail */ if (PCNTL_G(head) && PCNTL_G(tail)) { - PCNTL_G(tail)->next = psig_first; + PCNTL_G(tail)->next = psig; } else { - PCNTL_G(head) = psig_first; + PCNTL_G(head) = psig; } PCNTL_G(tail) = psig; PCNTL_G(pending_signals) = 1; diff --git a/ext/pcntl/tests/gh11498.phpt b/ext/pcntl/tests/waiting_on_sigchild_pcntl_wait.phpt similarity index 79% rename from ext/pcntl/tests/gh11498.phpt rename to ext/pcntl/tests/waiting_on_sigchild_pcntl_wait.phpt index 4a9f87a94d82e..482888cfadd97 100644 --- a/ext/pcntl/tests/gh11498.phpt +++ b/ext/pcntl/tests/waiting_on_sigchild_pcntl_wait.phpt @@ -1,5 +1,5 @@ --TEST-- -GH-11498 (SIGCHLD is not always returned from proc_open) +Waiting on SIGCHLD with a pcntl_wait() loop --EXTENSIONS-- pcntl --SKIPIF-- @@ -14,8 +14,10 @@ $processes = []; pcntl_async_signals(true); pcntl_signal(SIGCHLD, function($sig, $info) use (&$processes) { - echo "SIGCHLD\n"; - unset($processes[$info['pid']]); + while (($pid = pcntl_wait($status, WUNTRACED | WNOHANG)) > 0) { + echo "SIGCHLD\n"; + unset($processes[$pid]); + } }, false); for ($i = 0; $i <= 5; $i++) {