Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion TUnit.Core/Tracking/ObjectTracker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Exception> s_asyncCallbackErrors = new();
Expand Down
22 changes: 11 additions & 11 deletions TUnit.Engine/Scheduling/ConstraintKeyScheduler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,18 @@ public async ValueTask ExecuteTestsWithConstraintsAsync(
// Track which constraint keys are currently in use
var lockedKeys = new HashSet<string>();
var lockObject = new object();

// Queue for tests waiting for their constraint keys to become available
var waitingTests = new ConcurrentQueue<(AbstractExecutableTest Test, IReadOnlyList<string> ConstraintKeys, TaskCompletionSource<bool> StartSignal)>();

// Active test tasks
var activeTasks = new List<Task>();

// Process each test
foreach (var (test, constraintKeys, _) in sortedTests)
{
var startSignal = new TaskCompletionSource<bool>();

bool canStart;
lock (lockObject)
{
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -207,15 +207,15 @@ 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);
testToStart.StartSignal.SetResult(true);
}
}
}
}
}
4 changes: 2 additions & 2 deletions TUnit.Engine/Services/AfterHookPairTracker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ internal sealed class AfterHookPairTracker
private readonly ThreadSafeDictionary<Type, Task<List<Exception>>> _afterClassTasks = new();
private readonly ThreadSafeDictionary<Assembly, Task<List<Exception>>> _afterAssemblyTasks = new();
private Task<List<Exception>>? _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<CancellationTokenRegistration> _registrations = [];
Expand Down
4 changes: 2 additions & 2 deletions TUnit.Engine/Services/BeforeHookTaskCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ internal sealed class BeforeHookTaskCache
private readonly ThreadSafeDictionary<Type, Task> _beforeClassTasks = new();
private readonly ThreadSafeDictionary<Assembly, Task> _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<CancellationToken, ValueTask> taskFactory, CancellationToken cancellationToken)
{
Expand Down
2 changes: 1 addition & 1 deletion TUnit.Engine/Services/TestDependencyResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ internal sealed class TestDependencyResolver
private readonly HashSet<AbstractExecutableTest> _testsBeingResolved =
[
];
private readonly object _resolutionLock = new();
private readonly Lock _resolutionLock = new();

public void RegisterTest(AbstractExecutableTest test)
{
Expand Down
Loading