Merge b626-630 (batch #626+): release prior device/session state on re-Initialize in six drivers
Conflict in S7Driver.ReleaseConnectionAsync resolved: kept the gated Plc swap from #630 and carried the negotiated-PDU reset from #632 #294 into it. Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
@@ -225,6 +225,11 @@ public sealed class S7Driver
|
||||
/// <inheritdoc />
|
||||
public async Task InitializeAsync(string driverConfigJson, CancellationToken cancellationToken)
|
||||
{
|
||||
// #630: InitializeAsync is reachable on a live driver (DriverInstanceActor.AdoptConfigDuringInit
|
||||
// runs it with no teardown first). Stop the previous probe loop and drop the previous connection
|
||||
// before they are overwritten below. Deliberately NOT ShutdownAsync: that also drops _poll and the
|
||||
// subscriptions, which are owned by the shutdown path (the actor re-subscribes after every successful init).
|
||||
await ReleaseConnectionAsync().ConfigureAwait(false);
|
||||
Health = new DriverHealth(DriverState.Initializing, null, null);
|
||||
try
|
||||
{
|
||||
@@ -363,7 +368,18 @@ public sealed class S7Driver
|
||||
// disposing its CTS (drain-before-dispose, STAB-6-S7), so no poll iteration touches _gate
|
||||
// after this returns.
|
||||
await _poll.DisposeAsync().ConfigureAwait(false);
|
||||
await ReleaseConnectionAsync().ConfigureAwait(false);
|
||||
_writeFault = null;
|
||||
_connectBackoff.RecordSuccess();
|
||||
Health = new DriverHealth(DriverState.Unknown, Health.LastSuccessfulRead, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancels + bounded-joins the probe loop, then disposes its CTS and the PLC connection. The
|
||||
/// connection part of <see cref="ShutdownAsync"/> (everything but the poll engine and health).
|
||||
/// </summary>
|
||||
private async Task ReleaseConnectionAsync()
|
||||
{
|
||||
var probeCts = _probeCts;
|
||||
var probeTask = _probeTask;
|
||||
try { probeCts?.Cancel(); } catch { }
|
||||
@@ -382,14 +398,27 @@ public sealed class S7Driver
|
||||
_probeCts = null;
|
||||
_probeTask = null;
|
||||
|
||||
_initialized = false;
|
||||
_plcDead = false;
|
||||
_writeFault = null;
|
||||
_connectBackoff.RecordSuccess();
|
||||
try { Plc?.Dispose(); } catch { /* best-effort — tearing down anyway */ }
|
||||
Plc = null;
|
||||
Volatile.Write(ref _negotiatedPduSize, 0); // a stale post-teardown reading would read as "online"
|
||||
Health = new DriverHealth(DriverState.Unknown, Health.LastSuccessfulRead, null);
|
||||
// Swap the connection out under _gate so a Read/Write/poll pass that already holds the gate (they
|
||||
// re-read Plc inside it) can never see a disposed Plc mid-swap. Bounded: a wedged holder must not hang
|
||||
// teardown, in which case we proceed exactly as the un-gated path always did.
|
||||
var gated = false;
|
||||
try { gated = await _gate.WaitAsync(DrainTimeout).ConfigureAwait(false); } catch { }
|
||||
if (!gated)
|
||||
_logger.LogWarning(
|
||||
"S7 driver {DriverInstanceId}: disposing the S7 connection while an operation still holds the gate (waited {Timeout})",
|
||||
_driverInstanceId, DrainTimeout);
|
||||
try
|
||||
{
|
||||
_initialized = false;
|
||||
_plcDead = false;
|
||||
try { Plc?.Dispose(); } catch { /* best-effort — tearing down anyway */ }
|
||||
Plc = null;
|
||||
Volatile.Write(ref _negotiatedPduSize, 0); // a stale post-teardown reading would read as "online"
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (gated) { try { _gate.Release(); } catch (ObjectDisposedException) { } }
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
Reference in New Issue
Block a user