fix(abcip): keep a sync Dispose, but one that never blocks on the teardown
F10 follow-through: removing IDisposable outright broke a synchronous drv.Dispose() call site outside this lane (Runtime.Tests AbCipAlmaNativeAlarmEndToEndTests). The sync Dispose() is back, but it starts the same teardown as DisposeAsync and returns instead of parking the caller on DisposeAsync().AsTask().GetAwaiter().GetResult(); a background fault is logged. The runtime still takes the IAsyncDisposable path (preferred, never both). Refs #737 Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
This commit is contained in:
@@ -27,7 +27,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip;
|
||||
/// handles GC-collected tags.</para>
|
||||
/// </remarks>
|
||||
public sealed class AbCipDriver : IDriver, IReadable, IWritable, ITagDiscovery, IBatchSubscribable,
|
||||
IHostConnectivityProbe, IPerCallHostResolver, IAlarmSource, ISkippedTagReporter, IAsyncDisposable
|
||||
IHostConnectivityProbe, IPerCallHostResolver, IAlarmSource, ISkippedTagReporter, IAsyncDisposable, IDisposable
|
||||
{
|
||||
/// <summary>Authored tags this driver could not build into its poll table.</summary>
|
||||
private readonly SkippedTagLedger _skippedTags = new();
|
||||
@@ -2268,8 +2268,8 @@ public sealed class AbCipDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
||||
/// Asynchronously releases all resources used by the driver — the same complete teardown as
|
||||
/// <see cref="ShutdownAsync"/>. Idempotent, and safe CONCURRENTLY with a still-running
|
||||
/// <see cref="ShutdownAsync"/> (the runtime's final stop abandons a shutdown that overruns its budget and
|
||||
/// disposes anyway): each teardown retires a disjoint device table. There is deliberately no synchronous
|
||||
/// <c>Dispose()</c> — it could only block on this teardown (sync-over-async).
|
||||
/// disposes anyway): each teardown retires a disjoint device table. This is the dispose the runtime calls
|
||||
/// (it prefers <see cref="IAsyncDisposable"/>).
|
||||
/// </summary>
|
||||
/// <returns>A value task representing the asynchronous disposal.</returns>
|
||||
public async ValueTask DisposeAsync()
|
||||
@@ -2277,6 +2277,22 @@ public sealed class AbCipDriver : IDriver, IReadable, IWritable, ITagDiscovery,
|
||||
await ShutdownAsync(CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the same teardown as <see cref="DisposeAsync"/> WITHOUT blocking the calling thread on it (review
|
||||
/// 07 F10: the old <c>DisposeAsync().AsTask().GetAwaiter().GetResult()</c> parked the caller — a dispatcher
|
||||
/// thread — for up to the probe join, with the usual sync-over-async deadlock exposure). The teardown runs to
|
||||
/// completion in the background and any fault is logged. Callers that need completion use
|
||||
/// <see cref="DisposeAsync"/>; kept only for synchronous <c>using</c> / <c>Dispose()</c> call sites.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
var teardown = DisposeAsync().AsTask();
|
||||
if (teardown.IsCompleted) { teardown.GetAwaiter().GetResult(); return; }
|
||||
_ = teardown.ContinueWith(
|
||||
t => _logger.LogWarning(t.Exception, "AbCip driver {DriverInstanceId}: background dispose failed", _driverInstanceId),
|
||||
CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Per-device runtime state. Holds the parsed host address, family profile, and the
|
||||
/// live libplctag.NET <see cref="IAbCipTagRuntime"/> instances keyed by tag name.
|
||||
|
||||
Reference in New Issue
Block a user