PDComm.HsmsWorker
Class
Runs an HsmsEndpoint on a dedicated background thread and exposes it through asynchronous methods.
public sealed class HsmsWorker : IAsyncDisposableThe worker thread creates and owns the endpoint and performs every native call: it polls the endpoint every HsmsWorkerOptions.PollIntervalMilliseconds or sooner when work arrives, runs queued commands in order, and buffers events for HsmsWorker.ReadEventsAsync. All members are thread-safe.
Both queues are bounded. A command that does not fit in HsmsWorkerOptions.CommandCapacity or HsmsWorkerOptions.MaxCommandBytes fails immediately with PdcommStatus.Backpressure instead of waiting. When the event buffer is full, the worker stops reading native events until you consume some, and commands that send messages fail with PdcommStatus.Backpressure until then.
Read events continuously and dispose each one. To shut down gracefully, call HsmsWorker.StopAsync, drain HsmsWorker.ReadEventsAsync and await HsmsWorker.Completion; then dispose the worker.
await using HsmsWorker worker = await HsmsWorker.StartAsync(
address, endpointOptions, workerOptions, deadline.Token);
HsmsToken request = default;
await using IAsyncEnumerator<HsmsEvent> events =
worker.ReadEventsAsync(deadline.Token).GetAsyncEnumerator();
while (await events.MoveNextAsync())
{
using HsmsEvent current = events.Current;
HsmsEventInfo info = current.GetInfo();
if (request == default && (await worker.GetStateAsync(deadline.Token)).State == HsmsConnectionState.Selected)
{
request = await worker.SubmitRequestAsync(1, 1, true, expectedBody, deadline.Token);
}
if (info.Kind == HsmsEventKind.Received && info.IsReply && info.Token == request)
{
break; // S1F2 received
}
}
await worker.StopAsync(deadline.Token);
while (await events.MoveNextAsync())
{
events.Current.Dispose();
}
await worker.Completion.WaitAsync(deadline.Token);Members
CancelAsync
Method
Cancels an open transaction on the worker thread.
public Task<bool> CancelAsync(HsmsToken token, CancellationToken cancellationToken = default)The command runs on the worker thread. The task faults with PdcommException (PdcommStatus.Backpressure) when the command queue or HsmsWorkerOptions.MaxCommandBytes is full, with ObjectDisposedException after the worker stopped accepting commands, and otherwise with the exception of HsmsEndpoint.Cancel. It is canceled when cancellationToken is canceled before the command runs.
Parameters
| Name | Description |
|---|---|
token | The token returned when the transaction started. |
cancellationToken | A token that cancels the command if it has not started running. |
Returns
A task whose result is true if the transaction was open and is now cancelled.
DeselectAsync
Method
Queues Deselect.req on the worker thread.
public Task<HsmsToken> DeselectAsync(CancellationToken cancellationToken = default)The command runs on the worker thread. The task faults with PdcommException (PdcommStatus.Backpressure) when the command queue or HsmsWorkerOptions.MaxCommandBytes is full or event output is blocked, with ObjectDisposedException after the worker stopped accepting commands, and otherwise with the exception of HsmsEndpoint.Deselect. It is canceled when cancellationToken is canceled before the command runs.
Parameters
| Name | Description |
|---|---|
cancellationToken | A token that cancels the command if it has not started running. |
Returns
A task whose result is the token of the control transaction.
DisposeAsync
Method
Stops the worker, closes the endpoint and waits for the worker thread to exit.
public ValueTask DisposeAsync()New commands are rejected with ObjectDisposedException, queued commands fail, and buffered events that were not read are disposed. The worker still stops the connection and waits up to HsmsWorkerOptions.ShutdownTimeoutMilliseconds for it to close.
If the worker ended with an error, for example a TimeoutException because the connection did not close in time, this method rethrows it after the thread has exited.
Returns
A task that completes when the worker thread has exited.
GetLocalAddressAsync
Method
Reads the endpoint's local socket address on the worker thread.
public Task<string> GetLocalAddressAsync(CancellationToken cancellationToken = default)The command runs on the worker thread. The task faults with PdcommException (PdcommStatus.Backpressure) when the command queue or HsmsWorkerOptions.MaxCommandBytes is full, with ObjectDisposedException after the worker stopped accepting commands, and otherwise with the exception of HsmsEndpoint.GetLocalAddress. It is canceled when cancellationToken is canceled before the command runs.
Parameters
| Name | Description |
|---|---|
cancellationToken | A token that cancels the command if it has not started running. |
Returns
A task whose result is the numeric local socket address.
GetStateAsync
Method
Reads the endpoint's connection state on the worker thread.
public Task<HsmsState> GetStateAsync(CancellationToken cancellationToken = default)The command runs on the worker thread. The task faults with PdcommException (PdcommStatus.Backpressure) when the command queue or HsmsWorkerOptions.MaxCommandBytes is full, with ObjectDisposedException after the worker stopped accepting commands, and otherwise with the exception of HsmsEndpoint.GetState. It is canceled when cancellationToken is canceled before the command runs.
Parameters
| Name | Description |
|---|---|
cancellationToken | A token that cancels the command if it has not started running. |
Returns
A task whose result is a snapshot of the endpoint state.
IsPendingAsync
Method
Determines on the worker thread whether a transaction is still open.
public Task<bool> IsPendingAsync(HsmsToken token, CancellationToken cancellationToken = default)The command runs on the worker thread. The task faults with PdcommException (PdcommStatus.Backpressure) when the command queue or HsmsWorkerOptions.MaxCommandBytes is full, with ObjectDisposedException after the worker stopped accepting commands, and otherwise with the exception of HsmsEndpoint.IsPending. It is canceled when cancellationToken is canceled before the command runs.
Parameters
| Name | Description |
|---|---|
token | The token returned when the transaction started. |
cancellationToken | A token that cancels the command if it has not started running. |
Returns
A task whose result is true if the transaction has not completed yet.
LinktestAsync
Method
Queues Linktest.req on the worker thread.
public Task<HsmsToken> LinktestAsync(CancellationToken cancellationToken = default)The command runs on the worker thread. The task faults with PdcommException (PdcommStatus.Backpressure) when the command queue or HsmsWorkerOptions.MaxCommandBytes is full or event output is blocked, with ObjectDisposedException after the worker stopped accepting commands, and otherwise with the exception of HsmsEndpoint.Linktest. It is canceled when cancellationToken is canceled before the command runs.
Parameters
| Name | Description |
|---|---|
cancellationToken | A token that cancels the command if it has not started running. |
Returns
A task whose result is the token of the control transaction.
ReadEventsAsync
Method
Reads the events produced by the endpoint, in order.
public IAsyncEnumerable<HsmsEvent> ReadEventsAsync(CancellationToken cancellationToken = default)Several enumerators may run at once; each event is delivered to only one of them. Reading events frees space in the event buffer, which lets the worker continue polling.
Parameters
| Name | Description |
|---|---|
cancellationToken | A token that ends the enumeration with OperationCanceledException. |
Returns
An asynchronous sequence of events; the caller owns each event and must dispose it. The sequence ends when the worker exits, and throws the worker's terminal error if it failed.
ReplyAsync
Method
Queues the reply to a received primary message on the worker thread.
public Task ReplyAsync(HsmsEvent primary, byte stream, byte function, bool replyExpected, ReadOnlyMemory<byte> body, CancellationToken cancellationToken = default)The command runs on the worker thread. The task faults with PdcommException (PdcommStatus.Backpressure) when the command queue or HsmsWorkerOptions.MaxCommandBytes is full or event output is blocked, with ObjectDisposedException after the worker stopped accepting commands, and otherwise with the exception of HsmsEndpoint.Reply. It is canceled when cancellationToken is canceled before the command runs. primary stays valid until the command finishes, even if you dispose it meanwhile.
Parameters
| Name | Description |
|---|---|
primary | The received primary event that expects a reply. |
stream | The reply's stream number, normally the primary's stream. |
function | The reply's function number, normally the primary's function plus one, or 0 to abort. |
replyExpected | Whether to set the W bit on the reply; normally false. |
body | One complete encoded SECS-II item, or empty for a header-only reply. It is copied. |
cancellationToken | A token that cancels the command if it has not started running. |
Returns
A task that completes when the reply has been queued on the endpoint.
Notes
- Throws
ArgumentNullException:primaryisnull. - Throws
OperationCanceledException:cancellationTokenis already canceled. - Throws
ObjectDisposedException:primaryhas been disposed. - Throws
PdcommException:bodyis larger thanHsmsOptions.MaxFrameBytesminus 14 or thanSecsLimits.MessageBytes(PdcommStatus.LimitExceeded).
SelectAsync
Method
Queues Select.req on the worker thread.
public Task<HsmsToken> SelectAsync(CancellationToken cancellationToken = default)The command runs on the worker thread. The task faults with PdcommException (PdcommStatus.Backpressure) when the command queue or HsmsWorkerOptions.MaxCommandBytes is full or event output is blocked, with ObjectDisposedException after the worker stopped accepting commands, and otherwise with the exception of HsmsEndpoint.Select. It is canceled when cancellationToken is canceled before the command runs.
Parameters
| Name | Description |
|---|---|
cancellationToken | A token that cancels the command if it has not started running. |
Returns
A task whose result is the token of the control transaction.
StartAsync
Method
Creates an HSMS endpoint on a new worker thread and starts it.
public static Task<HsmsWorker> StartAsync(string address, HsmsOptions options, HsmsWorkerOptions workerOptions, CancellationToken cancellationToken = default)Because this is an asynchronous method, every exception is reported through the returned task.
Parameters
| Name | Description |
|---|---|
address | The numeric socket address to connect to (active) or listen on (passive); see HsmsEndpoint.Create. |
options | The endpoint's role, session, timers and limits. |
workerOptions | The worker's queue capacities and timing. |
cancellationToken | A token that abandons the startup; the worker is then disposed. |
Returns
A task whose result is the running worker, which the caller owns and must dispose.
Notes
- Throws
ArgumentNullException:address,optionsorworkerOptionsisnull. - Throws
ArgumentOutOfRangeException:workerOptionshas a nonpositive capacity or interval, a negative byte budget, or aHsmsWorkerOptions.MaxEventBytessmaller than one frame body. - Throws
OperationCanceledException:cancellationTokenwas canceled during startup. - Throws
PdcommException: The endpoint could not be created or started; seeHsmsEndpoint.Create.
StopAsync
Method
Begins a graceful shutdown of the connection and of the worker.
public Task StopAsync(CancellationToken cancellationToken = default)After the stop, the worker keeps polling until the connection is closed, the native events are drained and the queued commands have run, then exits and completes HsmsWorker.Completion. Keep reading events meanwhile. If this does not happen within HsmsWorkerOptions.ShutdownTimeoutMilliseconds, HsmsWorker.Completion faults with TimeoutException. Calling this method again after a stop has no further effect.
Parameters
| Name | Description |
|---|---|
cancellationToken | A token that cancels the command if it has not started running. |
Returns
A task that completes when the stop has been issued, not when the connection has closed.
SubmitRequestAsync
Method
Queues a primary data message on the worker thread.
public Task<HsmsToken> SubmitRequestAsync(byte stream, byte function, bool replyExpected, ReadOnlyMemory<byte> body, CancellationToken cancellationToken = default)The command runs on the worker thread. The task faults with PdcommException (PdcommStatus.Backpressure) when the command queue or HsmsWorkerOptions.MaxCommandBytes is full or event output is blocked, with ObjectDisposedException after the worker stopped accepting commands, and otherwise with the exception of HsmsEndpoint.Request. It is canceled when cancellationToken is canceled before the command runs.
Parameters
| Name | Description |
|---|---|
stream | The SECS-II stream number (0 to 127). |
function | The SECS-II function number; primary messages use odd numbers. |
replyExpected | Whether to set the W bit and wait for a reply within T3. |
body | One complete encoded SECS-II item, or empty for a header-only message. It is copied. |
cancellationToken | A token that cancels the command if it has not started running. |
Returns
A task whose result is the token of the transaction. The reply or failure arrives later through HsmsWorker.ReadEventsAsync with the same token.
Notes
- Throws
PdcommException:bodyis larger thanHsmsOptions.MaxFrameBytesminus 14 or thanSecsLimits.MessageBytes(PdcommStatus.LimitExceeded).
Completion
Property
Gets a task that completes when the worker thread has exited and released the endpoint.
public Task Completion { get; }The task completes successfully after a stop has closed the connection, drained the native events and run every queued command. It faults with the error that ended the worker, for example a TimeoutException when the endpoint could not close within HsmsWorkerOptions.ShutdownTimeoutMilliseconds.
LocalAddress
Property
Gets the endpoint's local socket address while it is listening or connected, or null otherwise.
public string? LocalAddress { get; }A passive worker publishes its listening address before HsmsWorker.StartAsync completes, so you can read the port chosen for port 0. The value is refreshed on every poll.