Skip to content

Commit 015470f

Browse files
committed
Clean up canceller function references when they are no longer needed
1 parent e521e57 commit 015470f

4 files changed

Lines changed: 52 additions & 0 deletions

File tree

src/Deferred.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public function promise()
2323
$this->rejectCallback = $reject;
2424
$this->notifyCallback = $notify;
2525
}, $this->canceller);
26+
$this->canceller = null;
2627
}
2728

2829
return $this->promise;

src/Promise.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,8 @@ private function settle(ExtendedPromiseInterface $promise)
202202

203203
if ($promise instanceof self) {
204204
$promise->requiredCancelRequests++;
205+
} else {
206+
$this->canceller = null;
205207
}
206208

207209
$handlers = $this->handlers;

tests/DeferredTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,41 @@ public function progressIsAnAliasForNotify()
3939

4040
$deferred->progress($sentinel);
4141
}
42+
43+
/** @test */
44+
public function shouldRejectWithoutCreatingGarbageCyclesIfCancellerRejectsWithException()
45+
{
46+
gc_collect_cycles();
47+
$deferred = new Deferred(function ($resolve, $reject) {
48+
$reject(new \Exception('foo'));
49+
});
50+
$deferred->promise()->cancel();
51+
unset($deferred);
52+
53+
$this->assertSame(0, gc_collect_cycles());
54+
}
55+
56+
/** @test */
57+
public function shouldRejectWithoutCreatingGarbageCyclesIfParentCancellerRejectsWithException()
58+
{
59+
gc_collect_cycles();
60+
$deferred = new Deferred(function ($resolve, $reject) {
61+
$reject(new \Exception('foo'));
62+
});
63+
$deferred->promise()->then()->cancel();
64+
unset($deferred);
65+
66+
$this->assertSame(0, gc_collect_cycles());
67+
}
68+
69+
/** @test */
70+
public function shouldRejectWithoutCreatingGarbageCyclesIfCancellerHoldsReferenceAndExplicitlyRejectWithException()
71+
{
72+
gc_collect_cycles();
73+
$deferred = new Deferred(function () use (&$deferred) { });
74+
$deferred->reject(new \Exception('foo'));
75+
unset($deferred);
76+
77+
$this->assertSame(0, gc_collect_cycles());
78+
}
4279
}

tests/PromiseTest.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,19 @@ public function shouldRejectWithoutCreatingGarbageCyclesIfResolverWithReferenceT
160160
$promise = new Promise(function () use (&$promise) {
161161
throw new \Exception('foo');
162162
});
163+
unset($promise);
164+
165+
$this->assertSame(0, gc_collect_cycles());
166+
}
167+
168+
/** @test */
169+
public function shouldRejectWithoutCreatingGarbageCyclesIfCancellerHoldsReferenceAndResolverThrowsException()
170+
{
171+
gc_collect_cycles();
163172

173+
$promise = new Promise(function () {
174+
throw new \Exception('foo');
175+
}, function () use (&$promise) { });
164176
unset($promise);
165177

166178
$this->assertSame(0, gc_collect_cycles());

0 commit comments

Comments
 (0)