diff --git a/src/Dragon-ISO.Engine/Discovery/NdiDiscoveryService.cs b/src/Dragon-ISO.Engine/Discovery/NdiDiscoveryService.cs index 4a0e4a2..f8b7e58 100644 --- a/src/Dragon-ISO.Engine/Discovery/NdiDiscoveryService.cs +++ b/src/Dragon-ISO.Engine/Discovery/NdiDiscoveryService.cs @@ -187,23 +187,42 @@ public sealed class NdiDiscoveryService } /// - /// Dispose the current finder and create a fresh one against the cached - /// discovery groups. Clears the seen-set so all currently-visible sources - /// will re-fire as on the next poll. + /// Create a fresh finder against the cached discovery groups and, only once it + /// is successfully in hand, dispose the old one and clear the seen-set so all + /// currently-visible sources re-fire as on + /// the next poll. + /// + /// Ordering matters: we build the replacement before disposing the + /// incumbent. If throws (the failure mode + /// this method's catch exists for), the incumbent finder is left untouched and + /// fully usable, the seen-set is preserved (no spurious re-Add storm), and the + /// loop simply keeps polling the existing finder until the next rebuild attempt. + /// + /// Returns true if the finder was actually swapped; false if the rebuild failed + /// and the existing finder was retained. Internal for unit-testing the failure + /// path. /// - private void RebuildFinder(string reason) + internal bool RebuildFinder(string reason) { + NdiFindHandle replacement; try { _logger.LogInformation("Rebuilding NDI finder ({Reason}).", reason); - _finder.Dispose(); - _finder = _interop.CreateFinder(_discoveryGroups); - _previous.Clear(); + replacement = _interop.CreateFinder(_discoveryGroups); } catch (Exception ex) { _logger.LogWarning(ex, "Finder rebuild failed ({Reason}); continuing with existing finder.", reason); + return false; } + + // New finder is live — retire the old one and reset discovery state. + var old = _finder; + _finder = replacement; + try { old.Dispose(); } + catch (Exception ex) { _logger.LogWarning(ex, "Disposing previous finder threw after rebuild ({Reason}).", reason); } + _previous.Clear(); + return true; } ///