Skip to content

Commit 3e4b3d4

Browse files
committed
Merge branch 'refs/heads/future-tick'
2 parents 9c5d786 + b98a945 commit 3e4b3d4

File tree

8 files changed

+331
-9
lines changed

8 files changed

+331
-9
lines changed

examples/next-tick.php

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
/*
4+
This example shows how nextTick and futureTick events are scheduled for
5+
execution.
6+
7+
The expected output is:
8+
9+
next-tick #1
10+
next-tick #2
11+
future-tick #1
12+
timer
13+
future-tick #2
14+
15+
Note that both nextTick and futureTick events are executed before timer and I/O
16+
events on each tick.
17+
18+
nextTick events registered inside an existing nextTick handler are guaranteed
19+
to be executed before timer and I/O handlers are processed, whereas futureTick
20+
handlers are always deferred.
21+
*/
22+
23+
require __DIR__.'/../vendor/autoload.php';
24+
25+
$loop = React\EventLoop\Factory::create();
26+
27+
$loop->addTimer(
28+
0,
29+
function () {
30+
echo 'timer' . PHP_EOL;
31+
}
32+
);
33+
34+
$loop->nextTick(
35+
function ($loop) {
36+
echo 'next-tick #1' . PHP_EOL;
37+
38+
$loop->nextTick(
39+
function () {
40+
echo 'next-tick #2' . PHP_EOL;
41+
}
42+
);
43+
}
44+
);
45+
46+
$loop->futureTick(
47+
function ($loop) {
48+
echo 'future-tick #1' . PHP_EOL;
49+
50+
$loop->futureTick(
51+
function () {
52+
echo 'future-tick #2' . PHP_EOL;
53+
}
54+
);
55+
}
56+
);
57+
58+
$loop->run();

src/React/EventLoop/ExtEventLoop.php

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Event;
66
use EventBase;
7+
use React\EventLoop\Tick\FutureTickQueue;
78
use React\EventLoop\Tick\NextTickQueue;
89
use React\EventLoop\Timer\Timer;
910
use React\EventLoop\Timer\TimerInterface;
@@ -16,6 +17,7 @@ class ExtEventLoop implements LoopInterface
1617
{
1718
private $eventBase;
1819
private $nextTickQueue;
20+
private $futureTickQueue;
1921
private $timerCallback;
2022
private $timerEvents;
2123
private $streamCallback;
@@ -29,6 +31,7 @@ public function __construct()
2931
{
3032
$this->eventBase = new EventBase();
3133
$this->nextTickQueue = new NextTickQueue($this);
34+
$this->futureTickQueue = new FutureTickQueue($this);
3235
$this->timerEvents = new SplObjectStorage();
3336

3437
$this->createTimerCallback();
@@ -157,13 +160,23 @@ public function nextTick(callable $listener)
157160
$this->nextTickQueue->add($listener);
158161
}
159162

163+
/**
164+
* {@inheritdoc}
165+
*/
166+
public function futureTick(callable $listener)
167+
{
168+
$this->futureTickQueue->add($listener);
169+
}
170+
160171
/**
161172
* {@inheritdoc}
162173
*/
163174
public function tick()
164175
{
165176
$this->nextTickQueue->tick();
166177

178+
$this->futureTickQueue->tick();
179+
167180
// @-suppression: https://github.com/reactphp/react/pull/234#discussion-diff-7759616R226
168181
@$this->eventBase->loop(EventBase::LOOP_ONCE | EventBase::LOOP_NONBLOCK);
169182
}
@@ -178,12 +191,17 @@ public function run()
178191
while ($this->running) {
179192
$this->nextTickQueue->tick();
180193

181-
if (!$this->streamEvents && !$this->timerEvents->count()) {
194+
$this->futureTickQueue->tick();
195+
196+
$flags = EventBase::LOOP_ONCE;
197+
if (!$this->running || !$this->futureTickQueue->isEmpty()) {
198+
$flags |= EventBase::LOOP_NONBLOCK;
199+
} elseif (!$this->streamEvents && !$this->timerEvents->count()) {
182200
break;
183201
}
184202

185203
// @-suppression: https://github.com/reactphp/react/pull/234#discussion-diff-7759616R226
186-
@$this->eventBase->loop(EventBase::LOOP_ONCE);
204+
@$this->eventBase->loop($flags);
187205
}
188206
}
189207

src/React/EventLoop/LibEvLoop.php

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use libev\EventLoop;
66
use libev\IOEvent;
77
use libev\TimerEvent;
8+
use React\EventLoop\Tick\FutureTickQueue;
89
use React\EventLoop\Tick\NextTickQueue;
910
use React\EventLoop\Timer\Timer;
1011
use React\EventLoop\Timer\TimerInterface;
@@ -18,6 +19,7 @@ class LibEvLoop implements LoopInterface
1819
{
1920
private $loop;
2021
private $nextTickQueue;
22+
private $futureTickQueue;
2123
private $timerEvents;
2224
private $readEvents = [];
2325
private $writeEvents = [];
@@ -27,6 +29,7 @@ public function __construct()
2729
{
2830
$this->loop = new EventLoop();
2931
$this->nextTickQueue = new NextTickQueue($this);
32+
$this->futureTickQueue = new FutureTickQueue($this);
3033
$this->timerEvents = new SplObjectStorage();
3134
}
3235

@@ -162,13 +165,23 @@ public function nextTick(callable $listener)
162165
$this->nextTickQueue->add($listener);
163166
}
164167

168+
/**
169+
* {@inheritdoc}
170+
*/
171+
public function futureTick(callable $listener)
172+
{
173+
$this->futureTickQueue->add($listener);
174+
}
175+
165176
/**
166177
* {@inheritdoc}
167178
*/
168179
public function tick()
169180
{
170181
$this->nextTickQueue->tick();
171182

183+
$this->futureTickQueue->tick();
184+
172185
$this->loop->run(EventLoop::RUN_ONCE | EventLoop::RUN_NOWAIT);
173186
}
174187

@@ -182,11 +195,16 @@ public function run()
182195
while ($this->running) {
183196
$this->nextTickQueue->tick();
184197

185-
if (!$this->readEvents && !$this->writeEvents && !$this->timerEvents->count()) {
198+
$this->futureTickQueue->tick();
199+
200+
$flags = EventLoop::RUN_ONCE;
201+
if (!$this->running || !$this->futureTickQueue->isEmpty()) {
202+
$flags |= EventLoop::RUN_NOWAIT;
203+
} elseif (!$this->readEvents && !$this->writeEvents && !$this->timerEvents->count()) {
186204
break;
187205
}
188206

189-
$this->loop->run(EventLoop::RUN_ONCE);
207+
$this->loop->run($flags);
190208
}
191209
}
192210

src/React/EventLoop/LibEventLoop.php

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Event;
66
use EventBase;
7+
use React\EventLoop\Tick\FutureTickQueue;
78
use React\EventLoop\Tick\NextTickQueue;
89
use React\EventLoop\Timer\Timer;
910
use React\EventLoop\Timer\TimerInterface;
@@ -18,6 +19,7 @@ class LibEventLoop implements LoopInterface
1819

1920
private $eventBase;
2021
private $nextTickQueue;
22+
private $futureTickQueue;
2123
private $timerCallback;
2224
private $timerEvents;
2325
private $streamCallback;
@@ -31,6 +33,7 @@ public function __construct()
3133
{
3234
$this->eventBase = event_base_new();
3335
$this->nextTickQueue = new NextTickQueue($this);
36+
$this->futureTickQueue = new FutureTickQueue($this);
3437
$this->timerEvents = new SplObjectStorage();
3538

3639
$this->createTimerCallback();
@@ -166,13 +169,23 @@ public function nextTick(callable $listener)
166169
$this->nextTickQueue->add($listener);
167170
}
168171

172+
/**
173+
* {@inheritdoc}
174+
*/
175+
public function futureTick(callable $listener)
176+
{
177+
$this->futureTickQueue->add($listener);
178+
}
179+
169180
/**
170181
* {@inheritdoc}
171182
*/
172183
public function tick()
173184
{
174185
$this->nextTickQueue->tick();
175186

187+
$this->futureTickQueue->tick();
188+
176189
event_base_loop($this->eventBase, EVLOOP_ONCE | EVLOOP_NONBLOCK);
177190
}
178191

@@ -186,11 +199,16 @@ public function run()
186199
while ($this->running) {
187200
$this->nextTickQueue->tick();
188201

189-
if (!$this->streamEvents && !$this->timerEvents->count()) {
202+
$this->futureTickQueue->tick();
203+
204+
$flags = EVLOOP_ONCE;
205+
if (!$this->running || !$this->futureTickQueue->isEmpty()) {
206+
$flags |= EVLOOP_NONBLOCK;
207+
} elseif (!$this->streamEvents && !$this->timerEvents->count()) {
190208
break;
191209
}
192210

193-
event_base_loop($this->eventBase, EVLOOP_ONCE);
211+
event_base_loop($this->eventBase, $flags);
194212
}
195213
}
196214

src/React/EventLoop/LoopInterface.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,15 @@ public function isTimerActive(TimerInterface $timer);
9595
*/
9696
public function nextTick(callable $listener);
9797

98+
/**
99+
* Schedule a callback to be invoked on a future tick of the event loop.
100+
*
101+
* Callbacks are guaranteed to be executed in the order they are enqueued.
102+
*
103+
* @param callable $listener The callback to invoke.
104+
*/
105+
public function futureTick(callable $listener);
106+
98107
/**
99108
* Perform a single iteration of the event loop.
100109
*/

src/React/EventLoop/StreamSelectLoop.php

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace React\EventLoop;
44

5+
use React\EventLoop\Tick\FutureTickQueue;
56
use React\EventLoop\Tick\NextTickQueue;
67
use React\EventLoop\Timer\Timer;
78
use React\EventLoop\Timer\TimerInterface;
@@ -12,7 +13,10 @@
1213
*/
1314
class StreamSelectLoop implements LoopInterface
1415
{
16+
const MICROSECONDS_PER_SECOND = 1000000;
17+
1518
private $nextTickQueue;
19+
private $futureTickQueue;
1620
private $timers;
1721
private $readStreams = [];
1822
private $readListeners = [];
@@ -23,6 +27,7 @@ class StreamSelectLoop implements LoopInterface
2327
public function __construct()
2428
{
2529
$this->nextTickQueue = new NextTickQueue($this);
30+
$this->futureTickQueue = new FutureTickQueue($this);
2631
$this->timers = new Timers();
2732
}
2833

@@ -135,13 +140,23 @@ public function nextTick(callable $listener)
135140
$this->nextTickQueue->add($listener);
136141
}
137142

143+
/**
144+
* {@inheritdoc}
145+
*/
146+
public function futureTick(callable $listener)
147+
{
148+
$this->futureTickQueue->add($listener);
149+
}
150+
138151
/**
139152
* {@inheritdoc}
140153
*/
141154
public function tick()
142155
{
143156
$this->nextTickQueue->tick();
144157

158+
$this->futureTickQueue->tick();
159+
145160
$this->timers->tick();
146161

147162
$this->waitForStreamActivity(0);
@@ -157,10 +172,12 @@ public function run()
157172
while ($this->running) {
158173
$this->nextTickQueue->tick();
159174

175+
$this->futureTickQueue->tick();
176+
160177
$this->timers->tick();
161178

162-
// Timers have placed more items on the next-tick queue ...
163-
if (!$this->nextTickQueue->isEmpty()) {
179+
// Next-tick or future-tick queues have pending callbacks ...
180+
if (!$this->running || !$this->nextTickQueue->isEmpty() || !$this->futureTickQueue->isEmpty()) {
164181
$timeout = 0;
165182

166183
// There is a pending timer, only block until it is due ...
@@ -178,7 +195,7 @@ public function run()
178195
break;
179196
}
180197

181-
$this->waitForStreamActivity($timeout);
198+
$this->waitForStreamActivity($timeout * self::MICROSECONDS_PER_SECOND);
182199
}
183200
}
184201

0 commit comments

Comments
 (0)