Skip to content

Commit 2d1a175

Browse files
committed
PR
1 parent 39ecea7 commit 2d1a175

File tree

7 files changed

+91
-38
lines changed

7 files changed

+91
-38
lines changed

src/MongoDB.Driver/Core/Clusters/Cluster.cs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,6 @@ private sealed class ServerSelectionWaitQueue : IDisposable
445445
private readonly InterlockedInt32 _rapidHeartbeatTimerCallbackState;
446446

447447
private int _serverSelectionWaitQueueSize;
448-
private bool _disposed;
449448

450449
public ServerSelectionWaitQueue(Cluster cluster)
451450
{
@@ -456,11 +455,7 @@ public ServerSelectionWaitQueue(Cluster cluster)
456455

457456
public void Dispose()
458457
{
459-
lock (_serverSelectionWaitQueueLock)
460-
{
461-
_disposed = true;
462-
_rapidHeartbeatTimer.Dispose();
463-
}
458+
_rapidHeartbeatTimer.Dispose();
464459
}
465460

466461
public IDisposable Enter(OperationContext operationContext, IServerSelector selector, ClusterDescription clusterDescription, long? operationId)
@@ -494,12 +489,14 @@ private void ExitServerSelectionWaitQueue()
494489
{
495490
if (--_serverSelectionWaitQueueSize == 0)
496491
{
497-
if (_disposed)
492+
try
498493
{
499-
return;
494+
_rapidHeartbeatTimer.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);
495+
}
496+
catch (ObjectDisposedException)
497+
{
498+
// Ignore ObjectDisposedException here, as ExitServerSelectionWaitQueue could be done after the WaitQueue was disposed.
500499
}
501-
502-
_rapidHeartbeatTimer.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);
503500
}
504501
}
505502
}

src/MongoDB.Driver/Core/Connections/TcpStreamFactory.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -165,14 +165,14 @@ private void ConfigureConnectedSocket(Socket socket)
165165

166166
private void Connect(Socket socket, EndPoint endPoint, CancellationToken cancellationToken)
167167
{
168-
var wasCallbackExecuted = false;
168+
var cancelledOrTimedOut = false;
169169
using var timeoutCancellationTokenSource = new CancellationTokenSource(_settings.ConnectTimeout);
170170
using var combinedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCancellationTokenSource.Token);
171171
using var cancellationSubscription = combinedCancellationTokenSource.Token.Register(() =>
172172
{
173173
try
174174
{
175-
wasCallbackExecuted = true;
175+
cancelledOrTimedOut = true;
176176
socket.Dispose();
177177
}
178178
catch
@@ -199,14 +199,15 @@ private void Connect(Socket socket, EndPoint endPoint, CancellationToken cancell
199199
}
200200
catch (Exception)
201201
{
202-
if (!wasCallbackExecuted)
202+
if (!cancelledOrTimedOut)
203203
{
204204
try
205205
{
206206
socket.Dispose();
207207
}
208-
catch (Exception)
208+
catch
209209
{
210+
// Ignore any exceptions. Connection was failed, we do not need the socket anyway.
210211
}
211212
}
212213

@@ -244,8 +245,8 @@ private async Task ConnectAsync(Socket socket, EndPoint endPoint, CancellationTo
244245
{
245246
try
246247
{
247-
socket.Dispose();
248248
connectTask.IgnoreExceptions();
249+
socket.Dispose();
249250
}
250251
catch { }
251252

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/* Copyright 2010-present MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System;
17+
using FluentAssertions;
18+
using MongoDB.TestHelpers.XunitExtensions.TimeoutEnforcing;
19+
20+
namespace MongoDB.Bson.Tests;
21+
22+
public class UnobservedTaskExceptionTracking
23+
{
24+
[UnobservedExceptionTrackingFact]
25+
public void EnsureNoUnobservedTaskException()
26+
{
27+
GC.Collect();
28+
GC.WaitForPendingFinalizers();
29+
30+
UnobservedExceptionTestDiscoverer.UnobservedExceptions.Should().BeEmpty();
31+
}
32+
}
33+

tests/MongoDB.Driver.Tests/Specifications/connection-monitoring-and-pooling/ConnectionMonitoringAndPoolingTestRunner.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,7 @@ private void ExecuteCheckOut(
387387
else
388388
{
389389
tasks[target] = CreateTask(() => CheckOut(operation, connectionPool, map));
390+
tasks[target].IgnoreExceptions();
390391
}
391392
}
392393
}

tests/MongoDB.Driver.Tests/UnobservedTaskExceptionTracking.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,22 @@
1616
using System;
1717
using FluentAssertions;
1818
using MongoDB.TestHelpers.XunitExtensions.TimeoutEnforcing;
19+
using Xunit;
1920

2021
namespace MongoDB.Driver.Tests;
2122

2223
public class UnobservedTaskExceptionTracking
2324
{
2425
[UnobservedExceptionTrackingFact]
25-
public void EnsureNoUnobservedTaskException()
26+
public void EnsureNoUnobservedTaskException() =>
27+
EnsureNoUnobservedTaskExceptionImpl();
28+
29+
[UnobservedExceptionTrackingFact]
30+
[Trait("Category", "Integration")]
31+
public void EnsureNoUnobservedTaskException_Integration() =>
32+
EnsureNoUnobservedTaskExceptionImpl();
33+
34+
private void EnsureNoUnobservedTaskExceptionImpl()
2635
{
2736
GC.Collect();
2837
GC.WaitForPendingFinalizers();

tests/MongoDB.TestHelpers/MongoDB.TestHelpers.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<Description>Helper classes applicable to all test projects.</Description>
1313
</PropertyGroup>
1414

15+
<!-- On event of debugging of UnobservedTaskExceptions uncomment the following lines to find which test causes the exceptions -->
1516
<PropertyGroup>
1617
<DefineConstants>$(DefineConstants);UNOBSERVED_TASK_EXCEPTION_DEBUGGING</DefineConstants>
1718
</PropertyGroup>

tests/MongoDB.TestHelpers/XunitExtensions/TimeoutEnforcing/TimeoutEnforcingTestInvoker.cs

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,6 @@ private async Task<decimal> InvokeBaseOnTaskScheduler(object testClassInstance)
8080

8181
protected override async Task<decimal> InvokeTestMethodAsync(object testClassInstance)
8282
{
83-
#if UNOBSERVED_TASK_EXCEPTION_DEBUGGING
84-
Exception unobservedException = null;
85-
#endif
8683
var xUnitTestCase = Test.TestCase as IXunitTestCase;
8784
var timeoutMS = xUnitTestCase?.Timeout ?? 0;
8885
var timeout = Debugger.IsAttached
@@ -93,26 +90,11 @@ protected override async Task<decimal> InvokeTestMethodAsync(object testClassIns
9390
var testExceptionHandler = testClassInstance as ITestExceptionHandler;
9491

9592
decimal result;
93+
using var unobservedExceptionDebugger = UnobservedExceptionDebugger.Create();
9694
try
9795
{
98-
#if UNOBSERVED_TASK_EXCEPTION_DEBUGGING
99-
TaskScheduler.UnobservedTaskException += UnobservedTaskExceptionEventHandler;
100-
#endif
101-
10296
var baseTask = InvokeBaseOnTaskScheduler(testClassInstance);
10397
var resultTask = await Task.WhenAny(baseTask, Task.Delay(timeout));
104-
105-
#if UNOBSERVED_TASK_EXCEPTION_DEBUGGING
106-
GC.Collect();
107-
GC.WaitForPendingFinalizers();
108-
TaskScheduler.UnobservedTaskException -= UnobservedTaskExceptionEventHandler;
109-
110-
if (unobservedException != null)
111-
{
112-
throw unobservedException;
113-
}
114-
#endif
115-
11698
if (resultTask != baseTask)
11799
{
118100
throw new TestTimeoutException((int)timeout.TotalMilliseconds);
@@ -138,13 +120,42 @@ protected override async Task<decimal> InvokeTestMethodAsync(object testClassIns
138120
}
139121

140122
return result;
123+
}
141124

142-
#if UNOBSERVED_TASK_EXCEPTION_DEBUGGING
143-
void UnobservedTaskExceptionEventHandler(object sender, UnobservedTaskExceptionEventArgs unobservedExceptionArgs)
125+
private class UnobservedExceptionDebugger : IDisposable
126+
{
127+
private Exception _unobservedException;
128+
129+
private UnobservedExceptionDebugger()
144130
{
145-
unobservedException = unobservedExceptionArgs.Exception;
131+
TaskScheduler.UnobservedTaskException += UnobservedTaskExceptionEventHandler;
146132
}
133+
134+
public static UnobservedExceptionDebugger Create()
135+
{
136+
#if UNOBSERVED_TASK_EXCEPTION_DEBUGGING
137+
return new UnobservedExceptionDebugger();
138+
#else
139+
return null;
147140
#endif
141+
}
142+
143+
public void Dispose()
144+
{
145+
GC.Collect();
146+
GC.WaitForPendingFinalizers();
147+
TaskScheduler.UnobservedTaskException -= UnobservedTaskExceptionEventHandler;
148+
149+
if (_unobservedException != null)
150+
{
151+
throw _unobservedException;
152+
}
153+
}
154+
155+
private void UnobservedTaskExceptionEventHandler(object sender, UnobservedTaskExceptionEventArgs unobservedExceptionArgs)
156+
{
157+
_unobservedException = unobservedExceptionArgs.Exception;
158+
}
148159
}
149160
}
150161
}

0 commit comments

Comments
 (0)