fix: idempotent HistorianSession.DisposeAsync + upfront cancellation check

This commit is contained in:
Joseph Doherty
2026-06-25 03:05:10 -04:00
parent ad2c02eb42
commit d2b00fcda5
@@ -13,6 +13,7 @@ public sealed class HistorianSession : IAsyncDisposable
private readonly HistorianGrpcConnection _connection; private readonly HistorianGrpcConnection _connection;
private readonly HistorianGrpcHandshake.Session _session; private readonly HistorianGrpcHandshake.Session _session;
private readonly HistorianClientOptions _options; private readonly HistorianClientOptions _options;
private int _disposed;
/// <summary>Whether this session was opened read-only or write-enabled (the Open2 connection mode).</summary> /// <summary>Whether this session was opened read-only or write-enabled (the Open2 connection mode).</summary>
public HistorianSessionKind Kind { get; } public HistorianSessionKind Kind { get; }
@@ -41,6 +42,7 @@ public sealed class HistorianSession : IAsyncDisposable
int maxValues, int maxValues,
[EnumeratorCancellation] CancellationToken ct = default) [EnumeratorCancellation] CancellationToken ct = default)
{ {
ct.ThrowIfCancellationRequested();
var orch = new HistorianGrpcReadOrchestrator(_options); var orch = new HistorianGrpcReadOrchestrator(_options);
IReadOnlyList<HistorianSample> rows = await Task.Run( IReadOnlyList<HistorianSample> rows = await Task.Run(
() => orch.RunRawQueryOnSession(_connection, _session.ClientHandle, tag, startUtc, endUtc, maxValues, ct), ct) () => orch.RunRawQueryOnSession(_connection, _session.ClientHandle, tag, startUtc, endUtc, maxValues, ct), ct)
@@ -63,6 +65,7 @@ public sealed class HistorianSession : IAsyncDisposable
TimeSpan interval, TimeSpan interval,
[EnumeratorCancellation] CancellationToken ct = default) [EnumeratorCancellation] CancellationToken ct = default)
{ {
ct.ThrowIfCancellationRequested();
var orch = new HistorianGrpcReadOrchestrator(_options); var orch = new HistorianGrpcReadOrchestrator(_options);
IReadOnlyList<HistorianAggregateSample> rows = await Task.Run( IReadOnlyList<HistorianAggregateSample> rows = await Task.Run(
() => orch.RunAggregateQueryOnSession(_connection, _session.ClientHandle, tag, startUtc, endUtc, mode, interval, ct), ct) () => orch.RunAggregateQueryOnSession(_connection, _session.ClientHandle, tag, startUtc, endUtc, mode, interval, ct), ct)
@@ -180,7 +183,10 @@ public sealed class HistorianSession : IAsyncDisposable
/// also idle-expires on its own; this releases the local channel resources immediately.</summary> /// also idle-expires on its own; this releases the local channel resources immediately.</summary>
public ValueTask DisposeAsync() public ValueTask DisposeAsync()
{ {
_connection.Dispose(); if (Interlocked.Exchange(ref _disposed, 1) == 0)
{
_connection.Dispose();
}
return ValueTask.CompletedTask; return ValueTask.CompletedTask;
} }
} }