Fix thread-safety: snapshot TieLineCollection before enumeration in GetRouteToSource#1401
Fix thread-safety: snapshot TieLineCollection before enumeration in GetRouteToSource#1401
Conversation
…ion before enumeration Co-authored-by: ngenovese11 <23391587+ngenovese11@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR aims to prevent InvalidOperationException caused by concurrent modifications to TieLineCollection.Default while GetRouteToSource is enumerating tie lines during route discovery.
Changes:
- Snapshot
TieLineCollection.Defaultinto a local list viaToList()inside the recursiveGetRouteToSourceimplementation. - Update downstream LINQ queries to operate on the snapshot rather than the live global collection.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| // Take a snapshot to avoid InvalidOperationException if TieLineCollection.Default is | ||
| // modified concurrently (e.g. NVX tie line registration during route requests). | ||
| var tieLines = TieLineCollection.Default.ToList(); |
There was a problem hiding this comment.
TieLineCollection.Default.ToList() still enumerates the underlying List<TieLine> and can throw InvalidOperationException if another thread modifies the collection during the snapshot. To fully address this, reads and writes of TieLineCollection.Default need synchronization (shared lock, or a thread-safe/copy-on-write collection), ideally via a safe snapshot method on TieLineCollection. Also, because this method is recursive, taking a new ToList() snapshot on every recursion can add significant allocation/CPU overhead; consider taking one snapshot in the public entry point and passing it down through the recursive calls.
| var tieLines = TieLineCollection.Default.ToList(); | |
| List<TieLine> tieLines; | |
| lock (TieLineCollection.Default) | |
| { | |
| tieLines = TieLineCollection.Default.ToList(); | |
| } |
Concurrent NVX tie line registration modifies
TieLineCollection.Default(List<TieLine>) whileGetRouteToSourcelazily enumerates it via LINQ.Where(), causingInvalidOperationException: Collection was modified; enumeration operation may not executeduring room combiner scenario activation.Change
Extensions.cs—GetRouteToSource(private recursive overload): Take an eager snapshot ofTieLineCollection.Defaultat the top of each call via.ToList(). All downstreamWhere()/FirstOrDefault()queries operate on the stable copy.Warning
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
crl.entrust.net/usr/bin/dotnet dotnet build src/PepperDash.Essentials.Core/PepperDash.Essentials.Core.csproj(dns block)/usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/Essentials/Essentials/PepperDash.Essentials.4Series.sln --packages /tmp/codeql-scratch-34409fe8f58e31ec/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-34409fe8f58e31ec/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-34409fe8f58e31ec/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true(dns block)/usr/bin/dotnet dotnet restore --no-dependencies /tmp/codeql-scratch-34409fe8f58e31ec/dbs/csharp/working/8E3C38744AF97AEA71D98A0334D7EC97/missingpackages_workingdir --packages /tmp/codeql-scratch-34409fe8f58e31ec/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-34409fe8f58e31ec/dbs/csharp/working/nugetconfig/nuget.config --force(dns block)ocsp.entrust.net/usr/bin/dotnet dotnet build src/PepperDash.Essentials.Core/PepperDash.Essentials.Core.csproj(dns block)/usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/Essentials/Essentials/PepperDash.Essentials.4Series.sln --packages /tmp/codeql-scratch-34409fe8f58e31ec/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-34409fe8f58e31ec/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-34409fe8f58e31ec/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true(dns block)/usr/bin/dotnet dotnet restore --no-dependencies /tmp/codeql-scratch-34409fe8f58e31ec/dbs/csharp/working/8E3C38744AF97AEA71D98A0334D7EC97/missingpackages_workingdir --packages /tmp/codeql-scratch-34409fe8f58e31ec/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-34409fe8f58e31ec/dbs/csharp/working/nugetconfig/nuget.config --force(dns block)If you need me to access, download, or install something from one of these locations, you can either:
Original prompt
GetRouteToSource- Collection modified during enumeration #1400💬 Send tasks to Copilot coding agent from Slack and Teams to turn conversations into code. Copilot posts an update in your thread when it's finished.