From 059d75a17c2f252c5fe1b5217c91866089c09ece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Wed, 26 Apr 2017 11:48:27 +0200 Subject: [PATCH 1/2] Tick callback receives no arguments, remove cyclic dependency This can easily be replaced by using closure binding as documented. --- README.md | 29 ++++++++++++++--- examples/91-benchmark-ticks.php | 2 +- examples/93-benchmark-ticks-delay.php | 2 +- examples/94-benchmark-timers-delay.php | 2 +- src/ExtEventLoop.php | 2 +- src/LibEvLoop.php | 2 +- src/LibEventLoop.php | 2 +- src/LoopInterface.php | 43 +++++++++++++++++++++++--- src/StreamSelectLoop.php | 2 +- src/Tick/FutureTickQueue.php | 11 ++----- tests/AbstractLoopTest.php | 3 +- 11 files changed, 74 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index d67ad534..19e86181 100644 --- a/README.md +++ b/README.md @@ -126,12 +126,33 @@ schedule a callback to be invoked on a future tick of the event loop. This works very much similar to timers with an interval of zero seconds, but does not require the overhead of scheduling a timer queue. -Unlike timers, callbacks are guaranteed to be executed in the order they -are enqueued. +The tick callback function MUST be able to accept zero parameters. + +The tick callback function MUST NOT throw an `Exception`. +The return value of the tick callback function will be ignored and has +no effect, so for performance reasons you're recommended to not return +any excessive data structures. + +If you want to access any variables within your callback function, you +can bind arbitrary data to a callback closure like this: + +```php +function hello(LoopInterface $loop, $name) +{ + $loop->futureTick(function () use ($name) { + echo "hello $name\n"; + }); +} + +hello('Tester'); +``` + +Unlike timers, tick callbacks are guaranteed to be executed in the order +they are enqueued. Also, once a callback is enqueued, there's no way to cancel this operation. -This is often used to break down bigger tasks into smaller steps (a form of -cooperative multitasking). +This is often used to break down bigger tasks into smaller steps (a form +of cooperative multitasking). ```php $loop->futureTick(function () { diff --git a/examples/91-benchmark-ticks.php b/examples/91-benchmark-ticks.php index cdaa1e15..3f4690b3 100644 --- a/examples/91-benchmark-ticks.php +++ b/examples/91-benchmark-ticks.php @@ -9,7 +9,7 @@ $n = isset($argv[1]) ? (int)$argv[1] : 1000 * 100; for ($i = 0; $i < $n; ++$i) { - $loop->nextTick(function () { }); + $loop->futureTick(function () { }); } $loop->run(); diff --git a/examples/93-benchmark-ticks-delay.php b/examples/93-benchmark-ticks-delay.php index e242c65f..95ee78c6 100644 --- a/examples/93-benchmark-ticks-delay.php +++ b/examples/93-benchmark-ticks-delay.php @@ -11,7 +11,7 @@ if ($ticks > 0) { --$ticks; //$loop->addTimer(0, $tick); - $loop->nextTick($tick); + $loop->futureTick($tick); } else { echo 'done'; } diff --git a/examples/94-benchmark-timers-delay.php b/examples/94-benchmark-timers-delay.php index 69084e37..2d6cfa25 100644 --- a/examples/94-benchmark-timers-delay.php +++ b/examples/94-benchmark-timers-delay.php @@ -10,7 +10,7 @@ $tick = function () use (&$tick, &$ticks, $loop) { if ($ticks > 0) { --$ticks; - //$loop->nextTick($tick); + //$loop->futureTick($tick); $loop->addTimer(0, $tick); } else { echo 'done'; diff --git a/src/ExtEventLoop.php b/src/ExtEventLoop.php index d94e65db..538a3b9b 100644 --- a/src/ExtEventLoop.php +++ b/src/ExtEventLoop.php @@ -29,7 +29,7 @@ class ExtEventLoop implements LoopInterface public function __construct(EventBaseConfig $config = null) { $this->eventBase = new EventBase($config); - $this->futureTickQueue = new FutureTickQueue($this); + $this->futureTickQueue = new FutureTickQueue(); $this->timerEvents = new SplObjectStorage(); $this->createTimerCallback(); diff --git a/src/LibEvLoop.php b/src/LibEvLoop.php index 929d2eb6..f26ff6cd 100644 --- a/src/LibEvLoop.php +++ b/src/LibEvLoop.php @@ -26,7 +26,7 @@ class LibEvLoop implements LoopInterface public function __construct() { $this->loop = new EventLoop(); - $this->futureTickQueue = new FutureTickQueue($this); + $this->futureTickQueue = new FutureTickQueue(); $this->timerEvents = new SplObjectStorage(); } diff --git a/src/LibEventLoop.php b/src/LibEventLoop.php index b2c771cf..cf923d05 100644 --- a/src/LibEventLoop.php +++ b/src/LibEventLoop.php @@ -30,7 +30,7 @@ class LibEventLoop implements LoopInterface public function __construct() { $this->eventBase = event_base_new(); - $this->futureTickQueue = new FutureTickQueue($this); + $this->futureTickQueue = new FutureTickQueue(); $this->timerEvents = new SplObjectStorage(); $this->createTimerCallback(); diff --git a/src/LoopInterface.php b/src/LoopInterface.php index c2e5f75c..90ddba9d 100644 --- a/src/LoopInterface.php +++ b/src/LoopInterface.php @@ -91,14 +91,49 @@ public function isTimerActive(TimerInterface $timer); * This works very much similar to timers with an interval of zero seconds, * but does not require the overhead of scheduling a timer queue. * - * Unlike timers, callbacks are guaranteed to be executed in the order they - * are enqueued. + * The tick callback function MUST be able to accept zero parameters. + * + * The tick callback function MUST NOT throw an `Exception`. + * The return value of the tick callback function will be ignored and has + * no effect, so for performance reasons you're recommended to not return + * any excessive data structures. + * + * If you want to access any variables within your callback function, you + * can bind arbitrary data to a callback closure like this: + * + * ```php + * function hello(LoopInterface $loop, $name) + * { + * $loop->futureTick(function () use ($name) { + * echo "hello $name\n"; + * }); + * } + * + * hello('Tester'); + * ``` + * + * Unlike timers, tick callbacks are guaranteed to be executed in the order + * they are enqueued. * Also, once a callback is enqueued, there's no way to cancel this operation. * - * This is often used to break down bigger tasks into smaller steps (a form of - * cooperative multitasking). + * This is often used to break down bigger tasks into smaller steps (a form + * of cooperative multitasking). + * + * ```php + * $loop->futureTick(function () { + * echo 'b'; + * }); + * $loop->futureTick(function () { + * echo 'c'; + * }); + * echo 'a'; + * ``` + * + * See also [example #3](examples). * * @param callable $listener The callback to invoke. + * + * @return void */ public function futureTick(callable $listener); diff --git a/src/StreamSelectLoop.php b/src/StreamSelectLoop.php index 92fc5787..b4eafa2c 100644 --- a/src/StreamSelectLoop.php +++ b/src/StreamSelectLoop.php @@ -24,7 +24,7 @@ class StreamSelectLoop implements LoopInterface public function __construct() { - $this->futureTickQueue = new FutureTickQueue($this); + $this->futureTickQueue = new FutureTickQueue(); $this->timers = new Timers(); } diff --git a/src/Tick/FutureTickQueue.php b/src/Tick/FutureTickQueue.php index eeffd363..4a7c0bf7 100644 --- a/src/Tick/FutureTickQueue.php +++ b/src/Tick/FutureTickQueue.php @@ -2,20 +2,14 @@ namespace React\EventLoop\Tick; -use React\EventLoop\LoopInterface; use SplQueue; class FutureTickQueue { - private $eventLoop; private $queue; - /** - * @param LoopInterface $eventLoop The event loop passed as the first parameter to callbacks. - */ - public function __construct(LoopInterface $eventLoop) + public function __construct() { - $this->eventLoop = $eventLoop; $this->queue = new SplQueue(); } @@ -41,8 +35,7 @@ public function tick() while ($count--) { call_user_func( - $this->queue->dequeue(), - $this->eventLoop + $this->queue->dequeue() ); } } diff --git a/tests/AbstractLoopTest.php b/tests/AbstractLoopTest.php index 3351a8aa..2470e555 100644 --- a/tests/AbstractLoopTest.php +++ b/tests/AbstractLoopTest.php @@ -296,8 +296,7 @@ public function testFutureTick() { $called = false; - $callback = function ($loop) use (&$called) { - $this->assertSame($this->loop, $loop); + $callback = function () use (&$called) { $called = true; }; From 4827e00b5ac62a8d4d285f370bd8188eacbb0513 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20L=C3=BCck?= Date: Wed, 26 Apr 2017 11:50:48 +0200 Subject: [PATCH 2/2] Mark internal tick API as final to discourage external usage --- src/Tick/FutureTickQueue.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Tick/FutureTickQueue.php b/src/Tick/FutureTickQueue.php index 4a7c0bf7..eb65345d 100644 --- a/src/Tick/FutureTickQueue.php +++ b/src/Tick/FutureTickQueue.php @@ -4,7 +4,15 @@ use SplQueue; -class FutureTickQueue +/** + * A tick queue implementation that can hold multiple callback functions + * + * This class should only be used internally, see LoopInterface instead. + * + * @see LoopInterface + * @internal + */ +final class FutureTickQueue { private $queue;