From f72aca3cefa317fd5e1de04284d9a3066dd75dca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Fri, 12 Jan 2018 16:57:57 +0100 Subject: [PATCH 1/2] Detect exit immediately if last process pipe is closed --- src/Process.php | 10 +++++++++- tests/AbstractProcessTest.php | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/Process.php b/src/Process.php index c4494d0..4c48948 100644 --- a/src/Process.php +++ b/src/Process.php @@ -115,10 +115,18 @@ public function start(LoopInterface $loop, $interval = 0.1) return; } + // process already closed => report immediately + if (!$that->isRunning()) { + $that->close(); + $that->emit('exit', array($that->getExitCode(), $that->getTermSignal())); + return; + } + + // close not detected immediately => check regularly $loop->addPeriodicTimer($interval, function ($timer) use ($that, $loop) { if (!$that->isRunning()) { - $that->close(); $loop->cancelTimer($timer); + $that->close(); $that->emit('exit', array($that->getExitCode(), $that->getTermSignal())); } }); diff --git a/tests/AbstractProcessTest.php b/tests/AbstractProcessTest.php index 51e0c04..acacb5c 100644 --- a/tests/AbstractProcessTest.php +++ b/tests/AbstractProcessTest.php @@ -243,6 +243,41 @@ public function testStartAndAllowProcessToExitSuccessfullyUsingEventLoop() $this->assertFalse($process->isTerminated()); } + public function testProcessWillExitFasterThanExitInterval() + { + $loop = $this->createLoop(); + $process = new Process('echo hi'); + $process->start($loop, 2); + + $time = microtime(true); + $loop->run(); + $time = microtime(true) - $time; + + $this->assertLessThan(0.1, $time); + } + + public function testDetectsClosingStdoutWithoutHavingToWaitForExit() + { + $cmd = 'exec ' . $this->getPhpBinary() . ' -r ' . escapeshellarg('fclose(STDOUT); sleep(1);'); + + $loop = $this->createLoop(); + $process = new Process($cmd); + $process->start($loop); + + $closed = false; + $process->stdout->on('close', function () use (&$closed) { + $closed = true; + }); + + // run loop for 0.1s only + $loop->addTimer(0.1, function () use ($loop) { + $loop->stop(); + }); + $loop->run(); + + $this->assertTrue($closed); + } + public function testStartInvalidProcess() { $cmd = tempnam(sys_get_temp_dir(), 'react'); From 3d1e5f734e7d416035cf482a7935922741a4cbd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Tue, 16 Jan 2018 19:15:24 +0100 Subject: [PATCH 2/2] Additional tests to ensure process exit is detected after pipes close --- tests/AbstractProcessTest.php | 41 +++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/tests/AbstractProcessTest.php b/tests/AbstractProcessTest.php index acacb5c..fc2b58d 100644 --- a/tests/AbstractProcessTest.php +++ b/tests/AbstractProcessTest.php @@ -278,6 +278,47 @@ public function testDetectsClosingStdoutWithoutHavingToWaitForExit() $this->assertTrue($closed); } + public function testKeepsRunningEvenWhenAllStdioPipesHaveBeenClosed() + { + $cmd = 'exec ' . $this->getPhpBinary() . ' -r ' . escapeshellarg('fclose(STDIN);fclose(STDOUT);fclose(STDERR);sleep(1);'); + + $loop = $this->createLoop(); + $process = new Process($cmd); + $process->start($loop); + + $closed = 0; + $process->stdout->on('close', function () use (&$closed) { + ++$closed; + }); + $process->stderr->on('close', function () use (&$closed) { + ++$closed; + }); + + // run loop for 0.1s only + $loop->addTimer(0.1, function () use ($loop) { + $loop->stop(); + }); + $loop->run(); + + $this->assertEquals(2, $closed); + $this->assertTrue($process->isRunning()); + } + + public function testDetectsClosingProcessEvenWhenAllStdioPipesHaveBeenClosed() + { + $cmd = 'exec ' . $this->getPhpBinary() . ' -r ' . escapeshellarg('fclose(STDIN);fclose(STDOUT);fclose(STDERR);usleep(10000);'); + + $loop = $this->createLoop(); + $process = new Process($cmd); + $process->start($loop, 0.001); + + $time = microtime(true); + $loop->run(); + $time = microtime(true) - $time; + + $this->assertLessThan(0.1, $time); + } + public function testStartInvalidProcess() { $cmd = tempnam(sys_get_temp_dir(), 'react');