mirror of
https://github.com/danog/amp.git
synced 2025-01-22 13:21:16 +01:00
Remove Driver::run() and stop()
Added Driver::isRunning(). Driver now must be started and stopped through an instance of DriverControl.
This commit is contained in:
parent
9a2ebe777a
commit
9f68bd4046
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,3 +5,4 @@ phpunit.xml
|
||||
vendor
|
||||
humbug.json
|
||||
.php_cs.cache
|
||||
.phpunit.result.cache
|
||||
|
@ -41,7 +41,7 @@
|
||||
"amphp/phpunit-util": "^1",
|
||||
"amphp/php-cs-fixer-config": "dev-master",
|
||||
"react/promise": "^2",
|
||||
"phpunit/phpunit": "^6.0.9 | ^7",
|
||||
"phpunit/phpunit": "^9",
|
||||
"psalm/phar": "^3.11@dev",
|
||||
"jetbrains/phpstorm-stubs": "^2019.3"
|
||||
},
|
||||
|
23
lib/Loop.php
23
lib/Loop.php
@ -3,6 +3,7 @@
|
||||
namespace Amp;
|
||||
|
||||
use Amp\Loop\Driver;
|
||||
use Amp\Loop\DriverControl;
|
||||
use Amp\Loop\DriverFactory;
|
||||
use Amp\Loop\InvalidWatcherError;
|
||||
use Amp\Loop\UnsupportedFeatureException;
|
||||
@ -15,11 +16,10 @@ use Amp\Loop\Watcher;
|
||||
*/
|
||||
final class Loop
|
||||
{
|
||||
/**
|
||||
* @var Driver
|
||||
*/
|
||||
private static Driver $driver;
|
||||
|
||||
private static DriverControl $control;
|
||||
|
||||
/**
|
||||
* Disable construction as this is a static class.
|
||||
*/
|
||||
@ -37,6 +37,10 @@ final class Loop
|
||||
*/
|
||||
public static function set(Driver $driver): void
|
||||
{
|
||||
if (isset(self::$driver) && self::$driver->isRunning()) {
|
||||
throw new \Error("Can't swap the event loop while it is running");
|
||||
}
|
||||
|
||||
try {
|
||||
self::$driver = new class extends Driver {
|
||||
protected function activate(array $watchers): void
|
||||
@ -63,6 +67,7 @@ final class Loop
|
||||
\gc_collect_cycles();
|
||||
} finally {
|
||||
self::$driver = $driver;
|
||||
self::$control = $driver->createControl();
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,7 +88,7 @@ final class Loop
|
||||
self::$driver->defer($callback);
|
||||
}
|
||||
|
||||
self::$driver->run();
|
||||
self::$control->run();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -96,7 +101,15 @@ final class Loop
|
||||
*/
|
||||
public static function stop(): void
|
||||
{
|
||||
self::$driver->stop();
|
||||
self::$control->stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool True if the event loop is running, false if it is stopped.
|
||||
*/
|
||||
public static function isRunning(): bool
|
||||
{
|
||||
return self::$driver->isRunning();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -39,61 +39,31 @@ abstract class Driver
|
||||
/** @var callable(\Throwable):void|null */
|
||||
private $errorHandler;
|
||||
|
||||
private bool $running = false;
|
||||
|
||||
/** @var mixed[] */
|
||||
private array $registry = [];
|
||||
|
||||
/**
|
||||
* Run the event loop.
|
||||
*
|
||||
* One iteration of the loop is called one "tick". A tick covers the following steps:
|
||||
*
|
||||
* 1. Activate watchers created / enabled in the last tick / before `run()`.
|
||||
* 2. Execute all enabled defer watchers.
|
||||
* 3. Execute all due timer, pending signal and actionable stream callbacks, each only once per tick.
|
||||
*
|
||||
* The loop MUST continue to run until it is either stopped explicitly, no referenced watchers exist anymore, or an
|
||||
* exception is thrown that cannot be handled. Exceptions that cannot be handled are exceptions thrown from an
|
||||
* error handler or exceptions that would be passed to an error handler but none exists to handle them.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$this->running = true;
|
||||
|
||||
try {
|
||||
while ($this->running) {
|
||||
if ($this->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
$this->tick();
|
||||
}
|
||||
} finally {
|
||||
$this->stop();
|
||||
}
|
||||
}
|
||||
private int $runCount = 0;
|
||||
|
||||
/**
|
||||
* Create a control that can be used to start and stop a specific iteration of the driver loop.
|
||||
*
|
||||
* This method is intended for {@see \Amp\Promise\wait()} only and NOT exposed as method in {@see \Amp\Loop}.
|
||||
*
|
||||
* @return DriverControl
|
||||
*
|
||||
* @see Driver::run()
|
||||
*/
|
||||
public function createControl(): DriverControl
|
||||
{
|
||||
return new class(function () use (&$running): void {
|
||||
$this->runCount++;
|
||||
$running = true;
|
||||
while ($running) {
|
||||
if ($this->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
while ($running) {
|
||||
if ($this->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->tick();
|
||||
$this->tick();
|
||||
}
|
||||
} finally {
|
||||
$this->runCount--;
|
||||
}
|
||||
}, static function () use (&$running): void {
|
||||
$running = false;
|
||||
@ -120,16 +90,11 @@ abstract class Driver
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the event loop.
|
||||
*
|
||||
* When an event loop is stopped, it continues with its current tick and exits the loop afterwards. Multiple calls
|
||||
* to stop MUST be ignored and MUST NOT raise an exception.
|
||||
*
|
||||
* @return void
|
||||
* @return bool True if the event loop is running, false if it is stopped.
|
||||
*/
|
||||
public function stop(): void
|
||||
public function isRunning(): bool
|
||||
{
|
||||
$this->running = false;
|
||||
return $this->runCount > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -605,7 +570,6 @@ abstract class Driver
|
||||
* "on_writable" => ["enabled" => int, "disabled" => int],
|
||||
* "on_signal" => ["enabled" => int, "disabled" => int],
|
||||
* "enabled_watchers" => ["referenced" => int, "unreferenced" => int],
|
||||
* "running" => bool
|
||||
* ];
|
||||
*
|
||||
* Implementations MAY optionally add more information in the array but at minimum the above `key => value` format
|
||||
@ -673,7 +637,6 @@ abstract class Driver
|
||||
"on_readable" => $onReadable,
|
||||
"on_writable" => $onWritable,
|
||||
"on_signal" => $onSignal,
|
||||
"running" => (bool) $this->running,
|
||||
];
|
||||
}
|
||||
|
||||
@ -780,6 +743,6 @@ abstract class Driver
|
||||
}
|
||||
|
||||
/** @psalm-suppress RedundantCondition */
|
||||
$this->dispatch(empty($this->nextTickQueue) && empty($this->enableQueue) && $this->running && !$this->isEmpty());
|
||||
$this->dispatch(empty($this->nextTickQueue) && empty($this->enableQueue) && !$this->isEmpty());
|
||||
}
|
||||
}
|
||||
|
@ -5,14 +5,27 @@ namespace Amp\Loop;
|
||||
interface DriverControl extends \FiberScheduler
|
||||
{
|
||||
/**
|
||||
* Run the driver event loop.
|
||||
* Run the event loop.
|
||||
*
|
||||
* One iteration of the loop is called one "tick". A tick covers the following steps:
|
||||
*
|
||||
* 1. Activate watchers created / enabled in the last tick / before `run()`.
|
||||
* 2. Execute all enabled defer watchers.
|
||||
* 3. Execute all due timer, pending signal and actionable stream callbacks, each only once per tick.
|
||||
*
|
||||
* The loop MUST continue to run until it is either stopped explicitly, no referenced watchers exist anymore, or an
|
||||
* exception is thrown that cannot be handled. Exceptions that cannot be handled are exceptions thrown from an
|
||||
* error handler or exceptions that would be passed to an error handler but none exists to handle them.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run(): void;
|
||||
|
||||
/**
|
||||
* Stop the driver event loop.
|
||||
* Stop the event loop.
|
||||
*
|
||||
* When an event loop is stopped, it continues with its current tick and exits the loop afterwards. Multiple calls
|
||||
* to stop MUST be ignored and MUST NOT raise an exception.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
@ -22,6 +22,13 @@ namespace Amp
|
||||
*/
|
||||
function await(Promise|ReactPromise|array $promise): mixed
|
||||
{
|
||||
static $loop, $control;
|
||||
|
||||
if ($loop !== Loop::get()) {
|
||||
$loop = Loop::get();
|
||||
$control = $loop->createControl();
|
||||
}
|
||||
|
||||
if (!$promise instanceof Promise) {
|
||||
if ($promise instanceof ReactPromise) {
|
||||
$promise = Promise\adapt($promise);
|
||||
@ -30,7 +37,7 @@ namespace Amp
|
||||
}
|
||||
}
|
||||
|
||||
return \Fiber::await($promise, Loop::get());
|
||||
return \Fiber::await($promise, $control);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -8,16 +8,15 @@ use PHPUnit\Framework\TestCase;
|
||||
|
||||
class DriverStateTest extends TestCase
|
||||
{
|
||||
/** @var Driver */
|
||||
private $loop;
|
||||
private Driver $loop;
|
||||
|
||||
protected function setUp()
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->loop = $this->getMockForAbstractClass(Driver::class);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function defaultsToNull()
|
||||
public function defaultsToNull(): void
|
||||
{
|
||||
$this->assertNull($this->loop->getState("foobar"));
|
||||
}
|
||||
@ -26,7 +25,7 @@ class DriverStateTest extends TestCase
|
||||
* @test
|
||||
* @dataProvider provideValues
|
||||
*/
|
||||
public function getsPreviouslySetValue($value)
|
||||
public function getsPreviouslySetValue($value): void
|
||||
{
|
||||
$this->loop->setState("foobar", $value);
|
||||
$this->assertSame($value, $this->loop->getState("foobar"));
|
||||
@ -36,13 +35,13 @@ class DriverStateTest extends TestCase
|
||||
* @test
|
||||
* @dataProvider provideValues
|
||||
*/
|
||||
public function getsPreviouslySetValueViaAccessor($value)
|
||||
public function getsPreviouslySetValueViaAccessor($value): void
|
||||
{
|
||||
Loop::setState("foobar", $value);
|
||||
$this->assertSame($value, Loop::getState("foobar"));
|
||||
}
|
||||
|
||||
public function provideValues()
|
||||
public function provideValues(): array
|
||||
{
|
||||
return [
|
||||
["string"],
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -9,7 +9,7 @@ class TracingDriverTest extends DriverTest
|
||||
{
|
||||
public function getFactory(): callable
|
||||
{
|
||||
return static function () {
|
||||
return static function (): TracingDriver {
|
||||
return new TracingDriver(new NativeDriver);
|
||||
};
|
||||
}
|
||||
@ -18,7 +18,7 @@ class TracingDriverTest extends DriverTest
|
||||
* @dataProvider provideRegistrationArgs
|
||||
* @group memoryleak
|
||||
*/
|
||||
public function testNoMemoryLeak($type, $args)
|
||||
public function testNoMemoryLeak(string $type, array $args): void
|
||||
{
|
||||
// Skip, because the driver intentionally leaks
|
||||
$this->assertTrue(true);
|
||||
|
Loading…
x
Reference in New Issue
Block a user