ProtocolActor Class¶
Default IProtocolActor: one mailbox (System.Collections.Concurrent.ConcurrentQueue<> of work items), one loop, one sorted list of pending timers — both owned exclusively by whichever thread is currently running the loop, so neither needs its own lock (FR-RAW-020/021). The loop blocks on a SemaphoreSlim for either new mailbox work or the next timer deadline, whichever comes first, and never polls (FR-RAW-022). Any exception from a posted work item or a fired timer is caught and raised via BackgroundExceptionOccurred; the loop keeps running afterward (FR-RAW-023).
Inheritance Object → ProtocolActor
Implements IProtocolActor, IDisposable
Constructors¶
ProtocolActor(ActorExecutionMode, SynchronizationContext) Constructor¶
Creates an actor and immediately starts its mailbox loop under mode.
public ProtocolActor(CanKit.Pro.Actor.ActorExecutionMode mode=CanKit.Pro.Actor.ActorExecutionMode.DedicatedThread, System.Threading.SynchronizationContext? synchronizationContext=null);
Parameters¶
mode ActorExecutionMode
Execution context for the loop (FR-RAW-024). Defaults to a dedicated thread.
synchronizationContext SynchronizationContext
Required when mode is SynchronizationContext; must be null for every other mode.
ProtocolActor(ActorExecutionMode, SynchronizationContext, ActorShutdownPolicy) Constructor¶
Creates an actor with an explicitly selected shutdown policy. The original two-parameter constructor continues to use DrainDueTimers.
public ProtocolActor(CanKit.Pro.Actor.ActorExecutionMode mode, System.Threading.SynchronizationContext? synchronizationContext, CanKit.Pro.Actor.ActorShutdownPolicy shutdownPolicy);
Parameters¶
mode ActorExecutionMode
Execution context for the loop (FR-RAW-024).
synchronizationContext SynchronizationContext
Required for SynchronizationContext mode; null otherwise.
shutdownPolicy ActorShutdownPolicy
Whether pending due timers are executed or discarded at shutdown.
Exceptions¶
ArgumentOutOfRangeException
The shutdown policy is not defined.
Properties¶
ProtocolActor.IsOnCurrentActor Property¶
True when <i>the calling thread</i> is currently executing a work item or timer callback belonging to this actor, false otherwise.
Property Value¶
Remarks¶
Lets a public sync API safely detect that it is already on the actor loop and run the requested work inline instead of routing it through PostAsync(Action, CancellationToken) and synchronously waiting on the returned task — which would deadlock the loop against itself. External callers still take the marshal-through-mailbox path exactly as before.
Thread-scoped, deliberately: a Task started from inside a callback runs on
a different thread and reports false, because it genuinely is not on the actor
and must not mutate actor state inline. (Backing this with an
System.Threading.AsyncLocal<> instead would flow the flag into every such task through the
ExecutionContext and report true there — the bug this property is
most likely to be trusted with preventing.) In
SynchronizationContext mode the flag is set on whichever
thread the context actually runs the callback on, so it stays correct there too.
Methods¶
ProtocolActor.Dispose() Method¶
Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
Implements Dispose()
ProtocolActor.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).
Parameters¶
work Action
Implements Post(Action)
ProtocolActor.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.
public 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.
Implements PostAsync(Action, CancellationToken)
Returns¶
ProtocolActor.PostAsync<T>(Func<T>, CancellationToken) Method¶
Same as PostAsync(Action, CancellationToken) but returns work's result.
public System.Threading.Tasks.Task<T> PostAsync<T>(System.Func<T> work, System.Threading.CancellationToken cancellationToken=default(System.Threading.CancellationToken));
Type parameters¶
T
Parameters¶
The work to run on the mailbox loop.
cancellationToken CancellationToken
Withdraws the work while it is still queued.
Implements PostAsync<T>(Func<T>, CancellationToken)
Returns¶
ProtocolActor.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.
Parameters¶
delay TimeSpan
callback Action
Implements Schedule(TimeSpan, Action)
Returns¶
Events¶
ProtocolActor.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.
Implements BackgroundExceptionOccurred