fix(mtconnect): volatile _options publication, IConvertible write guard, idempotent client dispose
Driver.MTConnect-005/-006/-007, each TDD'd red-first. - -005: _options was the one field at the StartCoreAsync commit point published by a plain write and read lock-free from ReadAsync/WriteAsync/DiscoverAsync/HostName/health. Now Volatile.Write/Volatile.Read like _session/_binding/_announcedInstanceId, pinned by a source scan (MTConnectOptionsPublicationTests) that fails on any unfenced touch. - -006: TryFormatValue guarded null and Array, but Convert.ToString renders ANY non-IConvertible value as its type name under a Good status. Guard is now the property the remark relies on; the accepted set (string/bool/numeric/DateTime) is unchanged. - -007: MTConnectAgentClient.Dispose was a check-then-set on a non-volatile bool, so two racing disposals both passed and the loser cancelled an already-disposed CTS. Now an Interlocked.Exchange claim over an int, with the request seams reading it volatile.
This commit is contained in:
@@ -370,6 +370,13 @@ internal sealed class CannedAgentClient : IMTConnectAgentClient, IDisposable
|
||||
/// <summary>When set, a write throws this instead of answering — the transport-failure leg.</summary>
|
||||
public Exception? WriteFailure { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ran after a write has been recorded and before its outcome is returned. The deterministic
|
||||
/// injection point for something that must happen BETWEEN two slots of one batch — cancelling
|
||||
/// the batch's token, for instance — with no timing hope involved.
|
||||
/// </summary>
|
||||
public Action? AfterWrite { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<MTConnectWriteOutcome> WriteObservationAsync(
|
||||
string deviceName, string dataItemId, string value, CancellationToken cancellationToken)
|
||||
@@ -377,6 +384,7 @@ internal sealed class CannedAgentClient : IMTConnectAgentClient, IDisposable
|
||||
ObjectDisposedException.ThrowIf(IsDisposed, this);
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
Writes.Enqueue((deviceName, dataItemId, value));
|
||||
AfterWrite?.Invoke();
|
||||
|
||||
return WriteFailure is null ? Task.FromResult(WriteOutcome) : throw WriteFailure;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user