Menu

Reference / Async

CancellationToken

finalclass
final class CancellationToken

The read-only half of cancellation.

A token can be handed to a helper, a library or a long loop so that it can observe cancellation without also getting the power to cancel. The write half is the scope itself, TaskGroup::cancel(). A token is a view of a TaskGroup, so there is no second cancellation state to keep in sync with the scope tree. Inside a task, Context::token() gives the token of the scope it is in.

group(function (TaskGroup $g) {
    $token = $g->token();
    $g->spawn(function () use ($token) {
        while (!$token->isCancelled()) { step(); }
    });
});

Example

use Async\CancelledException;
use Async\TaskGroup;
use function Async\async;
use function Async\delay;
use function Async\group;

async(function () {
    group(function (TaskGroup $g) {
        $token = $g->token();
        $token->onCancel(fn() => print("onCancel hook ran\n"));

        // A task that suspends is interrupted with CancelledException,
        // which extends \Error: catch (\Exception) cannot swallow it.
        $g->spawn(function () {
            try {
                delay(10.0);
            } catch (CancelledException $e) {
                echo "sleeper cancelled\n";
                throw $e;
            }
        });

        // A loop that never suspends polls the token instead.
        $g->spawn(function () use ($g, $token) {
            for ($step = 1; $step <= 5; $step++) {
                if ($step === 3) {
                    $g->cancel();   // the write half; $token is the read-only view
                }
                if ($token->isCancelled()) {
                    echo "loop stopped at step $step\n";
                    return;
                }
                echo "step $step\n";
            }
        });
    });
    echo "scope joined\n";
});

Output

step 1
step 2
onCancel hook ran
loop stopped at step 3
sleeper cancelled
scope joined

See also: TaskGroup::token, Context::token

Methods

__construct()Create a token that observes $scope; normally obtained from TaskGroup::token() or Context::token().
isCancelled()True once this scope, or any ancestor, was cancelled.
throwIfCancelled()Cancellation checkpoint: throw CancelledException if cancellation was requested.
deadline()The earliest deadline on this scope or an ancestor, as a unix time.
remaining()Seconds left before the deadline, never negative.
onCancel()Run $fn when this scope is cancelled.

__construct()#source

function __construct(private TaskGroup $scope)

Create a token that observes $scope; normally obtained from TaskGroup::token() or Context::token().

Parameters

$scope TaskGroup
the scope to observe

isCancelled()#source

function isCancelled(): bool

True once this scope, or any ancestor, was cancelled.

Returns

bool — whether cancellation was requested

throwIfCancelled()#source

function throwIfCancelled(): void

Cancellation checkpoint: throw CancelledException if cancellation was requested.

A task that suspends gets the exception delivered automatically; this is for a CPU-bound loop that never reaches a suspend point.

Throws

CancelledException
when the scope was cancelled

deadline()#source

function deadline(): ?float

The earliest deadline on this scope or an ancestor, as a unix time.

Returns

?float — unix timestamp, or null when no deadline applies

remaining()#source

function remaining(): ?float

Seconds left before the deadline, never negative.

Returns

?float — seconds remaining, or null when unbounded

onCancel()#source

function onCancel(callable $fn): void

Run $fn when this scope is cancelled.

The hook for releasing a resource the scheduler knows nothing about (a handle, a lock, a subprocess). It fires once, synchronously, inside cancel(); if the scope is already cancelled it runs immediately. A handler that throws does not unwind cancel(): the exception is recorded as the scope's failure.

Parameters

$fn callable
called with no arguments

View source