From 2cfac0ae63ddfe261cf64a695ffeed021b7453f1 Mon Sep 17 00:00:00 2001 From: ikpil Date: Sun, 20 Apr 2025 13:29:49 +0900 Subject: [PATCH] added atomic tests --- src/Box2D.NET/B2Atomics.cs | 9 +++- test/Box2D.NET.Test/B2AtomicTests.cs | 79 ++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 test/Box2D.NET.Test/B2AtomicTests.cs diff --git a/src/Box2D.NET/B2Atomics.cs b/src/Box2D.NET/B2Atomics.cs index c7f9b1f9..e05d0782 100644 --- a/src/Box2D.NET/B2Atomics.cs +++ b/src/Box2D.NET/B2Atomics.cs @@ -2,37 +2,44 @@ // SPDX-FileCopyrightText: 2025 Ikpil Choi(ikpil@naver.com) // SPDX-License-Identifier: MIT +using System.Runtime.CompilerServices; using System.Threading; namespace Box2D.NET { public static class B2Atomics { + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void b2AtomicStoreInt(ref B2AtomicInt a, int value) { Interlocked.Exchange(ref a.value, value); } - + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int b2AtomicLoadInt(ref B2AtomicInt a) { return Interlocked.CompareExchange(ref a.value, 0, 0); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int b2AtomicFetchAddInt(ref B2AtomicInt a, int increment) { return Interlocked.Add(ref a.value, increment) - increment; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool b2AtomicCompareExchangeInt(ref B2AtomicInt a, int expected, int desired) { return expected == Interlocked.CompareExchange(ref a.value, desired, expected); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void b2AtomicStoreU32(ref B2AtomicU32 a, uint value) { Interlocked.Exchange(ref a.value, value); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint b2AtomicLoadU32(ref B2AtomicU32 a) { return (uint)Interlocked.Read(ref a.value); diff --git a/test/Box2D.NET.Test/B2AtomicTests.cs b/test/Box2D.NET.Test/B2AtomicTests.cs new file mode 100644 index 00000000..cb312658 --- /dev/null +++ b/test/Box2D.NET.Test/B2AtomicTests.cs @@ -0,0 +1,79 @@ +using System; +using System.Threading; +using NUnit.Framework; +using static Box2D.NET.B2Atomics; + +namespace Box2D.NET.Test; + +public class B2AtomicTests +{ + [Test] + public void Test_b2Atomic_Int_Store_And_Load() + { + B2AtomicInt atomic = new B2AtomicInt(); + b2AtomicStoreInt(ref atomic, 42); + Assert.That(b2AtomicLoadInt(ref atomic), Is.EqualTo(42)); + } + + [Test] + public void Test_b2Atomic_Int_FetchAdd() + { + B2AtomicInt atomic = new B2AtomicInt(); + b2AtomicStoreInt(ref atomic, 10); + int original = b2AtomicFetchAddInt(ref atomic, 5); + Assert.That(original, Is.EqualTo(10)); + Assert.That(b2AtomicLoadInt(ref atomic), Is.EqualTo(15)); + } + + [Test] + public void Test_b2Atomic_Int_CompareExchange() + { + B2AtomicInt atomic = new B2AtomicInt(); + b2AtomicStoreInt(ref atomic, 100); + + // Success case + bool exchanged = b2AtomicCompareExchangeInt(ref atomic, 100, 200); + Assert.That(exchanged, Is.True); + Assert.That(b2AtomicLoadInt(ref atomic), Is.EqualTo(200)); + + // Fail case + exchanged = b2AtomicCompareExchangeInt(ref atomic, 100, 300); + Assert.That(exchanged, Is.False); + Assert.That(b2AtomicLoadInt(ref atomic), Is.EqualTo(200)); + } + + [Test] + public void Test_b2Atomic_U32_Store_And_Load() + { + B2AtomicU32 atomic = new B2AtomicU32(); + b2AtomicStoreU32(ref atomic, 123456789u); + Assert.That(b2AtomicLoadU32(ref atomic), Is.EqualTo(123456789u)); + } + + [Test] + public void Test_b2Atomic_Int_FetchAdd_IsThreadSafe() + { + B2AtomicInt atomic = new B2AtomicInt(); + int threadCount = (int)Math.Round(Environment.ProcessorCount * 2.5f); + int addsPerThread = 100_000; + Thread[] threads = new Thread[threadCount]; + + for (int i = 0; i < threadCount; i++) + { + threads[i] = new Thread(() => + { + for (int j = 0; j < addsPerThread; j++) + { + b2AtomicFetchAddInt(ref atomic, 1); + } + }); + threads[i].Start(); + } + + foreach (Thread t in threads) + t.Join(); + + Assert.That(b2AtomicLoadInt(ref atomic), Is.EqualTo(threadCount * addsPerThread)); + } + +} \ No newline at end of file