Skip to content

Commit a2e9560

Browse files
authored
Merge pull request #36 from lucasnetau/gc_cycles
Improve `first()` promise resolution to clean up any garbage references
2 parents 2806f6f + 01297de commit a2e9560

File tree

2 files changed

+45
-2
lines changed

2 files changed

+45
-2
lines changed

src/functions.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ function first(EventEmitterInterface $stream, $event = 'data')
141141
return new Promise\Promise(function ($resolve, $reject) use ($stream, $event, &$listener) {
142142
$listener = function ($data = null) use ($stream, $event, &$listener, $resolve) {
143143
$stream->removeListener($event, $listener);
144+
$listener = null;
144145
$resolve($data);
145146
};
146147
$stream->on($event, $listener);
@@ -156,8 +157,11 @@ function first(EventEmitterInterface $stream, $event = 'data')
156157
});
157158
}
158159

159-
$stream->on('close', function () use ($stream, $event, $listener, $reject) {
160-
$stream->removeListener($event, $listener);
160+
$stream->on('close', function () use ($stream, $event, &$listener, $reject) {
161+
if ($listener !== null) {
162+
$stream->removeListener($event, $listener);
163+
$listener = null;
164+
}
161165
$reject(new \RuntimeException('Stream closed'));
162166
});
163167
}, function ($_, $reject) use ($stream, $event, &$listener) {

tests/FirstTest.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,43 @@ public function testCancelPendingStreamWillReject()
112112

113113
$this->expectPromiseReject($promise);
114114
}
115+
116+
public function testNoGarbageCollectionCyclesAfterClosingStream()
117+
{
118+
\gc_collect_cycles();
119+
$stream = new ThroughStream();
120+
$promise = Stream\first($stream);
121+
122+
$stream->close();
123+
124+
$this->assertSame(0, \gc_collect_cycles());
125+
}
126+
127+
public function testShouldResolveWithoutCreatingGarbageCyclesAfterDataThenClose()
128+
{
129+
\gc_collect_cycles();
130+
131+
$stream = new ThroughStream();
132+
133+
$promise = Stream\first($stream);
134+
135+
$stream->emit('data', array('hello', $stream));
136+
$stream->close();
137+
138+
$this->expectPromiseResolve($promise);
139+
$this->assertSame(0, \gc_collect_cycles());
140+
}
141+
142+
public function testCancelPendingStreamWillRejectWithoutCreatingGarbageCycles()
143+
{
144+
\gc_collect_cycles();
145+
$stream = new ThroughStream();
146+
147+
$promise = Stream\first($stream);
148+
149+
$promise->cancel();
150+
151+
$this->expectPromiseReject($promise);
152+
$this->assertSame(0, \gc_collect_cycles());
153+
}
115154
}

0 commit comments

Comments
 (0)