Skip to content

Cancellation & Deadline ​

In persistent, asynchronous applications, operations might need to be terminated early because a client disconnected, a request exceeded its execution budget, or the system is undergoing a graceful shutdown. Nexphant implements a cooperative cancellation mechanism using CancellationToken, CancellationSource, and Deadline.

Cancellation Tokens and Sources ​

Cancellation is managed via a Cancellation Source, which creates and cancels an associated Cancellation Token.

  • CancellationSource: The trigger mechanism used to cancel operations from the outside.
  • CancellationToken: The passive token passed to asynchronous tasks. It allows checking if a cancellation has been requested.

Basic Usage ​

php
use Nexphant\Runtime\Runtime;

// Create a cancellation source (or use new CancellationSource())
$source = Runtime::cancel();
$token = $source->token();

// Pass the token to an asynchronous operation
Runtime::spawn(function () use ($token) {
    while (true) {
        // Cooperatively check if cancellation was requested
        $token->throwIfCancelled();
        
        // Yield to the event loop
        Runtime::sleep(0.5);
    }
});

// Cancel the operation after 3 seconds
Runtime::sleep(3.0);
$source->cancel('Operation took too long');

Cooperative Cancellation Check ​

Nexphant utilizes cooperative (not preemptive) cancellation. An operation is not abruptly killed by the OS; instead, it must regularly check the token and exit gracefully.

You have three main methods on CancellationToken:

  1. isCancelled(): Returns true if cancellation was triggered.
  2. reason(): Returns the string reason given when cancel() was called.
  3. throwIfCancelled(): Throws a CancelledException if the token is cancelled.
php
use Nexphant\Core\Cancellation\CancellationToken;

function processItems(array $items, CancellationToken $token): void
{
    foreach ($items as $item) {
        // Check for cancellation
        $token->throwIfCancelled();
        
        // Process item...
    }
}

Cancellation Callbacks ​

You can register a callback to be run automatically when a token is cancelled (e.g., to close socket connections or abort HTTP requests):

php
use Nexphant\Core\Cancellation\CancellationToken;

$token->onCancel(function (string $reason) use ($socket) {
    // This callback runs immediately when cancelled
    fclose($socket);
});

Deadlines ​

A Deadline represents a fixed point in time when an operation must complete.

Creating a Deadline ​

You can initialize deadlines relative to the current time, or at an absolute unix timestamp:

php
use Nexphant\Runtime\Runtime;
use Nexphant\Core\Cancellation\Deadline;

// 5 seconds from now
$deadline = Runtime::deadline(5.0); // or Deadline::fromSeconds(5.0)

// 250 milliseconds from now
$deadline = Deadline::fromMilliseconds(250);

// Absolute timestamp
$deadline = Deadline::at(microtime(true) + 10.0);

Checking Deadlines ​

php
// Check if the deadline has passed
if ($deadline->expired()) {
    throw new DeadlineExceededException();
}

// Check remaining duration
$ms = $deadline->remainingMs();
$seconds = $deadline->remainingSeconds();

// Throw an exception immediately if expired
$deadline->throwIfExpired();

Hierarchical Tokens ​

You can chain cancellation tokens together by passing a parent token to a new CancellationToken constructor. If the parent token is cancelled, the child token automatically inherits the cancellation:

php
use Nexphant\Core\Cancellation\CancellationToken;
use Nexphant\Core\Cancellation\CancellationSource;

$parentSource = new CancellationSource();
$parentToken = $parentSource->token();

// Create a child token linked to the parent
$childToken = new CancellationToken($parentToken);

// Cancelling the parent cancels the child automatically
$parentSource->cancel('parent_terminated');

assert($childToken->isCancelled() === true); // true
assert($childToken->reason() === 'parent_cancelled');

Released under the MIT License.