Skip to content
Start free evaluation

PDComm.HsmsWorker

Class

Runs an HsmsEndpoint on a dedicated background thread and exposes it through asynchronous methods.

public sealed class HsmsWorker : IAsyncDisposable

The 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

NameDescription
tokenThe token returned when the transaction started.
cancellationTokenA 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

NameDescription
cancellationTokenA 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

NameDescription
cancellationTokenA 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

NameDescription
cancellationTokenA 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

NameDescription
tokenThe token returned when the transaction started.
cancellationTokenA 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

NameDescription
cancellationTokenA 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

NameDescription
cancellationTokenA 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

NameDescription
primaryThe received primary event that expects a reply.
streamThe reply's stream number, normally the primary's stream.
functionThe reply's function number, normally the primary's function plus one, or 0 to abort.
replyExpectedWhether to set the W bit on the reply; normally false.
bodyOne complete encoded SECS-II item, or empty for a header-only reply. It is copied.
cancellationTokenA 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: primary is null.
  • Throws OperationCanceledException: cancellationToken is already canceled.
  • Throws ObjectDisposedException: primary has been disposed.
  • Throws PdcommException: body is larger than HsmsOptions.MaxFrameBytes minus 14 or than SecsLimits.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

NameDescription
cancellationTokenA 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

NameDescription
addressThe numeric socket address to connect to (active) or listen on (passive); see HsmsEndpoint.Create.
optionsThe endpoint's role, session, timers and limits.
workerOptionsThe worker's queue capacities and timing.
cancellationTokenA 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, options or workerOptions is null.
  • Throws ArgumentOutOfRangeException: workerOptions has a nonpositive capacity or interval, a negative byte budget, or a HsmsWorkerOptions.MaxEventBytes smaller than one frame body.
  • Throws OperationCanceledException: cancellationToken was canceled during startup.
  • Throws PdcommException: The endpoint could not be created or started; see HsmsEndpoint.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

NameDescription
cancellationTokenA 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

NameDescription
streamThe SECS-II stream number (0 to 127).
functionThe SECS-II function number; primary messages use odd numbers.
replyExpectedWhether to set the W bit and wait for a reply within T3.
bodyOne complete encoded SECS-II item, or empty for a header-only message. It is copied.
cancellationTokenA 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: body is larger than HsmsOptions.MaxFrameBytes minus 14 or than SecsLimits.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.