diff --git a/README.md b/README.md index 0bd090c..bc66f8c 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,10 @@ event loop implementations. This allows libraries and components from different vendors to operate in an event driven architecture, sharing a common event loop. +## Current Status + +This project is currently on hold and to be seen as failed for now. It might be reconsidered at a later point in time. The specification in its current state has been merged into [Amp](https://github.com/amphp/amp). Interoperability between [ReactPHP](https://github.com/reactphp/event-loop) and Amp will be solved via adapters instead of a common interface. [Icicle](https://github.com/icicleio/icicle) has been deprecated and parts of it been merged into Amp libraries. + ## Why Bother? Some programming languages, such as Javascript, have an event loop that is diff --git a/src/Loop.php b/src/Loop.php index f55db4f..27ddf28 100644 --- a/src/Loop.php +++ b/src/Loop.php @@ -136,8 +136,11 @@ public static function stop() /** * Defer the execution of a callback. * - * The deferred callable MUST be executed in the next tick of the event loop and before any other type of watcher. - * Order of enabling MUST be preserved when executing the callbacks. + * The deferred callable MUST be executed before any other type of watcher in a tick. Order of enabling MUST be + * preserved when executing the callbacks. + * + * The created watcher MUST immediately be marked as enabled, but only be activated (i.e. callback can be called) + * right before the next tick. Callbacks of watchers MUST NOT be called in the tick they were enabled. * * @param callable(string $watcherId, mixed $data) $callback The callback to defer. The `$watcherId` will be * invalidated before the callback call. @@ -157,6 +160,9 @@ public static function defer(callable $callback, $data = null) * The delay is a minimum and approximate, accuracy is not guaranteed. Order of calls MUST be determined by which * timers expire first, but timers with the same expiration time MAY be executed in any order. * + * The created watcher MUST immediately be marked as enabled, but only be activated (i.e. callback can be called) + * right before the next tick. Callbacks of watchers MUST NOT be called in the tick they were enabled. + * * @param int $delay The amount of time, in milliseconds, to delay the execution for. * @param callable(string $watcherId, mixed $data) $callback The callback to delay. The `$watcherId` will be * invalidated before the callback call. @@ -177,6 +183,9 @@ public static function delay($time, callable $callback, $data = null) * determined by which timers expire first, but timers with the same expiration time MAY be executed in any order. * The first execution is scheduled after the first interval period. * + * The created watcher MUST immediately be marked as enabled, but only be activated (i.e. callback can be called) + * right before the next tick. Callbacks of watchers MUST NOT be called in the tick they were enabled. + * * @param int $interval The time interval, in milliseconds, to wait between executions. * @param callable(string $watcherId, mixed $data) $callback The callback to repeat. * @param mixed $data Arbitrary data given to the callback function as the `$data` parameter. @@ -199,6 +208,9 @@ public static function repeat($interval, callable $callback, $data = null) * * Multiple watchers on the same stream MAY be executed in any order. * + * The created watcher MUST immediately be marked as enabled, but only be activated (i.e. callback can be called) + * right before the next tick. Callbacks of watchers MUST NOT be called in the tick they were enabled. + * * @param resource $stream The stream to monitor. * @param callable(string $watcherId, resource $stream, mixed $data) $callback The callback to execute. * @param mixed $data Arbitrary data given to the callback function as the `$data` parameter. @@ -221,6 +233,9 @@ public static function onReadable($stream, callable $callback, $data = null) * * Multiple watchers on the same stream MAY be executed in any order. * + * The created watcher MUST immediately be marked as enabled, but only be activated (i.e. callback can be called) + * right before the next tick. Callbacks of watchers MUST NOT be called in the tick they were enabled. + * * @param resource $stream The stream to monitor. * @param callable(string $watcherId, resource $stream, mixed $data) $callback The callback to execute. * @param mixed $data Arbitrary data given to the callback function as the `$data` parameter. @@ -242,6 +257,9 @@ public static function onWritable($stream, callable $callback, $data = null) * * Multiple watchers on the same signal MAY be executed in any order. * + * The created watcher MUST immediately be marked as enabled, but only be activated (i.e. callback can be called) + * right before the next tick. Callbacks of watchers MUST NOT be called in the tick they were enabled. + * * @param int $signo The signal number to monitor. * @param callable(string $watcherId, int $signo, mixed $data) $callback The callback to execute. * @param mixed $data Arbitrary data given to the callback function as the $data parameter. @@ -257,11 +275,10 @@ public static function onSignal($signo, callable $callback, $data = null) } /** - * Enable a watcher. + * Enable a watcher to be active starting in the next tick. * - * Watchers (enabling or new watchers) MUST immediately be marked as enabled, but only be activated (i.e. callbacks - * can be called) right before the next tick. Callbacks of watchers MUST not be called in the tick they were - * enabled. + * Watchers MUST immediately be marked as enabled, but only be activated (i.e. callbacks can be called) right before + * the next tick. Callbacks of watchers MUST NOT be called in the tick they were enabled. * * @param string $watcherId The watcher identifier. * @@ -276,7 +293,10 @@ public static function enable($watcherId) } /** - * Disable a watcher. + * Disable a watcher immediately. + * + * A watcher MUST be disabled immediately, e.g. if a defer watcher disables a later defer watcher, the second defer + * watcher isn't executed in this tick. * * Disabling a watcher MUST NOT invalidate the watcher. Calling this function MUST NOT fail, even if passed an * invalid watcher. @@ -402,13 +422,13 @@ public static function setErrorHandler(callable $callback = null) * The returned array MUST contain the following data describing the driver's currently registered watchers: * * [ - * "defer" => ["enabled" => int, "disabled" => int], - * "delay" => ["enabled" => int, "disabled" => int], - * "repeat" => ["enabled" => int, "disabled" => int], - * "on_readable" => ["enabled" => int, "disabled" => int], - * "on_writable" => ["enabled" => int, "disabled" => int], - * "on_signal" => ["enabled" => int, "disabled" => int], - * "watchers" => ["referenced" => int, "unreferenced" => int], + * "defer" => ["enabled" => int, "disabled" => int], + * "delay" => ["enabled" => int, "disabled" => int], + * "repeat" => ["enabled" => int, "disabled" => int], + * "on_readable" => ["enabled" => int, "disabled" => int], + * "on_writable" => ["enabled" => int, "disabled" => int], + * "on_signal" => ["enabled" => int, "disabled" => int], + * "enabled_watchers" => ["referenced" => int, "unreferenced" => int], * ]; * * Implementations MAY optionally add more information in the array but at minimum the above `key => value` format diff --git a/src/Loop/Driver.php b/src/Loop/Driver.php index fda1604..669e296 100644 --- a/src/Loop/Driver.php +++ b/src/Loop/Driver.php @@ -5,7 +5,10 @@ /** * Event loop driver which implements all basic operations to allow interoperability. * - * Registered callbacks MUST NOT be called from a file with strict types enabled (`declare(strict_types=1)`). + * Watchers (enabled or new watchers) MUST immediately be marked as enabled, but only be activated (i.e. callbacks can + * be called) right before the next tick. Callbacks of watchers MUST NOT be called in the tick they were enabled. + * + * All registered callbacks MUST NOT be called from a file with strict types enabled (`declare(strict_types=1)`). */ abstract class Driver { @@ -15,7 +18,13 @@ abstract class Driver private $registry = []; /** - * Start the 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 @@ -38,8 +47,11 @@ abstract public function stop(); /** * Defer the execution of a callback. * - * The deferred callable MUST be executed in the next tick of the event loop and before any other type of watcher. - * Order of enabling MUST be preserved when executing the callbacks. + * The deferred callable MUST be executed before any other type of watcher in a tick. Order of enabling MUST be + * preserved when executing the callbacks. + * + * The created watcher MUST immediately be marked as enabled, but only be activated (i.e. callback can be called) + * right before the next tick. Callbacks of watchers MUST NOT be called in the tick they were enabled. * * @param callable(string $watcherId, mixed $data) $callback The callback to defer. The `$watcherId` will be * invalidated before the callback call. @@ -55,6 +67,9 @@ abstract public function defer(callable $callback, $data = null); * The delay is a minimum and approximate, accuracy is not guaranteed. Order of calls MUST be determined by which * timers expire first, but timers with the same expiration time MAY be executed in any order. * + * The created watcher MUST immediately be marked as enabled, but only be activated (i.e. callback can be called) + * right before the next tick. Callbacks of watchers MUST NOT be called in the tick they were enabled. + * * @param int $delay The amount of time, in milliseconds, to delay the execution for. * @param callable(string $watcherId, mixed $data) $callback The callback to delay. The `$watcherId` will be * invalidated before the callback call. @@ -71,6 +86,9 @@ abstract public function delay($delay, callable $callback, $data = null); * determined by which timers expire first, but timers with the same expiration time MAY be executed in any order. * The first execution is scheduled after the first interval period. * + * The created watcher MUST immediately be marked as enabled, but only be activated (i.e. callback can be called) + * right before the next tick. Callbacks of watchers MUST NOT be called in the tick they were enabled. + * * @param int $interval The time interval, in milliseconds, to wait between executions. * @param callable(string $watcherId, mixed $data) $callback The callback to repeat. * @param mixed $data Arbitrary data given to the callback function as the `$data` parameter. @@ -89,6 +107,9 @@ abstract public function repeat($interval, callable $callback, $data = null); * * Multiple watchers on the same stream MAY be executed in any order. * + * The created watcher MUST immediately be marked as enabled, but only be activated (i.e. callback can be called) + * right before the next tick. Callbacks of watchers MUST NOT be called in the tick they were enabled. + * * @param resource $stream The stream to monitor. * @param callable(string $watcherId, resource $stream, mixed $data) $callback The callback to execute. * @param mixed $data Arbitrary data given to the callback function as the `$data` parameter. @@ -107,6 +128,9 @@ abstract public function onReadable($stream, callable $callback, $data = null); * * Multiple watchers on the same stream MAY be executed in any order. * + * The created watcher MUST immediately be marked as enabled, but only be activated (i.e. callback can be called) + * right before the next tick. Callbacks of watchers MUST NOT be called in the tick they were enabled. + * * @param resource $stream The stream to monitor. * @param callable(string $watcherId, resource $stream, mixed $data) $callback The callback to execute. * @param mixed $data Arbitrary data given to the callback function as the `$data` parameter. @@ -124,6 +148,9 @@ abstract public function onWritable($stream, callable $callback, $data = null); * * Multiple watchers on the same signal MAY be executed in any order. * + * The created watcher MUST immediately be marked as enabled, but only be activated (i.e. callback can be called) + * right before the next tick. Callbacks of watchers MUST NOT be called in the tick they were enabled. + * * @param int $signo The signal number to monitor. * @param callable(string $watcherId, int $signo, mixed $data) $callback The callback to execute. * @param mixed $data Arbitrary data given to the callback function as the $data parameter. @@ -135,11 +162,10 @@ abstract public function onWritable($stream, callable $callback, $data = null); abstract public function onSignal($signo, callable $callback, $data = null); /** - * Enable a watcher. + * Enable a watcher to be active starting in the next tick. * - * Watchers (enabling or new watchers) MUST immediately be marked as enabled, but only be activated (i.e. callbacks - * can be called) right before the next tick. Callbacks of watchers MUST not be called in the tick they were - * enabled. + * Watchers MUST immediately be marked as enabled, but only be activated (i.e. callbacks can be called) right before + * the next tick. Callbacks of watchers MUST NOT be called in the tick they were enabled. * * @param string $watcherId The watcher identifier. * @@ -150,7 +176,10 @@ abstract public function onSignal($signo, callable $callback, $data = null); abstract public function enable($watcherId); /** - * Disable a watcher. + * Disable a watcher immediately. + * + * A watcher MUST be disabled immediately, e.g. if a defer watcher disables a later defer watcher, the second defer + * watcher isn't executed in this tick. * * Disabling a watcher MUST NOT invalidate the watcher. Calling this function MUST NOT fail, even if passed an * invalid watcher. @@ -258,13 +287,13 @@ abstract public function setErrorHandler(callable $callback = null); * The returned array MUST contain the following data describing the driver's currently registered watchers: * * [ - * "defer" => ["enabled" => int, "disabled" => int], - * "delay" => ["enabled" => int, "disabled" => int], - * "repeat" => ["enabled" => int, "disabled" => int], - * "on_readable" => ["enabled" => int, "disabled" => int], - * "on_writable" => ["enabled" => int, "disabled" => int], - * "on_signal" => ["enabled" => int, "disabled" => int], - * "watchers" => ["referenced" => int, "unreferenced" => int], + * "defer" => ["enabled" => int, "disabled" => int], + * "delay" => ["enabled" => int, "disabled" => int], + * "repeat" => ["enabled" => int, "disabled" => int], + * "on_readable" => ["enabled" => int, "disabled" => int], + * "on_writable" => ["enabled" => int, "disabled" => int], + * "on_signal" => ["enabled" => int, "disabled" => int], + * "enabled_watchers" => ["referenced" => int, "unreferenced" => int], * ]; * * Implementations MAY optionally add more information in the array but at minimum the above `key => value` format