Skip to content

Commit 51b39ca

Browse files
committed
CSHARP-3884: Driver calls SemaphoreSlim.WaitAsync with negative timeout values
1 parent a7f8afe commit 51b39ca

File tree

2 files changed

+49
-5
lines changed

2 files changed

+49
-5
lines changed

src/MongoDB.Driver.Core/Core/ConnectionPools/ExclusiveConnectionPool.Helpers.cs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ private static class State
6464
public const int Disposed = 2;
6565
}
6666

67-
private sealed class AcquireConnectionHelper
67+
internal sealed class AcquireConnectionHelper
6868
{
6969
// private fields
7070
private readonly ExclusiveConnectionPool _pool;
@@ -106,8 +106,8 @@ public IConnectionHandle EnteredPool(bool enteredPool, CancellationToken cancell
106106

107107
if (enteredPool)
108108
{
109-
var timeSpentInWaitQueue = _stopwatch.Elapsed;
110-
using (var connectionCreator = new ConnectionCreator(_pool, _pool._settings.WaitQueueTimeout - timeSpentInWaitQueue))
109+
var timeout = EnsureTimeout();
110+
using (var connectionCreator = new ConnectionCreator(_pool, timeout))
111111
{
112112
connection = connectionCreator.CreateOpenedOrReuse(cancellationToken);
113113
}
@@ -123,8 +123,8 @@ public async Task<IConnectionHandle> EnteredPoolAsync(bool enteredPool, Cancella
123123

124124
if (enteredPool)
125125
{
126-
var timeSpentInWaitQueue = _stopwatch.Elapsed;
127-
using (var connectionCreator = new ConnectionCreator(_pool, _pool._settings.WaitQueueTimeout - timeSpentInWaitQueue))
126+
var timeout = EnsureTimeout();
127+
using (var connectionCreator = new ConnectionCreator(_pool, timeout))
128128
{
129129
connection = await connectionCreator.CreateOpenedOrReuseAsync(cancellationToken).ConfigureAwait(false);
130130
}
@@ -154,6 +154,19 @@ private AcquiredConnection FinalizePoolEnterance(PooledConnection pooledConnecti
154154
}
155155
}
156156

157+
private TimeSpan EnsureTimeout()
158+
{
159+
var timeSpentInWaitQueue = _stopwatch.Elapsed;
160+
var timeout = _pool._settings.WaitQueueTimeout - timeSpentInWaitQueue;
161+
162+
if (timeout <= TimeSpan.Zero)
163+
{
164+
throw _pool.CreateTimeoutException(_stopwatch, $"Timed out waiting for a connection after {timeSpentInWaitQueue.TotalMilliseconds}ms.");
165+
}
166+
167+
return timeout;
168+
}
169+
157170
public void Finally()
158171
{
159172
if (_enteredWaitQueue)

tests/MongoDB.Driver.Core.Tests/Core/ConnectionPools/ExclusiveConnectionPoolTests.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,37 @@ public void AquireConnection_should_timeout_when_no_sufficient_reused_connection
679679
subject.PendingCount.Should().Be(0);
680680
}
681681

682+
[Theory]
683+
[ParameterAttributeData]
684+
public async Task AcquireConnectionHelper_EnteredPool_should_not_create_connection_if_timed_out([Values(true, false)] bool async)
685+
{
686+
var settings = _settings.With(
687+
maintenanceInterval: Timeout.InfiniteTimeSpan,
688+
minConnections: 0,
689+
waitQueueSize: 1,
690+
waitQueueTimeout: TimeSpan.FromMilliseconds(2));
691+
692+
var mockConnectionFactory = new Mock<IConnectionFactory>();
693+
using var pool = CreateSubject(settings, mockConnectionFactory.Object);
694+
695+
var acquireConnectionHelper = new ExclusiveConnectionPool.AcquireConnectionHelper(pool);
696+
697+
acquireConnectionHelper.EnterWaitQueue();
698+
await Task.Delay(10);
699+
700+
Exception exception;
701+
if (async)
702+
{
703+
exception = await Record.ExceptionAsync(() => acquireConnectionHelper.EnteredPoolAsync(true, default));
704+
}
705+
else
706+
{
707+
exception = Record.Exception(() => acquireConnectionHelper.EnteredPool(true, default));
708+
}
709+
710+
exception.Should().BeOfType<TimeoutException>();
711+
}
712+
682713
[Fact]
683714
public void Clear_should_throw_an_InvalidOperationException_if_not_initialized()
684715
{

0 commit comments

Comments
 (0)