fix(server): resolve Medium code-review finding (Server-005)
Add _nodeManagerDisposed field; set it under Lock in Dispose before detaching the alarm-service handler; check it in OnAlarmServiceTransition under the same Lock so an in-flight transition cannot dispatch to a ConditionSink whose DriverNodeManager is being concurrently disposed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -110,6 +110,8 @@ public sealed class DriverNodeManager : CustomNodeManager2, IAddressSpaceBuilder
|
||||
private readonly AlarmConditionService? _alarmService;
|
||||
private readonly Dictionary<string, ConditionSink> _conditionSinks = new(StringComparer.OrdinalIgnoreCase);
|
||||
private EventHandler<AlarmConditionTransition>? _alarmTransitionHandler;
|
||||
// Guards OnAlarmServiceTransition against dispatch to a disposed node manager (Server-005).
|
||||
private bool _nodeManagerDisposed;
|
||||
|
||||
// Task #24 — Phase 7 Gap 1: route OPC UA Part 9 Acknowledge / Confirm method calls on
|
||||
// scripted alarm condition nodes to the ScriptedAlarmEngine so the engine state machine
|
||||
@@ -179,6 +181,11 @@ public sealed class DriverNodeManager : CustomNodeManager2, IAddressSpaceBuilder
|
||||
ConditionSink? sink;
|
||||
lock (Lock)
|
||||
{
|
||||
// Guard against a transition that arrives after Dispose has begun (Server-005).
|
||||
// The alarm service is shared across drivers, so detaching the handler in Dispose
|
||||
// does not synchronise against an in-flight Invoke — check _nodeManagerDisposed
|
||||
// under the same lock Dispose sets it under so we never forward to a torn-down sink.
|
||||
if (_nodeManagerDisposed) return;
|
||||
_conditionSinks.TryGetValue(t.ConditionId, out sink);
|
||||
}
|
||||
if (sink is null) return;
|
||||
@@ -302,10 +309,18 @@ public sealed class DriverNodeManager : CustomNodeManager2, IAddressSpaceBuilder
|
||||
/// </summary>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && _alarmService is not null && _alarmTransitionHandler is not null)
|
||||
if (disposing)
|
||||
{
|
||||
_alarmService.TransitionRaised -= _alarmTransitionHandler;
|
||||
_alarmTransitionHandler = null;
|
||||
// Mark disposed under Lock before detaching the handler so an in-flight
|
||||
// OnAlarmServiceTransition that already holds the lock sees _nodeManagerDisposed
|
||||
// and exits without forwarding to the sink (Server-005).
|
||||
lock (Lock) { _nodeManagerDisposed = true; }
|
||||
|
||||
if (_alarmService is not null && _alarmTransitionHandler is not null)
|
||||
{
|
||||
_alarmService.TransitionRaised -= _alarmTransitionHandler;
|
||||
_alarmTransitionHandler = null;
|
||||
}
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user