Skip to content

Commit da3412f

Browse files
committed
Don't run loop automatically when explicitly calling stop()
1 parent cd198af commit da3412f

File tree

5 files changed

+72
-3
lines changed

5 files changed

+72
-3
lines changed

README.md

+19
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,25 @@ explicit [`run()`](#run) calls. For BC reasons, the explicit [`run()`](#run)
223223
method is still valid and may still be useful in some applications, especially
224224
for a transition period towards the more concise style.
225225

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

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

src/Loop.php

+8-3
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

+18
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

+11
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

+16
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)