From 854d7bea1f970948667035be8bef5fa3ffc29b46 Mon Sep 17 00:00:00 2001 From: Eero Date: Mon, 29 Dec 2025 18:20:37 +0800 Subject: [PATCH] CBT2.0 Make Hull and Level methods thread-safe using ThreadLocal Replaced instance fields with ThreadLocal collections in Hull.GetConnectedHulls and Level.GetCells to ensure thread safety during parallel updates. Methods now return copies of the collections to prevent concurrent modification issues. --- Barotrauma/BarotraumaShared/SharedSource/Map/Hull.cs | 11 +++++++++-- .../SharedSource/Map/Levels/Level.cs | 12 ++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Hull.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Hull.cs index 50a1b3b39..08a7bad97 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Hull.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Hull.cs @@ -1214,15 +1214,22 @@ namespace Barotrauma } } - private readonly HashSet adjacentHulls = new HashSet(); + /// + /// Used in - ThreadLocal for thread safety during parallel updates + /// + private static readonly ThreadLocal> adjacentHullsLocal = + new ThreadLocal>(() => new HashSet()); + public IEnumerable GetConnectedHulls(bool includingThis, int? searchDepth = null, bool ignoreClosedGaps = false) { + var adjacentHulls = adjacentHullsLocal.Value; adjacentHulls.Clear(); int startStep = 0; searchDepth ??= 100; GetAdjacentHulls(adjacentHulls, ref startStep, searchDepth.Value, ignoreClosedGaps); if (!includingThis) { adjacentHulls.Remove(this); } - return adjacentHulls; + // Return a copy to prevent concurrent modification if the caller enumerates while another thread calls this method + return adjacentHulls.ToHashSet(); } private void GetAdjacentHulls(HashSet connectedHulls, ref int step, int searchDepth, bool ignoreClosedGaps = false) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Level.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Level.cs index c2e123bd1..d0ddf3fe9 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Level.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Level.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; +using System.Threading; using System.Xml.Linq; using Voronoi2; @@ -3655,9 +3656,15 @@ namespace Barotrauma return cells; } - private readonly List tempCells = new List(); + /// + /// Used in - ThreadLocal for thread safety during parallel updates + /// + private static readonly ThreadLocal> tempCellsLocal = + new ThreadLocal>(() => new List()); + public List GetCells(Vector2 worldPos, int searchDepth = 2) { + var tempCells = tempCellsLocal.Value; tempCells.Clear(); int gridPosX = (int)Math.Floor(worldPos.X / GridCellSize); int gridPosY = (int)Math.Floor(worldPos.Y / GridCellSize); @@ -3712,7 +3719,8 @@ namespace Barotrauma tempCells.AddRange(abyssIsland.Cells); } - return tempCells; + // Return a copy to prevent concurrent modification if the caller enumerates while another thread calls this method + return tempCells.ToList(); } public VoronoiCell GetClosestCell(Vector2 worldPos)