Skip to content

Commit 9712eea

Browse files
committed
Don't run loop automatically when explicitly calling stop()
1 parent f0853b1 commit 9712eea

File tree

5 files changed

+72
-3
lines changed

5 files changed

+72
-3
lines changed

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,25 @@ explicit [`run()`](#run) calls. For BC reasons, the explicit [`run()`](#run)
229229
method is still valid and may still be useful in some applications, especially
230230
for a transition period towards the more concise style.
231231

232+
If you don't want the `Loop` to run automatically, you can either explicitly
233+
[`run()`](#run) or [`stop()`](#stop) it. This can be useful if you're using
234+
a global exception handler like this:
235+
236+
```php
237+
use React\EventLoop\Loop;
238+
239+
Loop::addTimer(10.0, function () {
240+
echo 'Never happens';
241+
});
242+
243+
set_exception_handler(function (Throwable $e) {
244+
echo 'Error: ' . $e->getMessage() . PHP_EOL;
245+
Loop::stop();
246+
});
247+
248+
throw new RuntimeException('Demo');
249+
```
250+
232251
#### get()
233252

234253
The `get(): LoopInterface` method can be used to

src/Loop.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ final class Loop
1212
*/
1313
private static $instance;
1414

15+
/** @var bool */
16+
private static $stopped = false;
17+
1518
/**
1619
* Returns the event loop.
1720
* When no loop is set it will it will call the factory to create one.
@@ -32,22 +35,23 @@ public static function get()
3235

3336
self::$instance = $loop = Factory::create();
3437

35-
// Automatically run loop at end of program, unless already started explicitly.
38+
// Automatically run loop at end of program, unless already started or stopped explicitly.
3639
// This is tested using child processes, so coverage is actually 100%, see BinTest.
3740
// @codeCoverageIgnoreStart
3841
$hasRun = false;
3942
$loop->futureTick(function () use (&$hasRun) {
4043
$hasRun = true;
4144
});
4245

43-
register_shutdown_function(function () use ($loop, &$hasRun) {
46+
$stopped =& self::$stopped;
47+
register_shutdown_function(function () use ($loop, &$hasRun, &$stopped) {
4448
// Don't run if we're coming from a fatal error (uncaught exception).
4549
$error = error_get_last();
4650
if ((isset($error['type']) ? $error['type'] : 0) & (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR)) {
4751
return;
4852
}
4953

50-
if (!$hasRun) {
54+
if (!$hasRun && !$stopped) {
5155
$loop->run();
5256
}
5357
});
@@ -215,6 +219,7 @@ public static function run()
215219
*/
216220
public static function stop()
217221
{
222+
self::$stopped = true;
218223
self::get()->stop();
219224
}
220225
}

tests/BinTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,22 @@ public function testExecuteExampleWithUndefinedVariableShouldNotRunLoop()
5454

5555
$this->assertLessThan(1.0, $time);
5656
}
57+
58+
public function testExecuteExampleWithExplicitStopShouldNotRunLoop()
59+
{
60+
$time = microtime(true);
61+
exec(escapeshellarg(PHP_BINARY) . ' 21-stop.php 2>/dev/null');
62+
$time = microtime(true) - $time;
63+
64+
$this->assertLessThan(1.0, $time);
65+
}
66+
67+
public function testExecuteExampleWithExplicitStopInExceptionHandlerShouldNotRunLoop()
68+
{
69+
$time = microtime(true);
70+
exec(escapeshellarg(PHP_BINARY) . ' 22-uncaught-stop.php 2>/dev/null');
71+
$time = microtime(true) - $time;
72+
73+
$this->assertLessThan(1.0, $time);
74+
}
5775
}

tests/bin/21-stop.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
use React\EventLoop\Loop;
4+
5+
require __DIR__ . '/../../vendor/autoload.php';
6+
7+
Loop::addTimer(10.0, function () {
8+
echo 'never';
9+
});
10+
11+
Loop::stop();

tests/bin/22-stop-uncaught.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
use React\EventLoop\Loop;
4+
5+
require __DIR__ . '/../../vendor/autoload.php';
6+
7+
Loop::addTimer(10.0, function () {
8+
echo 'never';
9+
});
10+
11+
set_exception_handler(function (Exception $e) {
12+
echo 'Uncaught error occured' . PHP_EOL;
13+
Loop::stop();
14+
});
15+
16+
throw new RuntimeException();

0 commit comments

Comments
 (0)