WIP Make collections thread-safe and add safe iteration

Replaced static lists and dictionaries with thread-safe ConcurrentDictionary or ThreadLocal collections for various item components and systems. Updated all relevant code to use snapshots (ToArray, ToList) for safe iteration, and added helper methods for marking and clearing changed connections. These changes improve thread safety and prevent potential concurrency issues in multi-threaded scenarios.
This commit is contained in:
Eero
2025-12-28 04:59:56 +08:00
parent 90962b2328
commit 46595b1399
25 changed files with 519 additions and 245 deletions
@@ -222,13 +222,14 @@ namespace Barotrauma.Items.Components
public void SetRecipientsDirty()
{
recipientsDirty = true;
if (IsPower) { Powered.ChangedConnections.Add(this); }
if (IsPower) { Powered.MarkConnectionChanged(this); }
}
private void RefreshRecipients()
{
recipients.Clear();
foreach (var wire in wires)
// Use ToArray() snapshot for thread-safe iteration
foreach (var wire in wires.ToArray())
{
Connection recipient = wire.OtherConnection(this);
if (recipient != null) { recipients.Add(recipient); }
@@ -267,8 +268,8 @@ namespace Barotrauma.Items.Components
//Check if both connections belong to a larger grid
if (prevOtherConnection.recipients.Count > 1 && recipients.Count > 1)
{
Powered.ChangedConnections.Add(prevOtherConnection);
Powered.ChangedConnections.Add(this);
Powered.MarkConnectionChanged(prevOtherConnection);
Powered.MarkConnectionChanged(this);
}
else if (recipients.Count > 1)
{
@@ -284,7 +285,7 @@ namespace Barotrauma.Items.Components
else if (Grid.Connections.Count == 2)
{
//Delete the grid as these were the only 2 devices
Powered.Grids.Remove(Grid.ID);
Powered.Grids.TryRemove(Grid.ID, out _);
Grid = null;
prevOtherConnection.Grid = null;
}
@@ -325,8 +326,8 @@ namespace Barotrauma.Items.Components
else
{
//Flag change so that proper grids can be formed
Powered.ChangedConnections.Add(this);
Powered.ChangedConnections.Add(otherConnection);
Powered.MarkConnectionChanged(this);
Powered.MarkConnectionChanged(otherConnection);
}
}
@@ -339,7 +340,8 @@ namespace Barotrauma.Items.Components
{
LastSentSignal = signal;
enumeratingWires = true;
foreach (var wire in wires)
// Use ToArray() snapshot for thread-safe iteration
foreach (var wire in wires.ToArray())
{
Connection recipient = wire.OtherConnection(this);
if (recipient == null) { continue; }
@@ -354,14 +356,14 @@ namespace Barotrauma.Items.Components
GameMain.LuaCs.Hook.Call("signalReceived." + recipient.item.Prefab.Identifier, signal, recipient);
}
foreach (CircuitBoxConnection connection in CircuitBoxConnections)
foreach (CircuitBoxConnection connection in CircuitBoxConnections.ToArray())
{
connection.ReceiveSignal(signal);
GameMain.LuaCs.Hook.Call("signalReceived", signal, connection.Connection);
GameMain.LuaCs.Hook.Call("signalReceived." + connection.Connection.Item.Prefab.Identifier, signal, connection);
}
enumeratingWires = false;
foreach (var removedWire in removedWires)
foreach (var removedWire in removedWires.ToArray())
{
wires.Remove(removedWire);
}
@@ -372,14 +374,16 @@ namespace Barotrauma.Items.Components
{
conn.LastReceivedSignal = signal;
foreach (ItemComponent ic in conn.item.Components)
// Use ToArray() snapshot for thread-safe iteration
foreach (ItemComponent ic in conn.item.Components.ToArray())
{
ic.ReceiveSignal(signal, conn);
}
if (conn.Effects == null || signal.value == "0") { return; }
foreach (StatusEffect effect in conn.Effects)
// Use ToArray() snapshot for thread-safe iteration
foreach (StatusEffect effect in conn.Effects.ToArray())
{
conn.Item.ApplyStatusEffect(effect, ActionType.OnUse, (float)Timing.Step);
}
@@ -389,13 +393,15 @@ namespace Barotrauma.Items.Components
{
if (IsPower && Grid != null)
{
Powered.ChangedConnections.Add(this);
foreach (Connection c in recipients)
Powered.MarkConnectionChanged(this);
// Use ToArray() snapshot for thread-safe iteration
foreach (Connection c in recipients.ToArray())
{
Powered.ChangedConnections.Add(c);
Powered.MarkConnectionChanged(c);
}
}
foreach (var wire in wires)
// Use ToArray() snapshot for thread-safe iteration
foreach (var wire in wires.ToArray())
{
wire.RemoveConnection(this);
recipientsDirty = true;
@@ -403,7 +409,7 @@ namespace Barotrauma.Items.Components
if (enumeratingWires)
{
foreach (var wire in wires)
foreach (var wire in wires.ToArray())
{
removedWires.Add(wire);
}
@@ -446,7 +452,8 @@ namespace Barotrauma.Items.Components
{
XElement newElement = new XElement(IsOutput ? "output" : "input", new XAttribute("name", Name));
foreach (var wire in wires.OrderBy(w => w.Item.ID))
// Use ToArray() snapshot before OrderBy for thread-safe iteration
foreach (var wire in wires.ToArray().OrderBy(w => w.Item.ID))
{
newElement.Add(new XElement("link",
new XAttribute("w", wire.Item.ID.ToString()),