Skip to content

Commit f438b03

Browse files
committed
added B2ArenaAllocatorTest
1 parent c645611 commit f438b03

4 files changed

Lines changed: 134 additions & 8 deletions

File tree

src/Box2D.NET/B2ArenaAllocator.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ public class B2ArenaAllocator
1414
private IB2ArenaAllocatable[] _allocators;
1515
private int _allocatorCount;
1616

17+
public int Count => _allocatorCount;
18+
1719
public B2ArenaAllocator(int capacity)
1820
{
1921
_lock = new object();
@@ -22,10 +24,10 @@ public B2ArenaAllocator(int capacity)
2224
_allocatorCount = 0;
2325
}
2426

25-
public B2ArenaAllocatorImpl<T> GetOrCreateImpl<T>() where T : new()
27+
public B2ArenaAllocatorTyped<T> GetOrCreateFor<T>() where T : new()
2628
{
2729
var index = B2ArenaAllocatorIndexer.Index<T>();
28-
if (_allocators.Length <= index || null == _allocators[index])
30+
if (_allocators.Length <= index)
2931
{
3032
lock (_lock)
3133
{
@@ -40,7 +42,13 @@ public B2ArenaAllocator(int capacity)
4042

4143
_allocators = temp;
4244
}
45+
}
46+
}
4347

48+
if (null == _allocators[index])
49+
{
50+
lock (_lock)
51+
{
4452
// new
4553
if (null == _allocators[index])
4654
{
@@ -50,7 +58,7 @@ public B2ArenaAllocator(int capacity)
5058
}
5159
}
5260

53-
return _allocators[index] as B2ArenaAllocatorImpl<T>;
61+
return _allocators[index] as B2ArenaAllocatorTyped<T>;
5462
}
5563

5664
public Span<IB2ArenaAllocatable> AsSpan()
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ namespace Box2D.NET
1414
// if you try to interleave multiple allocate/free pairs.
1515
// This allocator uses the heap if space is insufficient.
1616
// I could remove the need to free entries individually.
17-
public class B2ArenaAllocatorImpl<T> : IB2ArenaAllocatable where T : new()
17+
public class B2ArenaAllocatorTyped<T> : IB2ArenaAllocatable where T : new()
1818
{
1919
public ArraySegment<T> data;
2020
public int capacity { get; set; }

src/Box2D.NET/B2ArenaAllocators.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ public static void b2DestroyArenaAllocator(B2ArenaAllocator allocator)
2626
}
2727
}
2828

29-
public static B2ArenaAllocatorImpl<T> b2CreateArenaAllocator<T>(int capacity) where T : new()
29+
public static B2ArenaAllocatorTyped<T> b2CreateArenaAllocator<T>(int capacity) where T : new()
3030
{
3131
Debug.Assert(capacity >= 0);
32-
B2ArenaAllocatorImpl<T> allocatorImpl = new B2ArenaAllocatorImpl<T>();
32+
B2ArenaAllocatorTyped<T> allocatorImpl = new B2ArenaAllocatorTyped<T>();
3333
allocatorImpl.capacity = capacity;
3434
allocatorImpl.data = b2Alloc<T>(capacity);
3535
allocatorImpl.allocation = 0;
@@ -41,7 +41,7 @@ public static void b2DestroyArenaAllocator(B2ArenaAllocator allocator)
4141

4242
public static ArraySegment<T> b2AllocateArenaItem<T>(B2ArenaAllocator allocator, int size, string name) where T : new()
4343
{
44-
var alloc = allocator.GetOrCreateImpl<T>();
44+
var alloc = allocator.GetOrCreateFor<T>();
4545
// ensure allocation is 32 byte aligned to support 256-bit SIMD
4646
int size32 = ((size - 1) | 0x1F) + 1;
4747

@@ -77,7 +77,7 @@ public static void b2DestroyArenaAllocator(B2ArenaAllocator allocator)
7777

7878
public static void b2FreeArenaItem<T>(B2ArenaAllocator allocator, ArraySegment<T> mem) where T : new()
7979
{
80-
var alloc = allocator.GetOrCreateImpl<T>();
80+
var alloc = allocator.GetOrCreateFor<T>();
8181
int entryCount = alloc.entries.count;
8282
Debug.Assert(entryCount > 0);
8383
ref B2ArenaEntry<T> entry = ref alloc.entries.data[entryCount - 1];
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
using System.Threading;
2+
using System.Threading.Tasks;
3+
using NUnit.Framework;
4+
5+
namespace Box2D.NET.Test;
6+
7+
public class B2ArenaAllocatorTests
8+
{
9+
[Test]
10+
public void GetOrCreateFor_CreatesAllocatorForType()
11+
{
12+
// Arrange
13+
var arena = new B2ArenaAllocator(10);
14+
15+
// Act
16+
var typedAllocator = arena.GetOrCreateFor<int>();
17+
18+
// Assert
19+
Assert.That(typedAllocator, Is.Not.Null, "Allocator should be created for the specified type.");
20+
Assert.That(typedAllocator, Is.AssignableFrom<B2ArenaAllocatorTyped<int>>(), "Returned allocator should be of the correct type.");
21+
}
22+
23+
[Test]
24+
public void GetOrCreateFor_ReturnsSameAllocatorForSameType()
25+
{
26+
// Arrange
27+
var arena = new B2ArenaAllocator(10);
28+
29+
// Act
30+
var typedAllocator1 = arena.GetOrCreateFor<int>();
31+
var typedAllocator2 = arena.GetOrCreateFor<int>();
32+
33+
// Assert
34+
Assert.That(typedAllocator1, Is.SameAs(typedAllocator2), "Allocator should be reused for the same type.");
35+
}
36+
37+
[Test]
38+
public void GetOrCreateFor_AllocatesForDifferentTypes()
39+
{
40+
// Arrange
41+
var arena = new B2ArenaAllocator(10);
42+
43+
// Act
44+
IB2ArenaAllocatable typedAllocator1 = arena.GetOrCreateFor<int>();
45+
Assert.That(arena.Count, Is.EqualTo(1));
46+
47+
IB2ArenaAllocatable typedAllocator2 = arena.GetOrCreateFor<uint>();
48+
Assert.That(arena.Count, Is.EqualTo(2));
49+
50+
// Assert
51+
Assert.That(typedAllocator1, Is.Not.SameAs(typedAllocator2), "Different types should result in different allocators.");
52+
}
53+
54+
[Test]
55+
public void AsSpan_ReturnsAllocatorsSpan()
56+
{
57+
// Arrange
58+
var arena = new B2ArenaAllocator(10);
59+
IB2ArenaAllocatable first = arena.GetOrCreateFor<int>();
60+
IB2ArenaAllocatable second = arena.GetOrCreateFor<float>();
61+
IB2ArenaAllocatable third = arena.GetOrCreateFor<double>();
62+
63+
// Act
64+
var span = arena.AsSpan();
65+
66+
// Assert
67+
Assert.That(span.Length, Is.EqualTo(3), "Span should contain at least one allocator.");
68+
Assert.That(span[0], Is.EqualTo(first));
69+
Assert.That(span[1], Is.EqualTo(second));
70+
Assert.That(span[2], Is.EqualTo(third));
71+
}
72+
73+
[Test]
74+
public void GetOrCreateFor_ShouldBeThreadSafe_WhenCalledConcurrently()
75+
{
76+
// Arrange
77+
var arena = new B2ArenaAllocator(10);
78+
var tasks = new Task[100];
79+
var typedAllocators = new IB2ArenaAllocatable[100];
80+
var ce = new CountdownEvent(1);
81+
82+
// Act: Run 100 tasks concurrently
83+
for (int i = 0; i < 100; i++)
84+
{
85+
int idx = i;
86+
tasks[i] = Task.Run(() =>
87+
{
88+
ce.Wait(1000);
89+
// Multiple threads trying to get or create an allocator for MyType
90+
var allocator1 = arena.GetOrCreateFor<byte>();
91+
var allocator2 = arena.GetOrCreateFor<char>();
92+
var allocator3 = arena.GetOrCreateFor<short>();
93+
var allocator4 = arena.GetOrCreateFor<int>();
94+
var allocator5 = arena.GetOrCreateFor<long>();
95+
var allocator6 = arena.GetOrCreateFor<ushort>();
96+
var allocator7 = arena.GetOrCreateFor<uint>();
97+
var allocator8 = arena.GetOrCreateFor<ulong>();
98+
var allocator9 = arena.GetOrCreateFor<float>();
99+
var allocator10 = arena.GetOrCreateFor<double>();
100+
typedAllocators[idx] = allocator1;
101+
});
102+
}
103+
104+
ce.Signal();
105+
106+
107+
// Wait for all tasks to complete
108+
Task.WhenAll(tasks).Wait();
109+
110+
// Assert: Ensure all threads got the same allocator instance for MyType
111+
for (int i = 1; i < typedAllocators.Length; i++)
112+
{
113+
Assert.That(typedAllocators[i], Is.SameAs(typedAllocators[0]), "Allocator instance should be shared among threads.");
114+
}
115+
116+
Assert.That(arena.Count, Is.EqualTo(10));
117+
}
118+
}

0 commit comments

Comments
 (0)