Skip to content

Commit 2cbc5fe

Browse files
committed
Various changes as per comments in PR.
1 parent 3028fca commit 2cbc5fe

File tree

2 files changed

+135
-116
lines changed

2 files changed

+135
-116
lines changed

ExtEventLoop.php

Lines changed: 131 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
use React\EventLoop\Timer\Timer;
99
use React\EventLoop\Timer\TimerInterface;
1010
use SplObjectStorage;
11-
use stdClass;
1211

1312
/**
1413
* An ext-event based event-loop.
@@ -17,10 +16,14 @@ class ExtEventLoop implements LoopInterface
1716
{
1817
private $eventBase;
1918
private $nextTickQueue;
19+
private $timerCallback;
2020
private $timerEvents;
21-
private $streamEvents;
21+
private $streamCallback;
22+
private $streamEvents = [];
23+
private $streamFlags = [];
24+
private $readListeners = [];
25+
private $writeListeners = [];
2226
private $running;
23-
private $keepAlive;
2427

2528
/**
2629
* @param EventBase|null $eventBase The libevent event base object.
@@ -34,13 +37,9 @@ public function __construct(EventBase $eventBase = null)
3437
$this->eventBase = $eventBase;
3538
$this->nextTickQueue = new NextTickQueue($this);
3639
$this->timerEvents = new SplObjectStorage;
37-
$this->streamEvents = [];
3840

39-
// Closures for cancelled timers and removed stream listeners are
40-
// kept in the keepAlive array until the flushEvents() is complete
41-
// to prevent the PHP fatal error caused by ext-event:
42-
// "Cannot destroy active lambda function"
43-
$this->keepAlive = [];
41+
$this->createTimerCallback();
42+
$this->createStreamCallback();
4443
}
4544

4645
/**
@@ -51,7 +50,12 @@ public function __construct(EventBase $eventBase = null)
5150
*/
5251
public function addReadStream($stream, $listener)
5352
{
54-
$this->addStreamEvent($stream, Event::READ, $listener);
53+
$key = (int) $stream;
54+
55+
if (!isset($this->readListeners[$key])) {
56+
$this->readListeners[$key] = $listener;
57+
$this->subscribeStreamEvent($stream, Event::READ);
58+
}
5559
}
5660

5761
/**
@@ -62,7 +66,12 @@ public function addReadStream($stream, $listener)
6266
*/
6367
public function addWriteStream($stream, $listener)
6468
{
65-
$this->addStreamEvent($stream, Event::WRITE, $listener);
69+
$key = (int) $stream;
70+
71+
if (!isset($this->writeListeners[$key])) {
72+
$this->writeListeners[$key] = $listener;
73+
$this->subscribeStreamEvent($stream, Event::WRITE, $listener);
74+
}
6675
}
6776

6877
/**
@@ -72,7 +81,12 @@ public function addWriteStream($stream, $listener)
7281
*/
7382
public function removeReadStream($stream)
7483
{
75-
$this->removeStreamEvent($stream, Event::READ);
84+
$key = (int) $stream;
85+
86+
if (isset($this->readListeners[$key])) {
87+
unset($this->readListeners[$key]);
88+
$this->unsubscribeStreamEvent($stream, Event::READ);
89+
}
7690
}
7791

7892
/**
@@ -82,7 +96,12 @@ public function removeReadStream($stream)
8296
*/
8397
public function removeWriteStream($stream)
8498
{
85-
$this->removeStreamEvent($stream, Event::WRITE);
99+
$key = (int) $stream;
100+
101+
if (isset($this->writeListeners[$key])) {
102+
unset($this->writeListeners[$key]);
103+
$this->unsubscribeStreamEvent($stream, Event::WRITE);
104+
}
86105
}
87106

88107
/**
@@ -94,17 +113,16 @@ public function removeStream($stream)
94113
{
95114
$key = (int) $stream;
96115

97-
if (!isset($this->streamEvents[$key])) {
98-
return;
99-
}
100-
101-
$entry = $this->streamEvents[$key];
102-
103-
$entry->event->free();
104-
105-
unset($this->streamEvents[$key]);
116+
if (isset($this->streamEvents[$key])) {
117+
$this->streamEvents[$key]->free();
106118

107-
$this->keepAlive[] = $entry->callback;
119+
unset(
120+
$this->streamFlags[$key],
121+
$this->streamEvents[$key],
122+
$this->readListeners[$key],
123+
$this->writeListeners[$key]
124+
);
125+
}
108126
}
109127

110128
/**
@@ -155,13 +173,8 @@ public function addPeriodicTimer($interval, $callback)
155173
public function cancelTimer(TimerInterface $timer)
156174
{
157175
if ($this->isTimerActive($timer)) {
158-
$entry = $this->timerEvents[$timer];
159-
176+
$this->timerEvents[$timer]->free();
160177
$this->timerEvents->detach($timer);
161-
162-
$entry->event->free();
163-
164-
$this->keepAlive[] = $entry->callback;
165178
}
166179
}
167180

@@ -200,8 +213,6 @@ public function tick()
200213
$this->nextTickQueue->tick();
201214

202215
$this->eventBase->loop(EventBase::LOOP_ONCE | EventBase::LOOP_NONBLOCK);
203-
204-
$this->keepAlive = [];
205216
}
206217

207218
/**
@@ -213,20 +224,13 @@ public function run()
213224

214225
while ($this->running) {
215226

216-
if (
217-
!$this->streamEvents
218-
&& !$this->timerEvents->count()
219-
&& $this->nextTickQueue->isEmpty()
220-
) {
227+
$this->nextTickQueue->tick();
228+
229+
if (!$this->streamEvents && !$this->timerEvents->count()) {
221230
break;
222231
}
223232

224-
$this->nextTickQueue->tick();
225-
226233
$this->eventBase->loop(EventBase::LOOP_ONCE);
227-
228-
$this->keepAlive = [];
229-
230234
}
231235
}
232236

@@ -251,74 +255,48 @@ protected function scheduleTimer(TimerInterface $timer)
251255
$flags |= Event::PERSIST;
252256
}
253257

254-
$entry = new stdClass;
255-
$entry->callback = function () use ($timer) {
256-
call_user_func($timer->getCallback(), $timer);
257-
258-
// Clean-up one shot timers ...
259-
if ($this->isTimerActive($timer) && !$timer->isPeriodic()) {
260-
$this->cancelTimer($timer);
261-
}
262-
};
263-
264-
$entry->event = new Event(
258+
$this->timerEvents[$timer] = $event = new Event(
265259
$this->eventBase,
266260
-1,
267261
$flags,
268-
$entry->callback
262+
$this->timerCallback,
263+
$timer
269264
);
270265

271-
$this->timerEvents->attach($timer, $entry);
272-
273-
$entry->event->add($timer->getInterval());
266+
$event->add($timer->getInterval());
274267
}
275268

276269
/**
277270
* Create a new ext-event Event object, or update the existing one.
278271
*
279-
* @param stream $stream
280-
* @param integer $flag Event::READ or Event::WRITE
281-
* @param callable $listener
272+
* @param stream $stream
273+
* @param integer $flag Event::READ or Event::WRITE
282274
*/
283-
protected function addStreamEvent($stream, $flag, $listener)
275+
protected function subscribeStreamEvent($stream, $flag)
284276
{
285277
$key = (int) $stream;
286278

287279
if (isset($this->streamEvents[$key])) {
288-
$entry = $this->streamEvents[$key];
289-
} else {
290-
$entry = new stdClass;
291-
$entry->event = null;
292-
$entry->flags = 0;
293-
$entry->listeners = [
294-
Event::READ => null,
295-
Event::WRITE => null,
296-
];
297-
298-
$entry->callback = function ($stream, $flags, $loop) use ($entry) {
299-
foreach ([Event::READ, Event::WRITE] as $flag) {
300-
if (
301-
$flag === ($flags & $flag) &&
302-
is_callable($entry->listeners[$flag])
303-
) {
304-
call_user_func(
305-
$entry->listeners[$flag],
306-
$stream,
307-
$this
308-
);
309-
}
310-
}
311-
};
312-
313-
$this->streamEvents[$key] = $entry;
314-
}
280+
$event = $this->streamEvents[$key];
315281

316-
$entry->listeners[$flag] = $listener;
317-
$entry->flags |= $flag;
282+
$event->del();
318283

319-
$this->configureStreamEvent($entry, $stream);
284+
$event->set(
285+
$this->eventBase,
286+
$stream,
287+
Event::PERSIST | ($this->streamFlags[$key] |= $flag),
288+
$this->streamCallback
289+
);
290+
} else {
291+
$this->streamEvents[$key] = $event = new Event(
292+
$this->eventBase,
293+
$stream,
294+
Event::PERSIST | ($this->streamFlags[$key] = $flag),
295+
$this->streamCallback
296+
);
297+
}
320298

321-
$entry->event->add();
299+
$event->add();
322300
}
323301

324302
/**
@@ -328,42 +306,80 @@ protected function addStreamEvent($stream, $flag, $listener)
328306
* @param stream $stream
329307
* @param integer $flag Event::READ or Event::WRITE
330308
*/
331-
protected function removeStreamEvent($stream, $flag)
309+
protected function unsubscribeStreamEvent($stream, $flag)
332310
{
333311
$key = (int) $stream;
334312

335-
if (!isset($this->streamEvents[$key])) {
313+
$flags = $this->streamFlags[$key] &= ~$flag;
314+
315+
if (0 === $flags) {
316+
$this->removeStream($stream);
317+
336318
return;
337319
}
338320

339-
$entry = $this->streamEvents[$key];
340-
$entry->flags &= ~$flag;
341-
$entry->listeners[$flag] = null;
321+
$event = $this->streamEvents[$key];
342322

343-
if (0 === $entry->flags) {
344-
$this->removeStream($stream);
345-
} else {
346-
$this->configureStreamEvent($entry, $stream);
347-
}
323+
$event->del();
324+
325+
$event->set(
326+
$this->eventBase,
327+
$stream,
328+
Event::PERSIST | $flags,
329+
$this->streamCallback
330+
);
331+
332+
$event->add();
348333
}
349334

350335
/**
351-
* Create or update an ext-event Event object for the stream.
336+
* Create a callback used as the target of timer events.
337+
*
338+
* A reference is kept to the callback for the lifetime of the loop
339+
* to prevent "Cannot destroy active lambda function" fatal error from
340+
* the event extension.
352341
*/
353-
protected function configureStreamEvent($entry, $stream)
342+
protected function createTimerCallback()
354343
{
355-
$flags = $entry->flags | Event::PERSIST;
344+
$this->timerCallback = function ($streamIgnored, $flagsIgnored, $timer) {
356345

357-
if ($entry->event) {
358-
$entry->event->del();
359-
$entry->event->set(
360-
$this->eventBase, $stream, $flags, $entry->callback
361-
);
362-
$entry->event->add();
363-
} else {
364-
$entry->event = new Event(
365-
$this->eventBase, $stream, $flags, $entry->callback
366-
);
367-
}
346+
call_user_func($timer->getCallback(), $timer);
347+
348+
// Clean-up one shot timers ...
349+
if (!$timer->isPeriodic() && $this->isTimerActive($timer)) {
350+
$this->cancelTimer($timer);
351+
}
352+
353+
};
354+
}
355+
356+
/**
357+
* Create a callback used as the target of stream events.
358+
*
359+
* A reference is kept to the callback for the lifetime of the loop
360+
* to prevent "Cannot destroy active lambda function" fatal error from
361+
* the event extension.
362+
*/
363+
protected function createStreamCallback()
364+
{
365+
$this->streamCallback = function ($stream, $flags) {
366+
367+
$key = (int) $stream;
368+
369+
if (
370+
Event::READ === (Event::READ & $flags)
371+
&& isset($this->readListeners[$key])
372+
) {
373+
call_user_func($this->readListeners[$key], $stream, $this);
374+
}
375+
376+
if (
377+
Event::WRITE === (Event::WRITE & $flags)
378+
&& isset($this->writeListeners[$key])
379+
) {
380+
call_user_func($this->writeListeners[$key], $stream, $this);
381+
}
382+
383+
};
368384
}
369385
}

StreamSelectLoop.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,10 @@ public function run()
201201

202202
// There is a pending timer, only block until it is due ...
203203
if ($scheduledAt = $this->timers->getFirst()) {
204-
$timeout = max(0, $scheduledAt - $this->timers->getTime());
204+
205+
if (0 > $timeout = $scheduledAt - $this->timers->getTime()) {
206+
$timeout = 0;
207+
}
205208

206209
// The only possible event is stream activity, so wait forever ...
207210
} elseif ($this->readStreams || $this->writeStreams) {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy