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
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:
isCancelled(): Returnstrueif cancellation was triggered.reason(): Returns the string reason given whencancel()was called.throwIfCancelled(): Throws aCancelledExceptionif the token is cancelled.
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):
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:
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
// 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:
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');