fix(driver-galaxy): resolve Medium code-review finding (Driver.Galaxy-006)
HashSet<T>.First() enumeration order is unspecified and unstable across mutations, so the "owner" handle attached to alarm events was non-deterministic when multiple alarm subscriptions were active. Change _alarmSubscriptions from HashSet to List (preserving insertion order) and pick [0] — the earliest-registered handle — as the deterministic owner. The server routes transitions by SourceNodeId, not by handle, so the choice of handle does not affect delivery to active subscribers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -75,7 +75,10 @@ public sealed class GalaxyDriver
|
||||
private readonly Lock _alarmHandlersLock = new();
|
||||
private readonly Lock _alarmFeedLock = new();
|
||||
private bool _alarmFeedWired;
|
||||
private readonly HashSet<GalaxyAlarmSubscriptionHandle> _alarmSubscriptions = new();
|
||||
// List preserves insertion order so OnAlarmFeedTransition always picks the
|
||||
// earliest-registered handle — a deterministic choice that doesn't vary as
|
||||
// handles are added/removed (Driver.Galaxy-006 fix: HashSet.First() is unstable).
|
||||
private readonly List<GalaxyAlarmSubscriptionHandle> _alarmSubscriptions = new();
|
||||
|
||||
// PR 4.W — production runtime owned by InitializeAsync. The driver builds these
|
||||
// when it opens a real gw session; tests bypass them by injecting seams via the
|
||||
@@ -964,12 +967,15 @@ public sealed class GalaxyDriver
|
||||
GalaxyAlarmSubscriptionHandle? handle;
|
||||
lock (_alarmHandlersLock)
|
||||
{
|
||||
// Pick any active subscription handle as the "owner" of the event. The
|
||||
// server-side state machine doesn't multiplex by handle today; if multiple
|
||||
// alarm subscriptions are active we still only fire the event once and
|
||||
// the AlarmConditionService dispatches per-source-node downstream.
|
||||
// Pick the earliest-registered handle as the event owner. The server routes
|
||||
// by SourceNodeId (not by handle), so every active subscriber sees the same
|
||||
// transition regardless of which handle is attached here. Using the first
|
||||
// insertion-order entry is deterministic and stable as long as at least one
|
||||
// subscription remains — HashSet.First() was unstable across mutations
|
||||
// (Driver.Galaxy-006 fix). _alarmSubscriptions is a List, so [0] is always
|
||||
// the earliest-registered handle.
|
||||
handle = _alarmSubscriptions.Count > 0
|
||||
? _alarmSubscriptions.First()
|
||||
? _alarmSubscriptions[0]
|
||||
: null;
|
||||
}
|
||||
if (handle is null) return;
|
||||
|
||||
Reference in New Issue
Block a user