Skip to content

Commit 134b94d

Browse files
committed
[EventLoop] Fix timers when using libevent-based loop.
Since a closure cannot destroy itself from inside its body, to prevent PHP from raising the fatal error "Cannot destroy active lambda function" when cancelling a periodic timer from inside its callback we previously tried to postpone the call to event_free() on the affected libevent timer resource at a later stage by queueing up timer resources to be freed. This approach was quite awkward and eventually lead to complex code, difficult to test and with other unexpected side effects such as memory leaks in a couple of corner cases. This new fix greatly reduces code complexity and makes it possible to free up timer resources immediatly when cancelling a timer: we simply keep a reference of our internal timer callback from inside itself so that its refcount is still > 1 when calling event_free(), effectively preventing PHP's garbage collector to kick in when the closure is still active.
1 parent 6da6647 commit 134b94d

File tree

1 file changed

+3
-11
lines changed

1 file changed

+3
-11
lines changed

LibEventLoop.php

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ class LibEventLoop implements LoopInterface
1010
private $callback;
1111

1212
private $timers = array();
13-
private $timersGc;
1413

1514
private $events = array();
1615
private $flags = array();
@@ -21,8 +20,6 @@ public function __construct()
2120
{
2221
$this->base = event_base_new();
2322
$this->callback = $this->createLibeventCallback();
24-
$this->timersGc = new \SplQueue();
25-
$this->timersGc->setIteratorMode(\SplQueue::IT_MODE_DELETE);
2623
}
2724

2825
protected function createLibeventCallback()
@@ -171,18 +168,13 @@ protected function addTimerInternal($interval, $callback, $periodic = false)
171168
);
172169

173170
$timer->signature = spl_object_hash($timer);
174-
$timersGc = $this->timersGc;
175-
176171
$that = $this;
177-
$callback = function () use ($timer, $timersGc, $that) {
178-
foreach ($timersGc as $resource) {
179-
event_free($resource);
180-
}
181172

173+
$callback = function () use ($timer, $that, &$callback) {
182174
if ($timer->cancelled === false) {
183175
call_user_func($timer->callback, $timer->signature, $timer->loop);
184176

185-
if ($timer->periodic === true) {
177+
if ($timer->periodic === true && $timer->cancelled === false) {
186178
event_add($timer->resource, $timer->interval);
187179
} else {
188180
$that->cancelTimer($timer->signature);
@@ -216,7 +208,7 @@ public function cancelTimer($signature)
216208

217209
$timer->cancelled = true;
218210
event_del($timer->resource);
219-
$this->timersGc->enqueue($timer->resource);
211+
event_free($timer->resource);
220212
unset($this->timers[$signature]);
221213
}
222214
}

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