From 0c78c927dc5b30889f632506038bdc369389ef47 Mon Sep 17 00:00:00 2001 From: penspanic Date: Sat, 11 Jan 2025 20:43:43 +0900 Subject: [PATCH] Fix thread-safety problem in World.WordSize by applying Interlocked operations --- src/Arch.Tests/MultiThreadTest.cs | 43 +++++++++++++++++++++++++++++++ src/Arch/Core/World.cs | 8 +++--- 2 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 src/Arch.Tests/MultiThreadTest.cs diff --git a/src/Arch.Tests/MultiThreadTest.cs b/src/Arch.Tests/MultiThreadTest.cs new file mode 100644 index 0000000..d33da5d --- /dev/null +++ b/src/Arch.Tests/MultiThreadTest.cs @@ -0,0 +1,43 @@ +using Arch.Core; +using static NUnit.Framework.Assert; + +namespace Arch.Tests; + +[TestFixture] +public class MultiThreadTest +{ + /// + /// Checks if the is correct when creating World multithreaded. + /// + [Test] + public void MultiThreadedCreateAndDestroy() + { + int originalWorldSize = World.WorldSize; + const int testCount = 10; + for (int i = 0; i < testCount; ++i) + { + var threads = new List(); + for (var j = 0; j < Environment.ProcessorCount; j++) + { + var thread = new Thread(() => + { + for (var j = 0; j < 1000; j++) + { + var world = World.Create(); + World.Destroy(world); + } + }); + threads.Add(thread); + } + + threads.ForEach(t => t.Start()); + + foreach (var thread in threads) + { + thread.Join(); + } + + That(World.WorldSize, Is.EqualTo(originalWorldSize)); + } + } +} diff --git a/src/Arch/Core/World.cs b/src/Arch/Core/World.cs index a200b4a..56d1159 100644 --- a/src/Arch/Core/World.cs +++ b/src/Arch/Core/World.cs @@ -81,7 +81,9 @@ public partial class World /// /// Tracks how many s exists. /// - public static int WorldSize { get; private set; } + public static int WorldSize => Interlocked.CompareExchange(ref worldSizeUnsafe, 0, 0); + + private static int worldSizeUnsafe; /// /// The shared static used for Multithreading. @@ -114,7 +116,7 @@ public static World Create() } Worlds[recycledId] = world; - WorldSize++; + Interlocked.Increment(ref worldSizeUnsafe); return world; } #endif @@ -131,7 +133,7 @@ public static void Destroy(World world) { Worlds[world.Id] = null!; RecycledWorldIds.Enqueue(world.Id); - WorldSize--; + Interlocked.Decrement(ref worldSizeUnsafe); } #endif