### Description The following code: ```php while ($await_processes) { $await_processes_copy = $await_processes; foreach ($await_processes_copy as $worker_id => $process) { $proc_status = proc_get_status($process['process']); if ($proc_status === false) { throw new MultiProcessHydraException("Failed to get proc status of worker $worker_id"); } if ($proc_status['running'] === false) { unset($await_processes[$worker_id]); $stdout = stream_get_contents(fopen($process['files']['stdout'], 'r')); $stderr = stream_get_contents(fopen($process['files']['stderr'], 'r')); $exit_code = $proc_status['exitcode']; $log_message = [ 'message' => "Awaited process", 'job_class' => $this->worker_class, 'stdout' => $stdout, 'stderr' => $stderr, 'worker_id' => $worker_id, 'exit_code' => $exit_code, ]; $logger->log($log_message); // If any child-process exits with a non-zero code (such as a fatal, uncatchable error) // process this individual worker as a failure, but continue to process the responses from // each other process. if ($exit_code !== 0) { $worker_responses[] = [[ 'error' => "Worker process $worker_id exited with non-zero exit code: $exit_code. " . "Stderr: $stderr", ]]; } else { $worker_responses[] = $cache->getCacheValue($process['files']['cache_file'])->asArray(); } unset($process['files']['cache_file']); foreach ($process['files'] as $_ => $file) { unlink($file); } } } } <?php ``` Resulted in this output: ``` Uncaught Exception: [MultiProcessHydraException] Worker process mp-hydra-6b0a280c-2428-41f5-b393-11e4477b4e9d-7 exited with non-zero exit code: -1. ...(multiple process exited with -1) ``` But I expected this output instead: The parent process checked the exitcode of child process to be 0 and finish the job, because from our log, all the subtasks were successfully finished. ### Investigation PHP 8.3 installs an internal SIGCHLD signal handler (not present in PHP 8.1) as part of the pcntl extension. When a child process exits, this handler intercepts the SIGCHLD signal and interrupts the waitpid() syscall inside proc_get_status(). Verified by: | Test | PHP 8.1 | PHP 8.3 | |------|---------|---------| | SIGCHLD disposition | SIG_DFL | CUSTOM_HANDLER | | proc_open("exit(42)") + proc_get_status() | exitcode: 42 | exitcode: -1 | | Same with php -n (no extensions) | exitcode: 42 | [Still broken — in PHP core](exitcode: -1) | | With pcntl_signal(SIGCHLD, SIG_DFL) first | exitcode: 42 | exitcode: 42 | #### Temporary Fix Added pcntl_signal(SIGCHLD, SIG_DFL) before spawning workers in MultiProcessHydra::spawn(), resetting SIGCHLD to default behavior so proc_get_status() can reap children without interruption. References: - [php.net/proc_get_status](https://www.php.net/proc_get_status) — PHP 8.3 cached field documentation - [php/php-src#10250](https://github.com/php/php-src/pull/10250) - [php/php-src#10239](https://github.com/php/php-src/issues/10239) ### PHP Version ```plain PHP 8.3.25 (cli) (built: Jan 5 2026 18:10:06) (NTS) Copyright (c) The PHP Group Zend Engine v4.3.25, Copyright (c) Zend Technologies with Zend OPcache v8.3.25, Copyright (c), by Zend Technologies ``` ### Operating System Amazon Linux 2 (AL2)