[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 */
|
/** @var ProcessPipes */
|
||||||
private $processPipes;
|
private $processPipes;
|
||||||
|
|
||||||
|
private $latestSignal;
|
||||||
|
|
||||||
private static $sigchild;
|
private static $sigchild;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -321,7 +323,7 @@ class Process
|
|||||||
usleep(1000);
|
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']));
|
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');
|
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 {
|
do {
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
} while ($this->isRunning() && microtime(true) < $timeoutMicro);
|
} while ($this->isRunning() && microtime(true) < $timeoutMicro);
|
||||||
@ -1158,6 +1161,7 @@ class Process
|
|||||||
$this->stdout = null;
|
$this->stdout = null;
|
||||||
$this->stderr = null;
|
$this->stderr = null;
|
||||||
$this->process = null;
|
$this->process = null;
|
||||||
|
$this->latestSignal = null;
|
||||||
$this->status = self::STATUS_READY;
|
$this->status = self::STATUS_READY;
|
||||||
$this->incrementalOutputOffset = 0;
|
$this->incrementalOutputOffset = 0;
|
||||||
$this->incrementalErrorOutputOffset = 0;
|
$this->incrementalErrorOutputOffset = 0;
|
||||||
@ -1201,6 +1205,8 @@ class Process
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->latestSignal = $signal;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,6 +147,50 @@ class SimpleProcessTest extends AbstractProcessTest
|
|||||||
parent::testSignalWithWrongNonIntSignal();
|
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}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
Reference in New Issue
Block a user