diff --git a/lib/Internal/EmitSource.php b/lib/Internal/EmitSource.php index a225201..f9f1251 100644 --- a/lib/Internal/EmitSource.php +++ b/lib/Internal/EmitSource.php @@ -136,7 +136,7 @@ final class EmitSource // No value has been emitted, suspend fiber to await next value. $this->waiting[$position] = \Fiber::this(); - return \Fiber::suspend(Loop::getDriver()); + return \Fiber::suspend(Loop::getScheduler()); } public function pipe(): Pipeline @@ -302,7 +302,7 @@ final class EmitSource if ($pair === null) { $this->yielding[$position] = \Fiber::this(); - return \Fiber::suspend(Loop::getDriver()); + return \Fiber::suspend(Loop::getScheduler()); } [$exception, $value] = $pair; diff --git a/lib/Loop.php b/lib/Loop.php index 049a7ec..c426dd7 100644 --- a/lib/Loop.php +++ b/lib/Loop.php @@ -388,6 +388,16 @@ final class Loop { return self::$driver; } + + /** + * Retrieve the active {@see FiberScheduler} instance associated with the event loop driver. + * + * @return \FiberScheduler + */ + public static function getScheduler(): \FiberScheduler + { + return self::$driver->getScheduler(); + } } // Default factory, don't move this to a file loaded by the composer "files" autoload mechanism, otherwise custom diff --git a/lib/Loop/Driver.php b/lib/Loop/Driver.php index 802de72..dbe1a0d 100644 --- a/lib/Loop/Driver.php +++ b/lib/Loop/Driver.php @@ -2,8 +2,15 @@ namespace Amp\Loop; -interface Driver extends \FiberScheduler +interface Driver { + /** + * Get the fiber scheduler associated with this driver. + * + * @return \FiberScheduler + */ + public function getScheduler(): \FiberScheduler; + /** * Run the event loop. * @@ -18,6 +25,8 @@ interface Driver extends \FiberScheduler * error handler or exceptions that would be passed to an error handler but none exists to handle them. * * @return void + * + * @throw \Error Thrown if the event loop is already running. */ public function run(): void; diff --git a/lib/Loop/DriverFoundation.php b/lib/Loop/DriverFoundation.php index f868659..ff64894 100644 --- a/lib/Loop/DriverFoundation.php +++ b/lib/Loop/DriverFoundation.php @@ -41,6 +41,22 @@ abstract class DriverFoundation implements Driver private bool $running = false; + private \FiberScheduler $scheduler; + + /** + * Get the fiber scheduler associated with this driver. + * + * @return \FiberScheduler + */ + public function getScheduler(): \FiberScheduler + { + if (!isset($this->scheduler) || $this->scheduler->isTerminated()) { + $this->scheduler = new \FiberScheduler(fn() => $this->run()); + } + + return $this->scheduler; + } + /** * Run the event loop. * @@ -55,6 +71,8 @@ abstract class DriverFoundation implements Driver * error handler or exceptions that would be passed to an error handler but none exists to handle them. * * @return void + * + * @throw \Error Thrown if the event loop is already running. */ public function run(): void { diff --git a/lib/Loop/TracingDriver.php b/lib/Loop/TracingDriver.php index d3d841f..3972a5d 100644 --- a/lib/Loop/TracingDriver.php +++ b/lib/Loop/TracingDriver.php @@ -25,6 +25,11 @@ final class TracingDriver implements Driver $this->driver = $driver; } + public function getScheduler(): \FiberScheduler + { + return $this->driver->getScheduler(); + } + public function run(): void { $this->driver->run(); diff --git a/lib/functions.php b/lib/functions.php index d031f07..eb7c7b1 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -34,7 +34,7 @@ namespace Amp } ); - return \Fiber::suspend(Loop::getDriver()); + return \Fiber::suspend(Loop::getScheduler()); } /** @@ -226,7 +226,7 @@ namespace Amp { $fiber = \Fiber::this(); Loop::delay($milliseconds, fn() => $fiber->resume()); - \Fiber::suspend(Loop::getDriver()); + \Fiber::suspend(Loop::getScheduler()); } /** @@ -255,7 +255,7 @@ namespace Amp $watchers[] = Loop::onSignal($signal, $callback); } - return \Fiber::suspend(Loop::getDriver()); + return \Fiber::suspend(Loop::getScheduler()); } /** diff --git a/stubs/Fiber.php b/stubs/Fiber.php index a4a016e..0695dd6 100644 --- a/stubs/Fiber.php +++ b/stubs/Fiber.php @@ -10,7 +10,7 @@ final class Fiber /** * Starts execution of the fiber. Returns when the fiber suspends or terminates. * - * Must be called within {@see FiberScheduler::run()}. + * Must be called within a {@see FiberScheduler}. * * @param mixed ...$args Arguments passed to fiber function. * @@ -23,7 +23,7 @@ final class Fiber * Resumes the fiber, returning the given value from {@see Fiber::suspend()}. * Returns when the fiber suspends or terminates. * - * Must be called within {@see FiberScheduler::run()}. + * Must be called within a {@see FiberScheduler}. * * @param mixed $value * @@ -36,7 +36,7 @@ final class Fiber * Throws the given exception into the fiber from {@see Fiber::suspend()}. * Returns when the fiber suspends or terminates. * - * Must be called within {@see FiberScheduler::run()}. + * Must be called within a {@see FiberScheduler}. * * @param Throwable $exception * @@ -68,19 +68,19 @@ final class Fiber /** * Returns the currently executing Fiber instance. * - * Cannot be called within {@see FiberScheduler::run()}. + * Cannot be called within {@see FiberScheduler}. * * @return self The currently executing fiber. * - * @throws FiberError Thrown if within {@see FiberScheduler::run()}. + * @throws FiberError Thrown if within {@see FiberScheduler}. */ public static function this(): self { } /** * Suspend execution of the fiber. The fiber may be resumed with {@see Fiber::resume()} or {@see Fiber::throw()} - * within the run() method of the instance of {@see FiberScheduler} given. + * within the callback used to create the {@see FiberScheduler} given. * - * Cannot be called within {@see FiberScheduler::run()}. + * Cannot be called within a {@see FiberScheduler}. * * @param FiberScheduler $scheduler * diff --git a/stubs/FiberScheduler.php b/stubs/FiberScheduler.php index 237844a..2989228 100644 --- a/stubs/FiberScheduler.php +++ b/stubs/FiberScheduler.php @@ -1,9 +1,29 @@ loop = ($this->getFactory())(); - - if (!$this->loop instanceof Driver) { - $this->fail("Factory did not return a loop Driver"); - } - \gc_collect_cycles(); }