diff --git a/TUnit.Core/Tracking/ObjectTracker.cs b/TUnit.Core/Tracking/ObjectTracker.cs index 9b5780b59a..ed85d31eba 100644 --- a/TUnit.Core/Tracking/ObjectTracker.cs +++ b/TUnit.Core/Tracking/ObjectTracker.cs @@ -21,7 +21,7 @@ internal class ObjectTracker(TrackableObjectGraphProvider trackableObjectGraphPr new(Helpers.ReferenceEqualityComparer.Instance); // Lock for atomic decrement-check-dispose operations to prevent race conditions - private static readonly object s_disposalLock = new(); + private static readonly Lock s_disposalLock = new(); // Collects errors from async disposal callbacks for post-session review private static readonly ConcurrentBag s_asyncCallbackErrors = new(); diff --git a/TUnit.Engine/Scheduling/ConstraintKeyScheduler.cs b/TUnit.Engine/Scheduling/ConstraintKeyScheduler.cs index 47c1b88dd6..c3ae0b7eb9 100644 --- a/TUnit.Engine/Scheduling/ConstraintKeyScheduler.cs +++ b/TUnit.Engine/Scheduling/ConstraintKeyScheduler.cs @@ -40,10 +40,10 @@ public async ValueTask ExecuteTestsWithConstraintsAsync( // Track which constraint keys are currently in use var lockedKeys = new HashSet(); var lockObject = new object(); - + // Queue for tests waiting for their constraint keys to become available var waitingTests = new ConcurrentQueue<(AbstractExecutableTest Test, IReadOnlyList ConstraintKeys, TaskCompletionSource StartSignal)>(); - + // Active test tasks var activeTasks = new List(); @@ -51,7 +51,7 @@ public async ValueTask ExecuteTestsWithConstraintsAsync( foreach (var (test, constraintKeys, _) in sortedTests) { var startSignal = new TaskCompletionSource(); - + bool canStart; lock (lockObject) { @@ -82,7 +82,7 @@ public async ValueTask ExecuteTestsWithConstraintsAsync( // Start the test immediately await _logger.LogDebugAsync($"Starting test {test.TestId} with constraint keys: {string.Join(", ", constraintKeys)}").ConfigureAwait(false); startSignal.SetResult(true); - + var testTask = ExecuteTestAndReleaseKeysAsync(test, constraintKeys, lockedKeys, lockObject, waitingTests, cancellationToken); test.ExecutionTask = testTask; activeTasks.Add(testTask); @@ -92,7 +92,7 @@ public async ValueTask ExecuteTestsWithConstraintsAsync( // Queue the test to wait for its keys await _logger.LogDebugAsync($"Queueing test {test.TestId} waiting for constraint keys: {string.Join(", ", constraintKeys)}").ConfigureAwait(false); waitingTests.Enqueue((test, constraintKeys, startSignal)); - + var testTask = WaitAndExecuteTestAsync(test, constraintKeys, startSignal, lockedKeys, lockObject, waitingTests, cancellationToken); test.ExecutionTask = testTask; activeTasks.Add(testTask); @@ -117,9 +117,9 @@ private async Task WaitAndExecuteTestAsync( { // Wait for signal to start await startSignal.Task.ConfigureAwait(false); - + await _logger.LogDebugAsync($"Starting previously queued test {test.TestId} with constraint keys: {string.Join(", ", constraintKeys)}").ConfigureAwait(false); - + await ExecuteTestAndReleaseKeysAsync(test, constraintKeys, lockedKeys, lockObject, waitingTests, cancellationToken).ConfigureAwait(false); } @@ -168,7 +168,7 @@ private async Task ExecuteTestAndReleaseKeysAsync( } // Check waiting tests to see if any can now run - + while (waitingTests.TryDequeue(out var waitingTest)) { // Check if all constraint keys are available for this waiting test - manual loop avoids LINQ allocation @@ -207,10 +207,10 @@ private async Task ExecuteTestAndReleaseKeysAsync( waitingTests.Enqueue(waitingTestItem); } } - + // Log and signal tests to start outside the lock await _logger.LogDebugAsync($"Released constraint keys for test {test.TestId}: {string.Join(", ", constraintKeys)}").ConfigureAwait(false); - + foreach (var testToStart in testsToStart) { await _logger.LogDebugAsync($"Unblocking waiting test {testToStart.Test.TestId} with constraint keys: {string.Join(", ", testToStart.ConstraintKeys)}").ConfigureAwait(false); @@ -218,4 +218,4 @@ private async Task ExecuteTestAndReleaseKeysAsync( } } } -} \ No newline at end of file +} diff --git a/TUnit.Engine/Services/AfterHookPairTracker.cs b/TUnit.Engine/Services/AfterHookPairTracker.cs index 5e6e12deb5..23a35254d2 100644 --- a/TUnit.Engine/Services/AfterHookPairTracker.cs +++ b/TUnit.Engine/Services/AfterHookPairTracker.cs @@ -17,8 +17,8 @@ internal sealed class AfterHookPairTracker private readonly ThreadSafeDictionary>> _afterClassTasks = new(); private readonly ThreadSafeDictionary>> _afterAssemblyTasks = new(); private Task>? _afterTestSessionTask; - private readonly object _testSessionLock = new(); - private readonly object _classLock = new(); + private readonly Lock _testSessionLock = new(); + private readonly Lock _classLock = new(); // Track cancellation registrations for cleanup private readonly ConcurrentBag _registrations = []; diff --git a/TUnit.Engine/Services/BeforeHookTaskCache.cs b/TUnit.Engine/Services/BeforeHookTaskCache.cs index 1a23fe93af..b906782df6 100644 --- a/TUnit.Engine/Services/BeforeHookTaskCache.cs +++ b/TUnit.Engine/Services/BeforeHookTaskCache.cs @@ -14,8 +14,8 @@ internal sealed class BeforeHookTaskCache private readonly ThreadSafeDictionary _beforeClassTasks = new(); private readonly ThreadSafeDictionary _beforeAssemblyTasks = new(); private Task? _beforeTestSessionTask; - private readonly object _testSessionLock = new(); - private readonly object _classLock = new(); + private readonly Lock _testSessionLock = new(); + private readonly Lock _classLock = new(); public ValueTask GetOrCreateBeforeTestSessionTask(Func taskFactory, CancellationToken cancellationToken) { diff --git a/TUnit.Engine/Services/TestDependencyResolver.cs b/TUnit.Engine/Services/TestDependencyResolver.cs index 08c76f3964..36865023c7 100644 --- a/TUnit.Engine/Services/TestDependencyResolver.cs +++ b/TUnit.Engine/Services/TestDependencyResolver.cs @@ -17,7 +17,7 @@ internal sealed class TestDependencyResolver private readonly HashSet _testsBeingResolved = [ ]; - private readonly object _resolutionLock = new(); + private readonly Lock _resolutionLock = new(); public void RegisterTest(AbstractExecutableTest test) {