Added UnsafeMultiHashMap

This commit is contained in:
2026-03-08 15:38:00 +09:00
parent 37d548085e
commit 21e755a56e
40 changed files with 619 additions and 156 deletions

View File

@@ -263,73 +263,6 @@ public unsafe class TestJobSystem
Assert.AreEqual(JobState.Completed, s_jobScheduler.GetJobStatus(completedHandle));
}
[TestMethod]
public void RaceConditionTest()
{
const int jobCount = 20000;
var pExecutedCount = (int*)NativeMemory.Alloc(sizeof(int));
*pExecutedCount = 0;
var startSignal = false;
// 1. Create a "Gatekeeper" vectorJob that spins/blocks a worker thread until signaled.
// This allows us to control exactly when the dependency completes.
var rootJob = new WaitJob { pSignal = &startSignal };
var rootHandle = s_jobScheduler.Schedule(ref rootJob);
// 2. Start a background task to flood the scheduler with dependencies on the Gatekeeper.
using var barrier = new Barrier(2);
var scheduleTask = Task.Run(() =>
{
var depJob = new IncrementJob { pCounter = pExecutedCount };
barrier.SignalAndWait(TestContext.CancellationTokenSource.Token); // Synchronize start with main thread
for (var i = 0; i < jobCount; i++)
{
// CONTENTION POINT:
// Trying to add a dependency to 'rootHandle'.
// Eventually, this will happen exactly while 'rootHandle' is transitioning to Completed.
s_jobScheduler.Schedule(ref depJob, rootHandle);
}
}, TestContext.CancellationTokenSource.Token);
barrier.SignalAndWait(TestContext.CancellationTokenSource.Token); // Wait for scheduler task to be ready
// Allow the scheduling loop to get a head start and queue some readers
Thread.Sleep(5);
// 3. Open the gate.
// This triggers the Gatekeeper to complete. It will change its State and iterate its dependency list.
// This happens CONCURRENTLY with the loop above adding more items to that same list.
startSignal = true;
scheduleTask.Wait(TestContext.CancellationTokenSource.Token);
// 4. Validate results
// If the lock-free logic works, every single dependent vectorJob must eventually execute.
// If there is a race (e.g., missed notification), pExecutedCount will stick below jobCount.
var spin = new SpinWait();
var timeout = DateTime.Now.AddSeconds(10);
while (Volatile.Read(ref *pExecutedCount) < jobCount)
{
if (DateTime.Now > timeout)
{
break;
}
spin.SpinOnce();
}
// Ensure the root vectorJob is officially cleaned up
s_jobScheduler.Wait(rootHandle);
Assert.AreEqual(jobCount, *pExecutedCount, "Race condition detected: Some dependent jobs failed to execute (Wait timeout).");
NativeMemory.Free(pExecutedCount);
}
[TestMethod]
public void SPMDCorrectness()
{