attach($process); return static::terminateProcesses($processes, $loop, $timeout); } /** * @param ProcessList $processes * @param LoopInterface $loop * @param int $timeout * @return \React\Promise\ExtendedPromiseInterface */ public static function terminateProcesses(ProcessList $processes, LoopInterface $loop, $timeout = 5) { if ($processes->count() === 0) { return resolve(); } $deferred = new Deferred(); $killTimer = $loop->addTimer($timeout, function () use ($deferred, $processes, $loop) { /** @var ChildProcess $process */ foreach ($processes as $process) { $pid = $process->getPid(); // Logger::error("Process $pid is still running, sending SIGKILL"); $process->terminate(SIGKILL); } // Let's add a bit of a delay after KILLing $loop->addTimer(0.1, function () use ($deferred) { $deferred->resolve(); }); }); $timer = $loop->addPeriodicTimer($timeout / 20, function () use ( $deferred, $processes, $loop, &$timer, $killTimer ) { $stopped = []; /** @var ChildProcess $process */ foreach ($processes as $process) { if (! $process->isRunning()) { $stopped[] = $process; } } foreach ($stopped as $process) { $processes->detach($process); } if ($processes->count() === 0) { $loop->cancelTimer($timer); $loop->cancelTimer($killTimer); $deferred->resolve(); } }); /** @var ChildProcess $process */ foreach ($processes as $process) { $process->terminate(SIGTERM); } return $deferred->promise(); } }