From 75f2df3da6341526e7f6a455dc5f091e8ac1f11b Mon Sep 17 00:00:00 2001 From: Dominique Fournier Date: Fri, 22 Dec 2017 12:09:20 +0000 Subject: [PATCH] Add fork support git-svn-id: https://svn.fournier38.fr/svn/ProgSVN/trunk@4006 bf3deb0d-5f1a-0410-827f-c0cc1f45334c --- fork.php | 195 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 fork.php diff --git a/fork.php b/fork.php new file mode 100644 index 0000000..c9e6d9f --- /dev/null +++ b/fork.php @@ -0,0 +1,195 @@ +pidList); + } + + /** Return the list of the active PID + */ + public function childList () + { + return $this->pidList; + } + + /** Create a child + * If some parameters are provided, the called child method will receive them + * This function fork and return the child PID + * @param callable $callable The callback method to use in child + * @param mixed|null $params The params to provide to child method + * @return The child PID + */ + public function startChild ($callable, $params = array ()) + { + $pid = pcntl_fork (); + if ($pid === -1) + throw new \Exception ("Can't fork the child", 500); + elseif ($pid) + { + // The parent + $this->pidList[$pid] = $pid; + return $pid; + } + // Call the child method + $args = func_get_args (); + unset ($args[0]); + call_user_func_array ($callable, $args); + exit; + } + + /** Wait the end of one child + * Return the PID of the dead child + */ + public function waitEndChild () + { + while (1) + { + $stoppedPid = pcntl_wait ($status, WNOHANG); + if ($stoppedPid > 0) + { + unset ($this->pidList[$stoppedPid]); + return $stoppedPid; + } + usleep (100); + } + } + + /** Stop (SIGTERM) a specific child. + * If the $maxWait parameter is set, wait the dead of the child for $maxWait + * seconds. If $maxWait is not set, do not wait the child, only send it the + * signal + * @param integer $pid The PID of the child to stop + * @param integer|null $maxWait The maximum time to wait the child if set + * @return $pid if the child is dead correctely. Return false if the child is + * not dead in the $maxWait time + */ + public function stopChild ($pid, $maxWait = null) + { + return $this->sendSignalToChild (SIGTERM, $pid, $maxWait); + } + + /** Kill (SIGKILL) a specific child. + * If the $maxWait parameter is set, wait the dead of the child for $maxWait + * seconds. If $maxWait is not set, do not wait the child, only send it the + * signal + * @param integer $pid The PID of the child to stop + * @param integer|null $maxWait The maximum time to wait the child if set + * @return $pid if the child is dead correctely. Return false if the child is + * not dead in the $maxWait time + */ + public function killChild ($pid, $maxWait = null) + { + return $this->sendSignalToChild (SIGKILL, $pid, $maxWait); + } + + /** Send a signal to a specific child. + * If the $maxWait parameter is set, wait the dead of the child for $maxWait + * seconds. If $maxWait is not set, do not wait the child, only send it the + * signal + * @param integer $signal The signal to send to child (SIGTERM or SIGKILL) + * @param integer $pid The PID of the child to stop + * @param integer|null $maxWait The maximum time to wait the child if set + * @return $pid if the child is dead correctely. Return false if the child is + * not dead in the $maxWait time + */ + private function sendSignalToChild ($signal, $pid, $maxWait = null) + { + if (! key_exists ($pid, $this->pidList)) + throw new \Exception ("Can't stop/kill pid '$pid' : not in the fork list", + 500); + posix_kill ($pid, SIGKILL); + if ($maxWait !== null) + { + $startWait = time (); + while (time () < $startWait + $maxWait) + { + if (pcntl_waitpid ($pid, $status, WNOHANG) > 0) + { + unset ($this->pidList[$pid]); + return $pid; + } + usleep (100); + } + return false; + } + return $pid; + } + + /** Stop all the existing children. + * If the $maxWait parameter is set, wait the dead of the child for $maxWait + * seconds. If $maxWait is not set, do not wait the child, only send it the + * signal + * @param integer|null $maxWait The maximum time to wait the child if set + * @return true if all the children are dead correctely. Return false if at + * least one child isnot dead in the $maxWait time + */ + public function stopAll ($maxWait = null) + { + return $this->sendSigToAll (SIGTERM, $maxWait); + } + + /** Kill all the existing children. + * If the $maxWait parameter is set, wait the dead of the child for $maxWait + * seconds. If $maxWait is not set, do not wait the child, only send it the + * signal + * @param integer|null $maxWait The maximum time to wait the child if set + * @return true if all the children are dead correctely. Return false if at + * least one child isnot dead in the $maxWait time + */ + public function killAll ($maxWait = null) + { + return $this->sendSigToAll (SIGKILL, $maxWait); + } + + /** Send a stop or kill signal to all the existing children. + * If the $maxWait parameter is set, wait the dead of the child for $maxWait + * seconds. If $maxWait is not set, do not wait the child, only send it the + * signal + * @param integer $signal The signal to send to child (SIGTERM or SIGKILL) + * @param integer|null $maxWait The maximum time to wait the child if set + * @return true if all the children are dead correctely. Return false if at + * least one child isnot dead in the $maxWait time + */ + private function sendSigToAll ($signal, $maxWait = null) + { + foreach ($this->pidList as $pid) + { + posix_kill ($pid, $signal); + } + if ($maxWait !== null) + { + $startWait = time (); + while (time () < $startWait + $maxWait) + { + $pid = pcntl_wait ($status, WNOHANG); + if ($pid > 0) + { + unset ($this->pidList[$pid]); + if (empty ($this->pidList)) + return true; + } + usleep (100); + } + return false; + } + return true; + } +}