/** * Start the child processes. * * This should only be called from the command line. It should be called * as early as possible during execution. * * This will return 'child' in the child processes. In the parent process, * it will run until all the child processes exit or a TERM signal is * received. It will then return 'done'. */ public function start() { // Trap SIGTERM pcntl_signal(SIGTERM, array($this, 'handleTermSignal'), false); do { // Start child processes if ($this->procsToStart) { if ($this->forkWorkers($this->procsToStart) == 'child') { return 'child'; } $this->procsToStart = 0; } // Check child status $status = false; $deadPid = pcntl_wait($status); if ($deadPid > 0) { // Respond to child process termination unset($this->children[$deadPid]); if ($this->flags & self::RESTART_ON_ERROR) { if (pcntl_wifsignaled($status)) { // Restart if the signal was abnormal termination // Don't restart if it was deliberately killed $signal = pcntl_wtermsig($status); if (in_array($signal, self::$restartableSignals)) { echo "Worker exited with signal {$signal}, restarting\n"; $this->procsToStart++; } } elseif (pcntl_wifexited($status)) { // Restart on non-zero exit status $exitStatus = pcntl_wexitstatus($status); if ($exitStatus > 0) { echo "Worker exited with status {$exitStatus}, restarting\n"; $this->procsToStart++; } } } // Throttle restarts if ($this->procsToStart) { usleep(500000); } } // Run signal handlers if (function_exists('pcntl_signal_dispatch')) { pcntl_signal_dispatch(); } else { declare (ticks=1) { $status = $status; } } // Respond to TERM signal if ($this->termReceived) { foreach ($this->children as $childPid => $unused) { posix_kill($childPid, SIGTERM); } $this->termReceived = false; } } while (count($this->children)); pcntl_signal(SIGTERM, SIG_DFL); return 'done'; }
function pleac_Running_Another_Program() { // Run a simple command and retrieve its result code. exec("vi {$myfile}", $output, $result_code); // ----------------------------- // Use the shell to perform redirection. exec('cmd1 args | cmd2 | cmd3 >outfile'); exec('cmd args <infile >outfile 2>errfile'); // ----------------------------- // Run a command, handling its result code or signal. $pid = pcntl_fork(); if ($pid == -1) { die('cannot fork'); } elseif ($pid) { pcntl_waitpid($pid, $status); if (pcntl_wifexited($status)) { $status = pcntl_wexitstatus($status); echo "program exited with status {$status}\n"; } elseif (pcntl_wifsignaled($status)) { $signal = pcntl_wtermsig($status); echo "program killed by signal {$signal}\n"; } elseif (pcntl_wifstopped($status)) { $signal = pcntl_wstopsig($status); echo "program stopped by signal {$signal}\n"; } } else { pcntl_exec($program, $args); } // ----------------------------- // Run a command while blocking interrupt signals. $pid = pcntl_fork(); if ($pid == -1) { die('cannot fork'); } elseif ($pid) { // parent catches INT and berates user declare (ticks=1); function handle_sigint($signal) { echo "Tsk tsk, no process interruptus\n"; } pcntl_signal(SIGINT, 'handle_sigint'); while (!pcntl_waitpid($pid, $status, WNOHANG)) { } } else { // child ignores INT and does its thing pcntl_signal(SIGINT, SIG_IGN); pcntl_exec('/bin/sleep', array('10')); } // ----------------------------- // Since there is no direct access to execv() and friends, and // pcntl_exec() won't let us supply an alternate program name // in the argument list, there is no way to run a command with // a different name in the process table. }
/** * Analyzes the passed status code of a process and sets properties accordingly. * * @param int $status * @author Dan Homorodean <*****@*****.**> */ public function __construct($status) { if (pcntl_wifexited($status)) { $this->status = self::STATUS_EXITED; $this->exitCode = pcntl_wexitstatus($status); } elseif (pcntl_wifsignaled($status)) { $this->status = self::STATUS_SIGNALED; $this->reasonSignal = pcntl_wtermsig($status); } elseif (pcntl_wifstopped($status)) { $this->status = self::STATUS_STOPPED; $this->reasonSignal = pcntl_wstopsig($status); } }
function test_exit_signal() { print "\n\nTesting pcntl_wifsignaled...."; $pid = pcntl_fork(); if ($pid == 0) { sleep(10); exit; } else { $options = 0; posix_kill($pid, SIGTERM); pcntl_waitpid($pid, $status, $options); if (pcntl_wifsignaled($status)) { $signal_print = pcntl_wtermsig($status); if ($signal_print == SIGTERM) { $signal_print = "SIGTERM"; } print "\nProcess was terminated by signal : " . $signal_print; } } }
private function processSignalChild() { $childrenQty = 0; while (true) { $exitedWorkerPid = pcntl_waitpid(-1, $status, WNOHANG); if ($exitedWorkerPid === 0) { Logger::getLogger('queue')->debug("process SIGCHLD complete, childrenQty={$childrenQty}"); return; } if ($exitedWorkerPid === -1) { $error_number = pcntl_get_last_error(); $error = "[{$error_number}] " . pcntl_strerror($error_number); $message = "Can't wait pid, error: '{$error}'"; if ($error_number === PCNTL_ECHILD) { Logger::getLogger('queue')->debug($message); } else { Logger::getLogger('queue')->error($message); } return; } Logger::getLogger('queue')->debug("exitedWorkerPid={$exitedWorkerPid}"); if (!isset($this->childrenInfo[$exitedWorkerPid])) { Logger::getLogger('queue')->error('pcntl_waitpid return unknown pid:' . var_export($exitedWorkerPid, true), new Exception()); continue; } $isExited = false; $queueNick = $this->childrenInfo[$exitedWorkerPid]['queueNick']; if (pcntl_wifexited($status)) { $isExited = true; $code = pcntl_wexitstatus($status); Logger::getLogger('queue')->debug("exitCode={$code}"); if (!isset($this->workersErrorsQty[$queueNick])) { $this->workersErrorsQty[$queueNick] = 0; } if ($code) { ++$this->workersErrorsQty[$queueNick]; Logger::getLogger('queue')->error("worker pid={$exitedWorkerPid} for queue {$queueNick} exit with code {$code}"); } else { $this->workersErrorsQty[$queueNick] = 0; } if ($this->workersErrorsQty[$queueNick] > $this->maxWorkersQty * 0.5) { $message = "queue {$queueNick} worker errors qty = {$this->workersErrorsQty[$queueNick]}. Disable this queue."; $this->queuesInfo[$queueNick]['state'] = 'error'; $this->queuesInfo[$queueNick]['message'] = $message; Logger::getLogger('queue')->error($message); if (isset($this->queues[$queueNick])) { $this->queues[$queueNick]->unlockTasks(); unset($this->queues[$queueNick]); } } } if (pcntl_wifsignaled($status)) { $isExited = true; $errorSignal = pcntl_wtermsig($status); Logger::getLogger('queue')->error("{$exitedWorkerPid} terminate by signal {$errorSignal}"); } if (pcntl_wifstopped($status)) { $stopSignal = pcntl_wstopsig($status); Logger::getLogger('queue')->error("{$exitedWorkerPid} stop by signal {$stopSignal}"); } if ($isExited) { --$this->workersQty; if (isset($this->queuesInfo[$queueNick])) { --$this->queuesInfo[$queueNick]['activeWorkersQty']; Logger::getLogger('queue')->debug("worker complete, workersQty={$this->workersQty},queue={$queueNick},activeWorkersQty={$this->queuesInfo[$queueNick]['activeWorkersQty']}"); } $this->freeWorkersNumbers[] = $this->childrenInfo[$exitedWorkerPid]['workerNumber']; unset($this->childrenInfo[$exitedWorkerPid]); } ++$childrenQty; } }
/** * Spawn one or more background processes and let them start running. * Each individual process will execute whatever's in the runThread() * method, which should be overridden. * * Child processes will be automatically respawned when they exit. * * @todo possibly allow for not respawning on "normal" exits... * though ParallelizingDaemon is probably better for workloads * that have forseeable endpoints. */ function run() { $this->initPipes(); $children = array(); for ($i = 1; $i <= $this->threads; $i++) { $pid = pcntl_fork(); if ($pid < 0) { $this->log(LOG_ERROR, "Couldn't fork for thread {$i}; aborting\n"); exit(1); } else { if ($pid == 0) { $this->initAndRunChild($i); } else { $this->log(LOG_INFO, "Spawned thread {$i} as pid {$pid}"); $children[$i] = $pid; } } sleep(common_config('queue', 'spawndelay')); } $this->log(LOG_INFO, "Waiting for children to complete."); while (count($children) > 0) { $status = null; $pid = pcntl_wait($status); if ($pid > 0) { $i = array_search($pid, $children); if ($i === false) { $this->log(LOG_ERR, "Ignoring exit of unrecognized child pid {$pid}"); continue; } if (pcntl_wifexited($status)) { $exitCode = pcntl_wexitstatus($status); $info = "status {$exitCode}"; } else { if (pcntl_wifsignaled($status)) { $exitCode = self::EXIT_ERR; $signal = pcntl_wtermsig($status); $info = "signal {$signal}"; } } unset($children[$i]); if ($this->shouldRespawn($exitCode)) { $this->log(LOG_INFO, "Thread {$i} pid {$pid} exited with {$info}; respawing."); $pid = pcntl_fork(); if ($pid < 0) { $this->log(LOG_ERROR, "Couldn't fork to respawn thread {$i}; aborting thread.\n"); } else { if ($pid == 0) { $this->initAndRunChild($i); } else { $this->log(LOG_INFO, "Respawned thread {$i} as pid {$pid}"); $children[$i] = $pid; } } sleep(common_config('queue', 'spawndelay')); } else { $this->log(LOG_INFO, "Thread {$i} pid {$pid} exited with status {$exitCode}; closing out thread."); } } } $this->log(LOG_INFO, "All child processes complete."); return true; }
} if (!mysqli_query($plink, sprintf($parent_sql, 'continue'))) { printf("[017] Parent cannot inform child to continue.\n", mysqli_errno($plink), mysqli_error($plink)); } break; } } mysqli_free_result($pres); } usleep(100); } while (time() - $start < 5 && $num_rows < 3); mysqli_close($plink); $wait_id = pcntl_waitpid($pid, $status); if (pcntl_wifexited($status) && 0 != ($tmp = pcntl_wexitstatus($status))) { printf("Exit code: %s\n", pcntl_wifexited($status) ? pcntl_wexitstatus($status) : 'n/a'); printf("Signal: %s\n", pcntl_wifsignaled($status) ? pcntl_wtermsig($status) : 'n/a'); printf("Stopped: %d\n", pcntl_wifstopped($status) ? pcntl_wstopsig($status) : 'n/a'); } break; } mysqli_free_result($res); mysqli_close($link); if (!($link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket))) { printf("[018] Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n", $host, $user, $db, $port, $socket); } if (!($res = mysqli_query($link, "SELECT sender, msg FROM messages ORDER BY msg_id ASC"))) { printf("[019] [%d] %s\n", mysqli_errno($link), mysqli_error($link)); } while ($row = mysqli_fetch_assoc($res)) { printf("%10s %s\n", $row['sender'], substr($row['msg'], 0, 5)); }
/** * @throws ScribuntoException */ protected function handleIOError() { $this->checkValid(); proc_terminate($this->proc); $wstatus = proc_close($this->proc); $signaled = pcntl_wifsignaled($wstatus); $termsig = pcntl_wtermsig($wstatus); $exitcode = pcntl_wexitstatus($wstatus); $this->proc = false; if ($signaled) { if (defined('SIGXCPU') && $termsig == SIGXCPU) { $this->exitError = $this->engine->newException('scribunto-common-timeout'); } else { $this->exitError = $this->engine->newException('scribunto-luastandalone-signal', array('args' => array($termsig))); } } else { $this->exitError = $this->engine->newException('scribunto-luastandalone-exited', array('args' => array($exitcode))); } throw $this->exitError; }
/** * Execute a command. * * @param $command Command to be run. * @param $args Arguments of the command to be run. * @param $env Environment variables to be set before running the command. * @param $input Data to be sent (piped) to the process. Default to null (no data will be sent to the process). * If a string, it will be sent to the process as text. If it is a file or filename, data will be read from the file and * sent to the process through the pipe. * @param $output Output data. Default to null (no output data will be saved). If it is a filename, * output data will be written to the file. * @param $timeout Seconds before timing out and aborting execution. * * @returns Zero if ok, anything else on error. */ function execute($command, $args = NULL, $env = NULL, $input = NULL, $output = NULL, $timeout = ExecutableRunner::DEFAULT_TIMEOUT) { if ($input != NULL) { $pipe_filename = tempnam(sys_get_temp_dir(), 'boca-'); posix_mkfifo($pipe_filename, 0600); } $pid = pcntl_fork(); if ($pid == 0) { // Child // Redirects stdin to pipe (the client will read data from pipe while the parent will write to it) fclose(STDIN); if ($input != NULL) { $STDIN = fopen($pipe_filename, 'r'); } // Redirects stdout to file if ($output != NULL) { if (is_resource($output) || is_string($output)) { fclose(STDOUT); fclose(STDERR); if (!is_resource($output)) { $output_file = fopen($output, 'w'); } else { $output_file = $output; } $STDOUT = $output_file; $STDERR = $output_file; } else { // fwrite($output, ); } } // Setup timeout mechanism pcntl_signal(SIGALRM, function ($signal) { fflush(STDOUT); fclose(STDOUT); fflush(STDERR); fclose(STDERR); posix_kill(posix_getpid(), SIGTERM); }); pcntl_alarm($timeout); // Configure environment $env = array(); if (!$this->resetEnv) { foreach ($_ENV as $key => $value) { $env[$key] = $value; } } foreach ($this->env as $key => $value) { $env[$key] = $value; } // Run command if ($args == NULL) { pcntl_exec($command); } else { if ($env == NULL) { // TODO: check what PHP does when pcntl_exec has $args or $env as NULL pcntl_exec($command, $args); } else { pcntl_exec($command, $args, $env); } } } else { // Parent if ($input != NULL) { $pipe = fopen($pipe_filename, 'w'); if (is_resource($input) || is_file($input)) { if (is_file($input)) { $input_file = fopen($input, 'r'); } else { $input_file = $input; } $input_data = fread($input_file, filesize($input)); fclose($input_file); } else { $input_data = $input; } fwrite($pipe, $input_data); fclose($pipe); } } if ($input != NULL) { unlink($pipe_filename); } pcntl_waitpid($pid, $status); if (pcntl_wifexited($status)) { return pcntl_wexitstatus($status); } if (pcntl_wifsignaled($status) || pcntl_wifstopped($status)) { return -1; } }
/** * W IF Signaled * * @param $status The status parameter is the status parameter supplied to a successful call to pcntl_waitpid(). * * @return bool Returns TRUE if the child process exited because of a signal which was not caught, FALSE otherwise. */ public function wIdSignaled($status) { return pcntl_wifsignaled($status); }
public function isSignalExit() : bool { return pcntl_wifsignaled($this->getStatus()); }
public function isSignaled() { return null !== $this->status && pcntl_wifsignaled($this->status); }
/** * Checks whether the status code represents a termination due to a signal. * * @return bool */ public function isSignaled() { return pcntl_wifsignaled($this->status); }
/** * Execute a command. * * @param $command Command to be run. * @param $args Arguments of the command to be run. * @param $env Environment variables to be set before running the command. * @param $input Data to be sent (piped) to the process. Default to null (no data will be sent to the process). * If a string, it will be sent to the process as text. If it is a file or filename, data will be read from the file and * sent to the process through the pipe. * @param $output Output data. Default to null (no output data will be saved). If it is a filename, * output data will be written to the file. * @param $timeout Seconds before timing out and aborting execution. * * @returns Zero if ok, anything else on error. */ function execute($command, $args = NULL, $env = NULL, $input = NULL, $output = NULL, $timeout = Runner::DEFAULT_TIMEOUT) { if ($input != NULL) { $pipe_filename = '/tmp/inpipe'; posix_mkfifo($pipe_filename, 0600); } $pid = pcntl_fork(); if ($pid == 0) { // Child // Redirects stdin to pipe (the client will read data from pipe while the parent will write to it) fclose(STDIN); if ($input != NULL) { $STDIN = fopen($pipe_filename, 'r'); } // Redirects stdout to file if ($output != NULL) { if (is_resource($output) || is_string($output)) { fclose(STDOUT); fclose(STDERR); if (!is_resource($output)) { $output_file = fopen($output, 'w'); } else { $output_file = $output; } $STDOUT = $output_file; $STDERR = $output_file; } else { // fwrite($output, ); } } pcntl_signal(SIGALRM, function ($signal) { fflush(STDOUT); fclose(STDOUT); fflush(STDERR); fclose(STDERR); posix_kill(posix_getpid(), SIGTERM); }); pcntl_alarm($timeout); if ($args == NULL) { pcntl_exec($command); } else { if ($env == NULL) { pcntl_exec($command, $args); } else { pcntl_exec($command, $args, $env); } } } else { // Parent if ($input != NULL) { $pipe = fopen($pipe_filename, 'w'); if (is_resource($input) || is_file($input)) { if (is_file($input)) { $input_file = fopen($input, 'r'); } else { $input_file = $input; } $input_data = fread($input_file, filesize($input)); fclose($input_file); } else { $input_data = $input; } fwrite($pipe, $input_data); fclose($pipe); } } if ($input != NULL) { unlink($pipe_filename); } pcntl_waitpid($pid, $status); if (pcntl_wifexited($status)) { return pcntl_wexitstatus($status); } if (pcntl_wifsignaled($status) || pcntl_wifstopped($status)) { return -1; } }
exit(0x80); } pcntl_waitpid(0, $status); VS(pcntl_wexitstatus($status), 0x80); $pid = pcntl_fork(); if ($pid == 0) { exit(0x12); } pcntl_waitpid(0, $status); VERIFY(pcntl_wifexited($status)); $pid = pcntl_fork(); if ($pid == 0) { exit(0x12); } pcntl_waitpid(0, $status); VERIFY(!pcntl_wifsignaled($status)); $pid = pcntl_fork(); if ($pid == 0) { exit(0x12); } pcntl_waitpid(0, $status); VERIFY(!pcntl_wifstopped($status)); $pid = pcntl_fork(); if ($pid == 0) { exit(0x12); } pcntl_waitpid(0, $status); VS(pcntl_wstopsig($status), 0x12); $pid = pcntl_fork(); if ($pid == 0) { exit(0x12);
/** * Supervise the children and react on finished processes. * * @param callback $ticker callback to call during each loop */ public function supervise($ticker = NULL, $respawnChecker = NULL) { // loop and monitor children while (!empty($this->children)) { $start = time(); // Check if a child exited foreach ($this->children as $pid => $fork) { $exited = pcntl_waitpid($pid, $status, WNOHANG); switch ($exited) { case $pid: $fork->setStop(); unset($this->children[$pid]); switch (TRUE) { // signal which was not caught case pcntl_wifsignaled($status): self::log(sprintf('Child %d, %s terminated from signal %d after running %d seconds.', $pid, $fork->description, pcntl_wtermsig($status), $fork->getDurationSeconds()), ezcLog::INFO); break; case pcntl_wifexited($status): $exitstatus = pcntl_wexitstatus($status); self::log(sprintf('Child %d, %s exited with status %d after running %d seconds.', $pid, $fork->description, $exitstatus, $fork->getDurationSeconds()), ezcLog::INFO); //@TODO make reforking configurable $respawn = $this->respawn; if ($respawn && is_callable($respawnChecker)) { $respawn = call_user_func($respawnChecker, $pid, $exitstatus); } if ($respawn) { self::log('refork ' . $fork->description, ezcLog::INFO); $this->fork($fork); } break; case pcntl_wifstopped($status): self::log(sprintf('Child %d, %s stopped from signal %d after running %d seconds.', $pid, $fork->description, pcntl_wstopsig($status), $fork->getDurationSeconds()), ezcLog::INFO); break; } break; case -1: self::log(sprintf('Got -1 when checking pid %d', $pid), ezcLog::ERROR); break; case 0: break; default: throw new Exception('pcntl_waitpid returned ' . $exited . ' for pid ' . $pid); } } // save CPU cycles // sleep( 5 ); $this->runServer(); if (is_callable($ticker)) { call_user_func($ticker); } self::log(sprintf('fork runner supervise loop took %d seconds.', time() - $start), ezcLog::DEBUG); } self::log('Leaving fork runner supervise function', ezcLog::DEBUG); }
/** * Wait for all child processes to complete */ public function wait() { // Wait for all children to return foreach ($this->child_pid_list as $child_pid) { if (pcntl_waitpid($child_pid, $status) < 0) { error_log(posix_strerror(posix_get_last_error())); } // Check to see if the child died a graceful death $status = 0; if (pcntl_wifsignaled($status)) { $return_code = pcntl_wexitstatus($status); $term_sig = pcntl_wtermsig($status); error_log("Child terminated with return code {$return_code} and signal {$term_sig}"); } } }
/** * @param $status * * @return bool */ private function watchdogProcessStatus($status) { switch (true) { case pcntl_wifexited($status): $code = pcntl_wexitstatus($status); $this->handleProcessExitStatus($this->watchdogPID, self::PROCESS_TYPE_WATCHDOG, $code); return true; case pcntl_wifsignaled($status): $sig = pcntl_wtermsig($status); if ($sig !== SIGKILL) { $this->logger->warn(sprintf("watchdog %d terminated with unhandled signal %s\n", $this->watchdogPID, pcntl_sig_name($sig))); } return true; case pcntl_wifstopped($status): $sig = pcntl_wstopsig($status); $this->logger->warn(sprintf("watchdog %d was stopped with signal %s\n", $this->watchdogPID, pcntl_sig_name($sig))); return false; default: $this->logger->error(sprintf("unexpected status for watchdog %d; exiting\n", $this->childPID)); exit(1); } }
/** * @param int $childPid */ private function handleQueuedChildSignals(int $childPid) { if (!isset($this->childSignalHandlers[$childPid], $this->childSignalQueue[$childPid])) { return; } $status = array_shift($this->childSignalQueue[$childPid]); if (pcntl_wifexited($status)) { $exitCode = pcntl_wexitstatus($status); $this->childSignalHandlers[$childPid]($exitCode); } elseif (pcntl_wifsignaled($status)) { $this->childSignalHandlers[$childPid](0); } elseif (pcntl_wifstopped($status)) { posix_kill($childPid, SIGCONT); } }
function poll_childs() { global $FORKS; global $ENGINE; for (;;) { $code = pcntl_waitpid(0, $status, WNOHANG); if ($code <= 0) { return; // do nothing } $cmd = @$FORKS[$code]; unset($FORKS[$code]); // avoid memory leaks if (pcntl_wifsignaled($status)) { $status = pcntl_wtermsig($status); check_global_answer("GLOBAL_SIGNALED {$code} {$status} ({$cmd})"); } if (pcntl_wifexited($status)) { $status = pcntl_wexitstatus($status); check_global_answer("GLOBAL_EXITED {$code} {$status} ({$cmd})"); } } }
$error = 4; $bot->log("Black forum {$thread_name}: error!"); $redis->DEL("{$black_key}:{$alliance_key}:forum:{$continent_key}:id"); } } } exit($error); }; $thread->start($thread, $bot, $c_continents, $forum_id); $bot->debug("Started " . $thread->getName() . " with PID " . $thread->getPid() . "..."); array_push($executeThread, $thread); } foreach ($executeThread as $thread) { pcntl_waitpid($thread->getPid(), $status, WUNTRACED); $bot->debug("Stopped " . $thread->getPid() . '@' . $thread->getName() . (!pcntl_wifexited($status) ? ' with' : ' without') . " errors!"); if (pcntl_wifsignaled($status)) { $bot->log($thread->getPid() . '@' . $thread->getName() . " stopped with state #" . pcntl_wexitstatus($status) . " errors!"); } } $bot->log("Fork: closing, all childs done!"); unset($executeThread); $redis->reInstance(); } else { $bot->log("Black error: no forum '" . BOT_BLACK_FORUM . "'"); $redis->DEL("{$black_key}:{$alliance_key}:forum:id"); } }, 'black'); // callbacks $bot->add_privmsg_hook("RebaseBlackForum", "LouBot_rebase_black_forum", true, '', function ($bot, $data) { global $redis; if (!$redis->status()) {
protected function onSIGCHLD($pid, $status) { if ($pid <= 0) { $this->logger->warn(sprintf('process PID is negative (pid: %s)', $pid)); return; } $this->logger->info(sprintf("Childs: (%s)", join(", ", array_keys($this->childs)))); if (key_exists($pid, $this->childs)) { unset($this->childs[$pid]); $this->logger->debug(sprintf("Child termination options. " . "wifexited: %d, wifsignaled: %d, wifstopped: %d, stopForking: %d", pcntl_wifexited($status), pcntl_wifsignaled($status), pcntl_wifstopped($status), $this->stopForking)); // In case of unnormal exit fork new child process // 1. status=65280 when child process died with fatal error (set by PHP) // 2. exit=9 when child was terminated by parent or by unhandled exception (set by ProcessPool) // 3. stopeed by signal $crashed = pcntl_wifexited($status) && $status == 65280 || pcntl_wifexited($status) && pcntl_wexitstatus($status) == self::$termExitCode || pcntl_wifsignaled($status); if ($crashed) { $this->logger->info(sprintf("Child PID: %s crashed (status: %s, wifexited: %s, wifsignaled: %s)", $pid, $status, pcntl_wifexited($status), pcntl_wifsignaled($status))); } else { $this->logger->info(sprintf("Child PID: %s seems normally terminated (status: %s, wifexited: %s, wifsignaled: %s)", $pid, $status, pcntl_wifexited($status), pcntl_wifsignaled($status))); } $numTasks = $this->workQueue ? $this->workQueue->capacity() : 0; if ($numTasks) { $this->logger->info(sprintf("Work queue still has %d unhandled tasks", $numTasks)); } if ($crashed || $numTasks) { try { if (!$this->stopForking) { $this->logger->info("Forking new worker process"); $this->forkChild(false); } else { $this->logger->info("Flag stopForking=1 prevents new process forking"); } } catch (Scalr_System_Ipc_Exception $e) { $this->logger->error(sprintf("Cannot fork child. Caught: <%s> %s", get_class($e), $e->getMessage())); } } } else { $this->logger->info(sprintf("Child PID: %s is unknown. Known childs: (%s)", $pid, join(",", array_keys($this->childs)))); } }
/** * PHP_Fork::wait() * Wait for the current thread to exit. * * * <code> * ... * $executeThread1->wait(); * ... * </code> * * After the relative process exited he disappears automaticly from the processes list. * * @return boolean true if the process is succesfully exited, false otherwise. */ public function wait() { $success = false; if ($this->_pid > 0 && $this->_running) { try { pcntl_waitpid($this->_pid, $temp = 0, WNOHANG); if (!($success = pcntl_wifexited($temp))) { if (pcntl_wifstopped($temp)) { throw new PHP_ForkException("Signal: " . pcntl_wstopsig($temp) . " caused this child to stop.", pcntl_wstopsig($temp)); } else { if (pcntl_wifsignaled($temp)) { throw new PHP_ForkException("Signal: " . pcntl_wtermsig($temp) . " caused this child to exit with return code: " . pcntl_wexitstatus($temp), pcntl_wexitstatus($temp)); } } } $this->_cleanThreadContext(); } catch (Exception $e) { throw new PHP_ForkException($e->getMessage(), $e->getCode()); } } return $success; }
/** * Checks whether the status code represents a termination due to a signal * * @param int $status * * @return bool */ public function wifsignaled(int $status) : bool { return pcntl_wifsignaled($status); }
/** * * @return boolean */ public function isSignaled() { return \pcntl_wifsignaled($this->_code); }
/** * update the process status * * @param bool $block */ protected function updateStatus($block = false) { if ($this->running !== true) { return; } if ($block) { $res = pcntl_waitpid($this->pid, $status); } else { $res = pcntl_waitpid($this->pid, $status, WNOHANG | WUNTRACED); } if ($res === -1) { throw new \RuntimeException('pcntl_waitpid failed. the process maybe available'); } elseif ($res === 0) { $this->running = true; } else { if (pcntl_wifsignaled($status)) { $this->term_signal = pcntl_wtermsig($status); } if (pcntl_wifstopped($status)) { $this->stop_signal = pcntl_wstopsig($status); } if (pcntl_wifexited($status)) { $this->errno = pcntl_wexitstatus($status); $this->errmsg = pcntl_strerror($this->errno); } else { $this->errno = pcntl_get_last_error(); $this->errmsg = pcntl_strerror($this->errno); } if (pcntl_wifsignaled($status)) { $this->if_signal = true; } else { $this->if_signal = false; } $this->running = false; } }
/** * update the process status * @param bool $block */ public function updateStatus($block = false) { if (empty($this->pid)) { $message = "the process pid is null, so maybe the process is not started"; throw new \RuntimeException($message); } if ($this->running === false) { return; } if ($block) { $res = pcntl_waitpid($this->pid, $status); } else { $res = pcntl_waitpid($this->pid, $status, WNOHANG | WUNTRACED); } if ($res === -1) { $message = "pcntl_waitpid failed. the process maybe available"; throw new \RuntimeException($message); } elseif ($res === 0) { $this->running = true; } else { if (pcntl_wifsignaled($status)) { $this->term_signal = pcntl_wtermsig($status); } if (pcntl_wifstopped($status)) { $this->stop_signal = pcntl_wstopsig($status); } if (pcntl_wifexited($status)) { $this->exit_code = pcntl_wexitstatus($status); } else { $this->errno = pcntl_get_last_error(); $this->errmsg = pcntl_strerror($this->errno); } $this->running = false; } }
<?php $pid = pcntl_fork(); if ($pid == 1) { die("failed"); } else { if ($pid) { $status = 0; pcntl_wait($status, WUNTRACED); var_dump(pcntl_wifexited($status)); posix_kill($pid, SIGCONT); pcntl_wait($status); var_dump(pcntl_wifsignaled($status)); var_dump(pcntl_wifstopped($status)); var_dump(pcntl_wexitstatus($status)); var_dump(pcntl_wait($status, WNOHANG | WUNTRACED)); var_dump(pcntl_wait()); var_dump(pcntl_waitpid()); var_dump(pcntl_wifexited()); var_dump(pcntl_wifstopped()); var_dump(pcntl_wifsignaled()); var_dump(pcntl_wexitstatus()); var_dump(pcntl_wtermsig()); var_dump(pcntl_wstopsig()); } else { posix_kill(posix_getpid(), SIGSTOP); exit(42); } }
/** * Wait for all child processes to complete */ public function wait() { // Read all the streams from child processes into an array. $content = $this->readResultsFromChildren(); // Wait for all children to return foreach ($this->child_pid_list as $child_pid) { if (pcntl_waitpid($child_pid, $status) < 0) { error_log(posix_strerror(posix_get_last_error())); } // Check to see if the child died a graceful death $status = 0; if (pcntl_wifsignaled($status)) { $return_code = pcntl_wexitstatus($status); $term_sig = pcntl_wtermsig($status); error_log("Child terminated with return code {$return_code} and signal {$term_sig}"); } } return $content; }