feat(memory): refactor allocation and add new queue

Refactored memory management by removing safety checks and introducing `MemoryHandle` for centralized tracking. Simplified allocation logic across allocators and enhanced `Dispose` methods for better resource cleanup.

Added `UnsafeChunkedQueue<T>`, a lock-free, dynamically resizing queue with chunk-based memory management, supporting parallel producers and consumers.

Updated unit tests to validate new queue functionality and ensure compatibility with refactored memory logic. Incremented assembly version to 1.6.12.

BREAKING CHANGE: Removed `#if MHP_ENABLE_SAFETY_CHECKS` blocks, altering memory validation behavior.
This commit is contained in:
2026-04-10 14:44:48 +09:00
parent dea8de60d0
commit a0deadc363
25 changed files with 647 additions and 456 deletions

View File

@@ -63,11 +63,9 @@ public class TestAllocationManager
var ptr1 = new MemoryBlock(1024, 8, scope.AllocationHandle);
Assert.IsTrue(ptr1.IsCreated);
Assert.AreEqual(1024u, ((VirtualStack*)scope.AllocationHandle.State)->Allocated);
ptr1.Dispose();
scope.Dispose();
Assert.AreEqual(0u, ((VirtualStack*)scope.AllocationHandle.State)->Allocated);
});
thread.Start();
@@ -76,10 +74,10 @@ public class TestAllocationManager
var ptr2 = new MemoryBlock(1024, 8, scope.AllocationHandle);
Assert.IsTrue(ptr2.IsCreated);
Assert.AreEqual(1024u, ((VirtualStack*)scope.AllocationHandle.State)->Allocated);
ptr2.Dispose();
scope.Dispose();
Assert.AreEqual(0u, ((VirtualStack*)scope.AllocationHandle.State)->Allocated);
thread.Join();
}
}

View File

@@ -186,7 +186,6 @@ public unsafe class TestArena
}
Assert.IsTrue(arena.Buffer == null);
Assert.AreEqual(0u, (uint)arena.Size);
Assert.IsNull(arena.Allocate(8, 8, AllocationOption.None));
arena.Dispose();
}

View File

@@ -144,22 +144,6 @@ public unsafe class TestStack
Assert.IsNull(stack.Buffer);
}
#if MHP_ENABLE_SAFETY_CHECKS
[TestMethod]
public void Stack_AllocationFailsOutsideScope()
{
var stack = new Stack(128 * 1024);
try
{
Assert.ThrowsExactly<InvalidOperationException>(() => stack.Allocate(32, 8, AllocationOption.Clear));
}
finally
{
stack.Dispose();
}
}
#endif
[TestMethod]
public void Stack_InvalidAlignment_Throws()
{

View File

@@ -145,25 +145,6 @@ public unsafe class TestVirtualStack
Assert.IsNull(stack.Buffer);
}
#if MHP_ENABLE_SAFETY_CHECKS
[TestMethod]
public void VirtualStack_AllocationFailsOutsideScope()
{
var stack = new VirtualStack(128 * 1024);
try
{
Assert.ThrowsExactly<InvalidOperationException>(() => stack.Allocate(32, 8, AllocationOption.Clear));
stack.Dispose();
Assert.IsTrue(stack.Buffer == null);
}
finally
{
stack.Dispose();
}
}
#endif
[TestMethod]
public void VirtualStack_InvalidAlignment_Throws()
{

View File

@@ -0,0 +1,94 @@
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
namespace Misaki.HighPerformance.Test.UnitTest.Collections;
[TestClass]
public class TestUnsafeChunkedQueue
{
public TestContext TestContext
{
get;
set;
}
[TestMethod]
public void BasicEnqueueDequeueTest()
{
using var queue = new UnsafeChunkedQueue<int>(32, Allocator.Persistent);
Assert.IsTrue(queue.IsCreated);
queue.Enqueue(10);
queue.Enqueue(20);
Assert.IsTrue(queue.TryDequeue(out var val1));
Assert.AreEqual(10, val1);
Assert.IsTrue(queue.TryDequeue(out var val2));
Assert.AreEqual(20, val2);
Assert.IsFalse(queue.TryDequeue(out _));
}
[TestMethod]
public void ChunkExpansionTest()
{
// Force chunk expansions by enqueuing more than the chunk capacity
using var queue = new UnsafeChunkedQueue<int>(16, Allocator.Persistent);
var totalItems = 100;
for (var i = 0; i < totalItems; i++)
{
queue.Enqueue(i);
}
for (var i = 0; i < totalItems; i++)
{
Assert.IsTrue(queue.TryDequeue(out var val));
Assert.AreEqual(i, val);
}
Assert.IsFalse(queue.TryDequeue(out _));
}
[TestMethod]
public void ConcurrentEnqueueDequeueTest()
{
// Multi-threaded stress test to verify lock-free safety and chunk caching
using var queue = new UnsafeChunkedQueue<int>(64, Allocator.Persistent);
var totalElements = 100_000;
var enqueueTask = Task.Run(() =>
{
Parallel.For(0, totalElements, i =>
{
queue.Enqueue(i);
});
}, TestContext.CancellationToken);
long sum = 0;
var count = 0;
var dequeueTask = Task.Run(() =>
{
while (Volatile.Read(ref count) < totalElements)
{
if (queue.TryDequeue(out var val))
{
Interlocked.Add(ref sum, val);
Interlocked.Increment(ref count);
}
}
}, TestContext.CancellationToken);
Task.WaitAll(enqueueTask, dequeueTask);
Assert.AreEqual(totalElements, count);
// Sum of 0..N-1 is N * (N - 1) / 2
var expectedSum = (long)totalElements * (totalElements - 1) / 2;
Assert.AreEqual(expectedSum, sum);
}
}

View File

@@ -17,10 +17,7 @@ public class TestUnsafeHashSet
[TestCleanup]
public void Cleanup()
{
if (_set.IsCreated)
{
_set.Dispose();
}
_set.Dispose();
}
[TestMethod]