Skip to content

IProtocolActor Interface

A protocol instance's documented threading model (arc42 §8.3, ADR-6; FR-RAW-020..024): exactly one mailbox, processed by exactly one logical loop, so a protocol instance's internal state (channel registers, timer queues, state machines) is never mutated from more than one place at a time — no locks needed by the protocol code itself (FR-RAW-020/021). Scheduling is event-driven, never a busy loop (FR-RAW-022); background exceptions are surfaced via BackgroundExceptionOccurred instead of being thrown on some unrelated caller thread or lost as an unobserved task exception (FR-RAW-023).

public interface IProtocolActor : System.IDisposable

Derived
↳ ProtocolActor

Implements IDisposable

Methods

IProtocolActor.Post(Action) Method

Enqueues work to run on the actor's mailbox loop and returns immediately ("tell" / fire-and-forget). If work throws, the exception is caught by the loop and surfaced via BackgroundExceptionOccurred — there is no other way for a fire-and-forget caller to observe it (FR-RAW-023).

void Post(System.Action work);

Parameters

work Action

IProtocolActor.PostAsync(Action, CancellationToken) Method

Enqueues work to run on the actor's mailbox loop and returns a task that completes once it has run ("ask"). Unlike Post(Action), an exception from work is surfaced through the returned task's fault, not through BackgroundExceptionOccurred — the caller is already positioned to observe it by awaiting.

cancellationToken withdraws work for as long as it has not started: a token cancelled before the call, or while the item waits in the mailbox, cancels the returned task at once and the work never runs. Work that has already started is never interrupted — it runs to completion, and cancelling then has no effect on the task's outcome, so the mailbox's single-writer discipline (FR-RAW-021) is not weakened by a half-finished work item.

System.Threading.Tasks.Task PostAsync(System.Action work, System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken));

Parameters

work Action

The work to run on the mailbox loop.

cancellationToken CancellationToken

Withdraws the work while it is still queued.

Returns

Task

IProtocolActor.PostAsync<T>(Func<T>, CancellationToken) Method

Same as PostAsync(Action, CancellationToken) but returns work's result.

System.Threading.Tasks.Task<T> PostAsync<T>(System.Func<T> work, System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken));

Type parameters

T

Parameters

work Func<T>

The work to run on the mailbox loop.

cancellationToken CancellationToken

Withdraws the work while it is still queued.

Returns

Task<T>

IProtocolActor.Schedule(TimeSpan, Action) Method

Schedules callback to run on the actor's mailbox loop once delay has elapsed, using event-driven waiting rather than polling (FR-RAW-022) — suitable for STmin waits, timeout checks, and similar periodic/timed protocol tasks. Disposing the returned handle cancels the callback on a best-effort basis: it will not fire if cancellation is observed before it becomes due, but a callback already in flight on the loop may still complete.

System.IDisposable Schedule(System.TimeSpan delay, System.Action callback);

Parameters

delay TimeSpan

callback Action

Returns

IDisposable

Events

IProtocolActor.BackgroundExceptionOccurred Event

Raised whenever a posted work item (via Post(Action)) or a scheduled callback (via Schedule(TimeSpan, Action)) throws — the actor's single, defined channel for background exceptions (FR-RAW-023). The mailbox loop keeps running afterward; one failing item never stops the actor. Never raised for PostAsync(Action, CancellationToken)/ PostAsync<T>(Func<T>, CancellationToken) failures, which surface through their own returned task instead.

Those reports are raised on the loop, as part of the callback that failed, so a subscriber may touch the protocol instance's single-writer state there. A report about the actor's own shutdown is the exception: ProtocolActor reports a IDisposable.Dispose that gave up waiting for the loop (TimeoutException) on the thread that called Dispose, while the loop is still inside the callback that would not finish. In the task-based execution modes it also reports a loop that failed outside every callback: on that thread too, or, when the loop fails only after Dispose gave up on it, on the thread the loop ended on, after its callbacks. A subscriber that mutates loop-owned state must not do so from those reports.

event EventHandler<Exception> BackgroundExceptionOccurred;

Event Type

EventHandler<Exception>