[Process] fix signal handling in wait()

wait() throws an exception when the process was terminated by a signal.
This should not happen when the termination was requested by calling
either the stop() or the signal() method (for example, inside a callback
which is passed to wait()).
This commit is contained in:
Christian Flothmann 2014-07-22 18:04:22 +02:00
parent 94ffc4fab2
commit eb68662360
2 changed files with 52 additions and 2 deletions

View File

@ -64,6 +64,8 @@ class Process
/** @var ProcessPipes */
private $processPipes;
private $latestSignal;
private static $sigchild;
/**
@ -321,7 +323,7 @@ class Process
usleep(1000);
}
if ($this->processInformation['signaled']) {
if ($this->processInformation['signaled'] && $this->processInformation['termsig'] !== $this->latestSignal) {
throw new RuntimeException(sprintf('The process has been signaled with signal "%s".', $this->processInformation['termsig']));
}
@ -661,7 +663,8 @@ class Process
throw new RuntimeException('Unable to kill the process');
}
}
proc_terminate($this->process);
// given `SIGTERM` may not be defined and that `proc_terminate` uses the constant value and not the constant itself, we use the same here
$this->doSignal(15, false);
do {
usleep(1000);
} while ($this->isRunning() && microtime(true) < $timeoutMicro);
@ -1158,6 +1161,7 @@ class Process
$this->stdout = null;
$this->stderr = null;
$this->process = null;
$this->latestSignal = null;
$this->status = self::STATUS_READY;
$this->incrementalOutputOffset = 0;
$this->incrementalErrorOutputOffset = 0;
@ -1201,6 +1205,8 @@ class Process
return false;
}
$this->latestSignal = $signal;
return true;
}

View File

@ -147,6 +147,50 @@ class SimpleProcessTest extends AbstractProcessTest
parent::testSignalWithWrongNonIntSignal();
}
public function testStopTerminatesProcessCleanly()
{
try {
$process = $this->getProcess('php -r "echo \'foo\'; sleep(1); echo \'bar\';"');
$process->run(function () use ($process) {
$process->stop();
});
} catch (RuntimeException $e) {
$this->fail('A call to stop() is not expected to cause wait() to throw a RuntimeException');
}
}
public function testKillSignalTerminatesProcessCleanly()
{
$this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. The process can not be signaled.');
try {
$process = $this->getProcess('php -r "echo \'foo\'; sleep(1); echo \'bar\';"');
$process->run(function () use ($process) {
if ($process->isRunning()) {
$process->signal(SIGKILL);
}
});
} catch (RuntimeException $e) {
$this->fail('A call to signal() is not expected to cause wait() to throw a RuntimeException');
}
}
public function testTermSignalTerminatesProcessCleanly()
{
$this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. The process can not be signaled.');
try {
$process = $this->getProcess('php -r "echo \'foo\'; sleep(1); echo \'bar\';"');
$process->run(function () use ($process) {
if ($process->isRunning()) {
$process->signal(SIGTERM);
}
});
} catch (RuntimeException $e) {
$this->fail('A call to signal() is not expected to cause wait() to throw a RuntimeException');
}
}
/**
* {@inheritdoc}
*/