git-svn-id: https://svn.fournier38.fr/svn/ProgSVN/trunk@5977 bf3deb0d-5f1a-0410-827f-c0cc1f45334c
258 lines
7.8 KiB
PHP
258 lines
7.8 KiB
PHP
<?php
|
|
/** DomFramework
|
|
* @package domframework
|
|
* @author Dominique Fournier <dominique@fournier38.fr>
|
|
*/
|
|
|
|
/** Manage the fork of children in Posix mode
|
|
*/
|
|
class fork
|
|
{
|
|
/** The PID list of childs
|
|
*/
|
|
private $pidList = array ();
|
|
|
|
/** The constructor check if the posix functions exists
|
|
*/
|
|
public function __construct ()
|
|
{
|
|
if (! function_exists ("pcntl_fork"))
|
|
throw new \Exception ("Can't fork as PHP doesn't have the pcntl_fork",
|
|
500);
|
|
declare (ticks=1);
|
|
@pcntl_async_signals (true);
|
|
}
|
|
|
|
/** Return the number of active PID
|
|
*/
|
|
public function childCount ()
|
|
{
|
|
return count ($this->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;
|
|
}
|
|
|
|
/** Create a detached child. The terminal is closed. All the displayed
|
|
* messages from the child are silently dropped.
|
|
* If some parameters are provided, the called child method will receive them
|
|
* This function fork and return the child PID
|
|
* @param string $name The name displayed in the processus list
|
|
* @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 startDetachedChild ($name, $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
|
|
$sid = posix_setsid();
|
|
|
|
// Will catch all the text messages from the application to not crash if
|
|
// there is an "echo"
|
|
ob_start ();
|
|
|
|
// Close the file handlers STDOUT/STDIN
|
|
fclose (STDIN);
|
|
fclose (STDOUT);
|
|
fclose (STDERR);
|
|
if (function_exists ("cli_set_process_title"))
|
|
cli_set_process_title ($name);
|
|
|
|
$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);
|
|
}
|
|
}
|
|
|
|
/** Clean the childs which are finished. Do not block the process
|
|
*/
|
|
public function cleanEndChild ()
|
|
{
|
|
$stoppedPid = pcntl_wait ($status, WNOHANG);
|
|
while ($stoppedPid > 0)
|
|
{
|
|
unset ($this->pidList[$stoppedPid]);
|
|
$stoppedPid = pcntl_wait ($status, WNOHANG);
|
|
}
|
|
}
|
|
|
|
/** 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)
|
|
{
|
|
if (! key_exists ($pid, $this->pidList))
|
|
throw new \Exception ("Can't stop pid '$pid' : not in the fork list",
|
|
500);
|
|
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)
|
|
{
|
|
if (! key_exists ($pid, $this->pidList))
|
|
throw new \Exception ("Can't kill pid '$pid' : not in the fork list",
|
|
500);
|
|
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)
|
|
{
|
|
posix_kill ($pid, $signal);
|
|
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;
|
|
}
|
|
}
|