Menu

Reference / Async

Context

finalclass
final class Context

The ambient view of the scope the calling task is in: cancellation, the effective deadline, and scoped values.

These are static methods that answer for the calling task, so a helper deep in the call stack can check for cancellation or read the request id without being passed anything. Outside async() there is no scope: isCancelled() is false, deadline(), remaining() and value() return null.

Context::withValue('request-id', $id, function () {
    spawn(fn() => log(Context::value('request-id')));
});

Example

use Async\Context;
use function Async\async;
use function Async\awaitAll;
use function Async\spawn;
use function Async\timeout;

async(function () {
    // Values are scoped: every task spawned inside sees them, nothing outside does.
    Context::withValue('request-id', 'r-42', function () {
        $t = spawn(fn() => Context::value('request-id'));
        echo "inside: ", $t->await(), "\n";
    });
    var_dump(Context::value('request-id'));

    // No deadline means unbounded.
    var_dump(Context::remaining());

    // A timeout() scope publishes its deadline.
    timeout(5.0, function () {
        $left = Context::remaining();
        echo "deadline set: ", ($left !== null && $left > 0.0 && $left <= 5.0) ? "yes" : "no", "\n";
        echo "cancelled: ", Context::isCancelled() ? "yes" : "no", "\n";
    });
});

Output

inside: r-42
NULL
NULL
deadline set: yes
cancelled: no

See also: CancellationToken, timeout

Methods

currentScope()The innermost scope open in the calling task.
token()The cancellation handle of the scope the calling task is in.
isCancelled()Whether the calling task's scope, or any ancestor, has been cancelled.
throwIfCancelled()Cancellation checkpoint: throw if the calling task's scope was cancelled.
deadline()The effective deadline of the calling task as a unix time.
remaining()Seconds left before the effective deadline, never negative.
value()A value bound by an enclosing withValue() scope.
withValue()Bind $key to $value for the duration of $body.

currentScope()#source

static function currentScope(): ?TaskGroup

The innermost scope open in the calling task.

Safe to call anywhere: it never builds the engine.

Returns

?TaskGroup — the scope, or null outside async()

token()#source

static function token(): CancellationToken

The cancellation handle of the scope the calling task is in.

Returns

CancellationToken — a read-only view of the current scope

Throws

\LogicException
outside async(), where there is no scope

See also: TaskGroup::token

isCancelled()#source

static function isCancelled(): bool

Whether the calling task's scope, or any ancestor, has been cancelled.

Returns

bool — true once cancellation was requested; false outside async()

throwIfCancelled()#source

static function throwIfCancelled(): void

Cancellation checkpoint: throw if the calling task's scope was cancelled.

Call it at loop heads in long CPU-bound tasks, which never reach a suspend point where cancellation would otherwise be delivered.

Throws

CancelledException
when the scope was cancelled

deadline()#source

static function deadline(): ?float

The effective deadline of the calling task as a unix time.

The earliest deadline of the enclosing timeout() scopes; a nested scope can only tighten it.

Returns

?float — unix timestamp, or null when there is no deadline

See also: timeout

remaining()#source

static function remaining(): ?float

Seconds left before the effective deadline, never negative.

Returns

?float — seconds remaining, or null when unbounded

Example

use Async\Context;
use function Async\async;
use function Async\awaitAll;
use function Async\spawn;
use function Async\timeout;

async(function () {
    // Values are scoped: every task spawned inside sees them, nothing outside does.
    Context::withValue('request-id', 'r-42', function () {
        $t = spawn(fn() => Context::value('request-id'));
        echo "inside: ", $t->await(), "\n";
    });
    var_dump(Context::value('request-id'));

    // No deadline means unbounded.
    var_dump(Context::remaining());

    // A timeout() scope publishes its deadline.
    timeout(5.0, function () {
        $left = Context::remaining();
        echo "deadline set: ", ($left !== null && $left > 0.0 && $left <= 5.0) ? "yes" : "no", "\n";
        echo "cancelled: ", Context::isCancelled() ? "yes" : "no", "\n";
    });
});

Output

inside: r-42
NULL
NULL
deadline set: yes
cancelled: no

value()#source

static function value(string $key): mixed

A value bound by an enclosing withValue() scope.

The request-id or correlation-id carrier: every task spawned inside the binding scope sees the value, nothing outside does. An inner binding shadows an outer one.

Parameters

$key string
the name the value was bound under

Returns

mixed — the bound value, or null when unbound

Example

use Async\Context;
use function Async\async;
use function Async\awaitAll;
use function Async\spawn;
use function Async\timeout;

async(function () {
    // Values are scoped: every task spawned inside sees them, nothing outside does.
    Context::withValue('request-id', 'r-42', function () {
        $t = spawn(fn() => Context::value('request-id'));
        echo "inside: ", $t->await(), "\n";
    });
    var_dump(Context::value('request-id'));

    // No deadline means unbounded.
    var_dump(Context::remaining());

    // A timeout() scope publishes its deadline.
    timeout(5.0, function () {
        $left = Context::remaining();
        echo "deadline set: ", ($left !== null && $left > 0.0 && $left <= 5.0) ? "yes" : "no", "\n";
        echo "cancelled: ", Context::isCancelled() ? "yes" : "no", "\n";
    });
});

Output

inside: r-42
NULL
NULL
deadline set: yes
cancelled: no

See also: withValue

withValue()#source

static function withValue(string $key, mixed $value, callable $body): mixed

Bind $key to $value for the duration of $body.

$body runs in a scope of its own, so it is joined like any other scope: every task it spawns finishes before withValue() returns.

Parameters

$key string
the name to bind
$value mixed
the value to bind
$body callable
called with the new TaskGroup

Returns

mixed — the value returned by $body

Example

use Async\Context;
use function Async\async;
use function Async\awaitAll;
use function Async\spawn;
use function Async\timeout;

async(function () {
    // Values are scoped: every task spawned inside sees them, nothing outside does.
    Context::withValue('request-id', 'r-42', function () {
        $t = spawn(fn() => Context::value('request-id'));
        echo "inside: ", $t->await(), "\n";
    });
    var_dump(Context::value('request-id'));

    // No deadline means unbounded.
    var_dump(Context::remaining());

    // A timeout() scope publishes its deadline.
    timeout(5.0, function () {
        $left = Context::remaining();
        echo "deadline set: ", ($left !== null && $left > 0.0 && $left <= 5.0) ? "yes" : "no", "\n";
        echo "cancelled: ", Context::isCancelled() ? "yes" : "no", "\n";
    });
});

Output

inside: r-42
NULL
NULL
deadline set: yes
cancelled: no

See also: value

View source