Skip to content

Commit

Permalink
Fix mem leaks & circular reference; prepare tag 1.1.1
Browse files Browse the repository at this point in the history
  • Loading branch information
bwoebi committed May 11, 2016
1 parent 66fa98d commit cc7db03
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 21 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
### 1.1.1

- Fix memory leak in NativeReactor, retaining an empty array
for each stream.
- Remove circular references in UvReactor to avoid garbage
collector calls.

1.1.0
-----

Expand Down
4 changes: 2 additions & 2 deletions lib/NativeReactor.php
Original file line number Diff line number Diff line change
Expand Up @@ -585,14 +585,14 @@ public function disable($watcherId) {
$streamId = $watcher->streamId;
unset($this->readWatchers[$streamId][$watcherId]);
if (empty($this->readWatchers[$streamId])) {
unset($this->readStreams[$streamId]);
unset($this->readWatchers[$streamId], $this->readStreams[$streamId]);
}
break;
case Watcher::IO_WRITER:
$streamId = $watcher->streamId;
unset($this->writeWatchers[$streamId][$watcherId]);
if (empty($this->writeWatchers[$streamId])) {
unset($this->writeStreams[$streamId]);
unset($this->writeWatchers[$streamId], $this->writeStreams[$streamId]);
}
break;
case Watcher::IMMEDIATE:
Expand Down
40 changes: 21 additions & 19 deletions lib/UvReactor.php
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,7 @@ private function registerTimer(callable $callback, $msDelay, $msInterval, array
$watcher->id = $watcherId = \spl_object_hash($watcher);
$watcher->type = ($isRepeating) ? Watcher::TIMER_REPEAT : Watcher::TIMER_ONCE;
$watcher->uvHandle = \uv_timer_init($this->loop);
$watcher->callback = $this->wrapTimerCallback($watcher, $callback);
$watcher->cbData = @$options["cb_data"];
$watcher->callback = $this->wrapTimerCallback($watcher, $callback, isset($options["cb_data"]) ? $options["cb_data"] : null);
$watcher->isEnabled = isset($options["enable"]) ? (bool) $options["enable"] : true;
$watcher->keepAlive = isset($options["keep_alive"]) ? (bool) $options["keep_alive"] : true;
$this->keepAliveCount += ($watcher->isEnabled && $watcher->keepAlive);
Expand All @@ -251,17 +250,18 @@ private function registerTimer(callable $callback, $msDelay, $msInterval, array
return $watcherId;
}

private function wrapTimerCallback($watcher, $callback) {
return function() use ($watcher, $callback) {
private function wrapTimerCallback($watcher, $callback, $cbData) {
$watcherId = $watcher->id;
$once = $watcher->type === Watcher::TIMER_ONCE;
return function() use ($once, $watcherId, $callback, $cbData) {
try {
$watcherId = $watcher->id;
$result = \call_user_func($callback, $watcherId, $watcher->cbData);
$result = \call_user_func($callback, $watcherId, $cbData);
if ($result instanceof \Generator) {
resolve($result)->when($this->onCoroutineResolution);
}
// The isset() check is necessary because the "once" timer
// callback may have cancelled itself when it was invoked.
if ($watcher->type === Watcher::TIMER_ONCE && isset($this->watchers[$watcherId])) {
if ($once && isset($this->watchers[$watcherId])) {
$this->clearWatcher($watcherId);
}
} catch (\Throwable $e) {
Expand Down Expand Up @@ -326,7 +326,7 @@ private function watchStream($stream, callable $callback, $type, array $options)
$watcher->id = $watcherId = \spl_object_hash($watcher);
$watcher->type = $type;
$watcher->callback = $callback;
$watcher->cbData = @$options["cb_data"];
$watcher->cbData = isset($options["cb_data"]) ? $options["cb_data"] : null;
$watcher->isEnabled = isset($options["enable"]) ? (bool) $options["enable"] : true;
$watcher->keepAlive = isset($options["keep_alive"]) ? (bool) $options["keep_alive"] : true;

Expand Down Expand Up @@ -386,19 +386,20 @@ private function makePollHandle($stream) {
$streamId = (int) $stream;

$poll = new \StdClass;
$poll->readers = [];
$poll->writers = [];
$readers = $writers = [];
$poll->readers = &$readers;
$poll->writers = &$writers;
$poll->disable = [];
$poll->flags = 0;
$poll->handle = \call_user_func($pollInitFunc, $this->loop, $stream);
$poll->callback = function($uvHandle, $stat, $events) use ($poll) {
$poll->callback = function($uvHandle, $stat, $events) use (&$readers, &$writers) {
if ($events & \UV::READABLE) {
foreach ($poll->readers as $watcher) {
foreach ($readers as $watcher) {
$this->invokePollWatcher($watcher);
}
}
if ($events & \UV::WRITABLE) {
foreach ($poll->writers as $watcher) {
foreach ($writers as $watcher) {
$this->invokePollWatcher($watcher);
}
}
Expand Down Expand Up @@ -439,12 +440,11 @@ public function onSignal($signo, callable $func, array $options = []) {
$watcher = new \StdClass;
$watcher->id = $watcherId = \spl_object_hash($watcher);
$watcher->type = Watcher::SIGNAL;
$watcher->callback = $this->wrapSignalCallback($watcher, $func);
$watcher->cbData = @$options["cb_data"];
$watcher->signo = $signo;
$watcher->callback = $this->wrapSignalCallback($watcher, $func, isset($options["cb_data"]) ? $options["cb_data"] : null);
$watcher->isEnabled = isset($options["enable"]) ? (bool) $options["enable"] : true;
$watcher->keepAlive = isset($options["keep_alive"]) ? (bool) $options["keep_alive"] : true;
$this->keepAliveCount += ($watcher->isEnabled && $watcher->keepAlive);
$watcher->signo = $signo;
$watcher->uvHandle = \uv_signal_init($this->loop);
if (empty($watcher->keepAlive)) {
\uv_unref($watcher->uvHandle);
Expand All @@ -457,10 +457,12 @@ public function onSignal($signo, callable $func, array $options = []) {
return $watcherId;
}

private function wrapSignalCallback($watcher, $callback) {
return function() use ($watcher, $callback) {
private function wrapSignalCallback($watcher, $callback, $cbData) {
$watcherId = $watcher->id;
$signo = $watcher->signo;
return function() use ($watcherId, $signo, $callback, $cbData) {
try {
$result = \call_user_func($callback, $watcher->id, $watcher->signo, $watcher->cbData);
$result = \call_user_func($callback, $watcherId, $signo, $cbData);
if ($result instanceof \Generator) {
resolve($result)->when($this->onCoroutineResolution);
}
Expand Down

0 comments on commit cc7db03

Please sign in to comment.