Skip to content

Commit d2b7244

Browse files
authored
Merge pull request #58 from clue-labs/fast-exit
Detect exit immediately if last process pipe is closed
2 parents 56c7db4 + 3d1e5f7 commit d2b7244

2 files changed

Lines changed: 85 additions & 1 deletion

File tree

src/Process.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,18 @@ public function start(LoopInterface $loop, $interval = 0.1)
115115
return;
116116
}
117117

118+
// process already closed => report immediately
119+
if (!$that->isRunning()) {
120+
$that->close();
121+
$that->emit('exit', array($that->getExitCode(), $that->getTermSignal()));
122+
return;
123+
}
124+
125+
// close not detected immediately => check regularly
118126
$loop->addPeriodicTimer($interval, function ($timer) use ($that, $loop) {
119127
if (!$that->isRunning()) {
120-
$that->close();
121128
$loop->cancelTimer($timer);
129+
$that->close();
122130
$that->emit('exit', array($that->getExitCode(), $that->getTermSignal()));
123131
}
124132
});

tests/AbstractProcessTest.php

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,82 @@ public function testStartAndAllowProcessToExitSuccessfullyUsingEventLoop()
243243
$this->assertFalse($process->isTerminated());
244244
}
245245

246+
public function testProcessWillExitFasterThanExitInterval()
247+
{
248+
$loop = $this->createLoop();
249+
$process = new Process('echo hi');
250+
$process->start($loop, 2);
251+
252+
$time = microtime(true);
253+
$loop->run();
254+
$time = microtime(true) - $time;
255+
256+
$this->assertLessThan(0.1, $time);
257+
}
258+
259+
public function testDetectsClosingStdoutWithoutHavingToWaitForExit()
260+
{
261+
$cmd = 'exec ' . $this->getPhpBinary() . ' -r ' . escapeshellarg('fclose(STDOUT); sleep(1);');
262+
263+
$loop = $this->createLoop();
264+
$process = new Process($cmd);
265+
$process->start($loop);
266+
267+
$closed = false;
268+
$process->stdout->on('close', function () use (&$closed) {
269+
$closed = true;
270+
});
271+
272+
// run loop for 0.1s only
273+
$loop->addTimer(0.1, function () use ($loop) {
274+
$loop->stop();
275+
});
276+
$loop->run();
277+
278+
$this->assertTrue($closed);
279+
}
280+
281+
public function testKeepsRunningEvenWhenAllStdioPipesHaveBeenClosed()
282+
{
283+
$cmd = 'exec ' . $this->getPhpBinary() . ' -r ' . escapeshellarg('fclose(STDIN);fclose(STDOUT);fclose(STDERR);sleep(1);');
284+
285+
$loop = $this->createLoop();
286+
$process = new Process($cmd);
287+
$process->start($loop);
288+
289+
$closed = 0;
290+
$process->stdout->on('close', function () use (&$closed) {
291+
++$closed;
292+
});
293+
$process->stderr->on('close', function () use (&$closed) {
294+
++$closed;
295+
});
296+
297+
// run loop for 0.1s only
298+
$loop->addTimer(0.1, function () use ($loop) {
299+
$loop->stop();
300+
});
301+
$loop->run();
302+
303+
$this->assertEquals(2, $closed);
304+
$this->assertTrue($process->isRunning());
305+
}
306+
307+
public function testDetectsClosingProcessEvenWhenAllStdioPipesHaveBeenClosed()
308+
{
309+
$cmd = 'exec ' . $this->getPhpBinary() . ' -r ' . escapeshellarg('fclose(STDIN);fclose(STDOUT);fclose(STDERR);usleep(10000);');
310+
311+
$loop = $this->createLoop();
312+
$process = new Process($cmd);
313+
$process->start($loop, 0.001);
314+
315+
$time = microtime(true);
316+
$loop->run();
317+
$time = microtime(true) - $time;
318+
319+
$this->assertLessThan(0.1, $time);
320+
}
321+
246322
public function testStartInvalidProcess()
247323
{
248324
$cmd = tempnam(sys_get_temp_dir(), 'react');

0 commit comments

Comments
 (0)