Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,6 @@ out DbConnectionInternal? recycledConnection
}

[Fact]
[ActiveIssue("https://github.com/dotnet/SqlClient/issues/3730")]
public async Task GetConnectionMaxPoolSize_ShouldRespectOrderOfRequest()
{
// Arrange
Expand All @@ -308,29 +307,31 @@ out DbConnectionInternal? internalConnection
Assert.NotNull(internalConnection);
}

// Use ManualResetEventSlim to synchronize the tasks
// and force the request queueing order.
using ManualResetEventSlim mresQueueOrder = new();
using CountdownEvent allRequestsQueued = new(2);
// Use multiple ManualResetEventSlim to ensure proper ordering
using ManualResetEventSlim firstTaskReady = new(false);
using ManualResetEventSlim secondTaskReady = new(false);
using ManualResetEventSlim startRequests = new(false);

// Act
var recycledTask = Task.Run(() =>
{
mresQueueOrder.Set();
allRequestsQueued.Signal();
firstTaskReady.Set();
startRequests.Wait();
pool.TryGetConnection(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think making these async introduced a deadlock. In some conditions, they'll hang on to threads and prevent future async operations from going through. I'm going to revert these changes other than the SpinWait -> Thread.Sleep()

new SqlConnection(""),
new SqlConnection("Timeout=5000"),
null,
new DbConnectionOptions("", null),
out DbConnectionInternal? recycledConnection
);
return recycledConnection;
});

var failedTask = Task.Run(() =>
{
// Force this request to be second in the queue.
mresQueueOrder.Wait();
allRequestsQueued.Signal();
secondTaskReady.Set();
startRequests.Wait();
// Add a small delay to ensure this request comes after the first
Thread.Sleep(50);
pool.TryGetConnection(
new SqlConnection("Timeout=1"),
null,
Expand All @@ -340,7 +341,20 @@ out DbConnectionInternal? failedConnection
return failedConnection;
});

allRequestsQueued.Wait();
// Wait for both tasks to be ready before starting the requests
firstTaskReady.Wait();
secondTaskReady.Wait();

// Use SpinWait to ensure both tasks are actually waiting
Copy link

Copilot AI Nov 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment mentions "Use SpinWait" but the code actually uses Thread.Sleep(100) instead of SpinWait. Consider either:

  1. Updating the comment to accurately reflect the synchronization mechanism: // Wait briefly to ensure both tasks are waiting on startRequests
  2. Or using an actual SpinWait if that's the intended approach

This comment-code mismatch could confuse future maintainers.

Suggested change
// Use SpinWait to ensure both tasks are actually waiting
// Wait briefly to ensure both tasks are waiting on startRequests

Copilot uses AI. Check for mistakes.
Thread.Sleep(100);

// Start both requests
startRequests.Set();

// Give time for both requests to be queued
await Task.Delay(200);

// Return the connection which should satisfy the first queued request
pool.ReturnInternalConnection(firstConnection!, firstOwningConnection);
var recycledConnection = await recycledTask;

Expand All @@ -350,7 +364,6 @@ out DbConnectionInternal? failedConnection
}

[Fact]
[ActiveIssue("https://github.com/dotnet/SqlClient/issues/3730")]
public async Task GetConnectionAsyncMaxPoolSize_ShouldRespectOrderOfRequest()
{
// Arrange
Expand Down Expand Up @@ -382,14 +395,14 @@ out DbConnectionInternal? internalConnection

// Act
var exceeded = pool.TryGetConnection(
new SqlConnection(""),
new SqlConnection("Timeout=5000"),
recycledTaskCompletionSource,
new DbConnectionOptions("", null),
out DbConnectionInternal? recycledConnection
);

// Gives time for the recycled connection to be queued before the failed request is initiated.
await Task.Delay(1000);
// Ensure sufficient time for the recycled connection request to be fully queued
await Task.Delay(200);

var exceeded2 = pool.TryGetConnection(
new SqlConnection("Timeout=1"),
Expand All @@ -398,6 +411,9 @@ out DbConnectionInternal? recycledConnection
out DbConnectionInternal? failedConnection
);

// Ensure the second request is also queued
await Task.Delay(100);

pool.ReturnInternalConnection(firstConnection!, firstOwningConnection);
recycledConnection = await recycledTaskCompletionSource.Task;

Expand Down
Loading