Skip to content

Commit fbdf0aa

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

File tree

4 files changed

+55
-2
lines changed

4 files changed

+55
-2
lines changed

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: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,6 @@ public function shouldRejectWithoutCreatingGarbageCyclesIfResolverThrowsExceptio
138138
public function shouldRejectWithoutCreatingGarbageCyclesIfCancellerWithReferenceThrowsException()
139139
{
140140
gc_collect_cycles();
141-
142141
$promise = new Promise(function () {}, function () use (&$promise) {
143142
throw new \Exception('foo');
144143
});
@@ -156,11 +155,25 @@ public function shouldRejectWithoutCreatingGarbageCyclesIfCancellerWithReference
156155
public function shouldRejectWithoutCreatingGarbageCyclesIfResolverWithReferenceThrowsException()
157156
{
158157
gc_collect_cycles();
159-
160158
$promise = new Promise(function () use (&$promise) {
161159
throw new \Exception('foo');
162160
});
161+
unset($promise);
162+
163+
$this->assertSame(0, gc_collect_cycles());
164+
}
163165

166+
/**
167+
* @test
168+
* @requires PHP 7
169+
* @see self::shouldRejectWithoutCreatingGarbageCyclesIfCancellerWithReferenceThrowsException
170+
*/
171+
public function shouldRejectWithoutCreatingGarbageCyclesIfCancellerHoldsReferenceAndResolverThrowsException()
172+
{
173+
gc_collect_cycles();
174+
$promise = new Promise(function () {
175+
throw new \Exception('foo');
176+
}, function () use (&$promise) { });
164177
unset($promise);
165178

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

0 commit comments

Comments
 (0)