[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:
parent
94ffc4fab2
commit
eb68662360
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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}
|
||||
*/
|
||||
|
Reference in New Issue
Block a user