1
0
mirror of https://github.com/danog/amp.git synced 2025-01-22 21:31:18 +01:00

Fix future completing then cancelling join

If both callbacks are enqueued they may be both invoked, so only throw to the suspension if the promise isn't resolved.
This commit is contained in:
Aaron Piotrowski 2021-09-16 20:54:19 -05:00
parent e0fe64c495
commit a7f31d1b98
No known key found for this signature in database
GPG Key ID: ADD1EF783EDE9EEB
2 changed files with 20 additions and 1 deletions

View File

@ -114,7 +114,9 @@ final class Future
$cancellationId = $token?->subscribe(function (\Throwable $reason) use (&$callbackId, $suspension): void {
$this->state->unsubscribe($callbackId);
$suspension->throw($reason);
if (!$this->state->isComplete()) { // Resume has already been scheduled if complete.
$suspension->throw($reason);
}
});
$callbackId = $this->state->subscribe(static function (?\Throwable $error, mixed $value) use (

View File

@ -2,6 +2,7 @@
namespace Amp\Future;
use Amp\CancellationTokenSource;
use Amp\CancelledException;
use Amp\Deferred;
use Amp\Future;
@ -9,6 +10,7 @@ use Amp\TimeoutCancellationToken;
use PHPUnit\Framework\TestCase;
use Revolt\EventLoop\Loop;
use function Amp\Future\spawn;
use function Revolt\EventLoop\defer;
use function Revolt\EventLoop\delay;
class FutureTest extends TestCase
@ -140,6 +142,21 @@ class FutureTest extends TestCase
self::assertTrue($future->join($token));
}
public function testCompleteThenCancelJoin(): void
{
$deferred = new Deferred;
$source = new CancellationTokenSource;
$future = $deferred->getFuture();
defer(function () use ($future, $source): void {
self::assertSame(1, $future->join($source->getToken()));
});
$deferred->complete(1);
$source->cancel();
delay(0.01); // Tick the event loop to enter defer callback.
}
/**
* @template T