WIP Make static collections thread-safe using ThreadStatic and ThreadLocal

Refactored various static and instance collections to use [ThreadStatic], ThreadLocal, or local variables to prevent concurrent modification issues during parallel updates. This affects status effect targets, affliction lists, damage modifiers, and cached data in Character, CharacterHealth, Limb, Explosion, Hull, Submarine, and ToolBox classes. Also replaced Dictionary caches with ConcurrentDictionary where appropriate for thread safety.
This commit is contained in:
Eero
2025-12-28 14:14:53 +08:00
parent c5fa49405f
commit 45312af297
7 changed files with 135 additions and 75 deletions
@@ -2,6 +2,7 @@
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
@@ -75,7 +76,7 @@ namespace Barotrauma
return !corrected;
}
private static readonly Dictionary<string, string> cachedFileNames = new Dictionary<string, string>();
private static readonly ConcurrentDictionary<string, string> cachedFileNames = new ConcurrentDictionary<string, string>();
public static string CorrectFilenameCase(string filename, out bool corrected, string directory = "")
{
@@ -153,7 +154,7 @@ namespace Barotrauma
if (i < subDirs.Length - 1) { filename += "/"; }
}
cachedFileNames.Add(originalFilename, filename);
cachedFileNames.TryAdd(originalFilename, filename);
return filename;
}
@@ -355,32 +356,26 @@ namespace Barotrauma
return text;
}
private static Dictionary<string, List<string>> cachedLines = new Dictionary<string, List<string>>();
private static readonly ConcurrentDictionary<string, List<string>> cachedLines = new ConcurrentDictionary<string, List<string>>();
public static string GetRandomLine(string filePath, Rand.RandSync randSync = Rand.RandSync.ServerAndClient)
{
List<string> lines;
if (cachedLines.ContainsKey(filePath))
{
lines = cachedLines[filePath];
}
else
List<string> lines = cachedLines.GetOrAdd(filePath, path =>
{
try
{
lines = File.ReadAllLines(filePath, catchUnauthorizedAccessExceptions: false).ToList();
cachedLines.Add(filePath, lines);
if (lines.Count == 0)
var fileLines = File.ReadAllLines(path, catchUnauthorizedAccessExceptions: false).ToList();
if (fileLines.Count == 0)
{
DebugConsole.ThrowError("File \"" + filePath + "\" is empty!");
return "";
DebugConsole.ThrowError("File \"" + path + "\" is empty!");
}
return fileLines;
}
catch (Exception e)
{
DebugConsole.ThrowError("Couldn't open file \"" + filePath + "\"!", e);
return "";
DebugConsole.ThrowError("Couldn't open file \"" + path + "\"!", e);
return new List<string>();
}
}
});
if (lines.Count == 0) return "";
return lines[Rand.Range(0, lines.Count, randSync)];