Skip to content

Commit f4fd11d

Browse files
committed
CSHARP-3978: CancellationTokenSource dispose in SemaphoreSlimSignalable and MaintenanceHelper
1 parent 8278e2b commit f4fd11d

File tree

2 files changed

+66
-22
lines changed

2 files changed

+66
-22
lines changed

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ public void Cancel()
183183
return;
184184
}
185185

186-
_cancellationTokenSource?.Cancel();
186+
CancelAndDispose();
187187
_cancellationTokenSource = null;
188188
_maintenanceTask = null;
189189
}
@@ -195,14 +195,20 @@ public void Start()
195195
return;
196196
}
197197

198-
_cancellationTokenSource?.Cancel();
198+
CancelAndDispose();
199199
_cancellationTokenSource = new CancellationTokenSource();
200+
var cancellationToken = _cancellationTokenSource.Token;
200201

201-
_maintenanceTask = Task.Run(() => _maintenanceTaskCreator(_cancellationTokenSource.Token), _cancellationTokenSource.Token);
202+
_maintenanceTask = Task.Run(() => _maintenanceTaskCreator(cancellationToken), cancellationToken);
202203
_maintenanceTask.ConfigureAwait(false);
203204
}
204205

205206
public void Dispose()
207+
{
208+
CancelAndDispose();
209+
}
210+
211+
private void CancelAndDispose()
206212
{
207213
_cancellationTokenSource?.Cancel();
208214
_cancellationTokenSource?.Dispose();

src/MongoDB.Driver.Core/Core/Misc/SemaphoreSlimSignalable.cs

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,28 @@ public enum SemaphoreWaitResult
2929
Entered
3030
}
3131

32+
private readonly struct CancellationContext : IDisposable
33+
{
34+
public bool IsSignaled { get; }
35+
public CancellationTokenSource LinkedCancellationTokenSource { get; }
36+
public CancellationTokenSource SignalCancellationTokenSource { get; }
37+
38+
public static CancellationContext Signaled { get; } = new CancellationContext(true, default, default);
39+
40+
public CancellationContext(
41+
bool isSignaled,
42+
CancellationTokenSource linkedCancellationTokenSource,
43+
CancellationTokenSource signaledCancellationTokenSource)
44+
{
45+
IsSignaled = isSignaled;
46+
LinkedCancellationTokenSource = linkedCancellationTokenSource;
47+
SignalCancellationTokenSource = signaledCancellationTokenSource;
48+
}
49+
50+
public CancellationToken CancellationToken => LinkedCancellationTokenSource.Token;
51+
public void Dispose() => LinkedCancellationTokenSource?.Dispose();
52+
}
53+
3254
public sealed class SemaphoreSlimSignalableAwaiter : IDisposable
3355
{
3456
private readonly SemaphoreSlimSignalable _semaphoreSlimSignalable;
@@ -91,6 +113,7 @@ public void Reset()
91113
{
92114
if (_signalCancellationTokenSource.IsCancellationRequested)
93115
{
116+
_signalCancellationTokenSource.Dispose();
94117
_signalCancellationTokenSource = new CancellationTokenSource();
95118
}
96119
}
@@ -111,21 +134,21 @@ public async Task<SemaphoreWaitResult> WaitAsync(TimeSpan timeout, CancellationT
111134

112135
public SemaphoreWaitResult WaitSignaled(TimeSpan timeout, CancellationToken cancellationToken)
113136
{
114-
var (tokenSourceLinked, signalTokenSource, signaled) = GetLinkedTokenAndCheckForSignaled(cancellationToken);
137+
using var cancellationContext = GetCancellationTokenContext(cancellationToken);
115138

116-
if (signaled)
139+
if (cancellationContext.IsSignaled)
117140
{
118141
return SemaphoreWaitResult.Signaled;
119142
}
120143

121144
try
122145
{
123-
var entered = _semaphore.Wait(timeout, tokenSourceLinked.Token);
146+
var entered = _semaphore.Wait(timeout, cancellationContext.CancellationToken);
124147
return entered ? SemaphoreWaitResult.Entered : SemaphoreWaitResult.TimedOut;
125148
}
126149
catch (OperationCanceledException)
127150
{
128-
if (IsSignaled(signalTokenSource.Token, cancellationToken))
151+
if (IsSignaled(cancellationContext.SignalCancellationTokenSource, cancellationToken))
129152
{
130153
return SemaphoreWaitResult.Signaled;
131154
}
@@ -136,22 +159,22 @@ public SemaphoreWaitResult WaitSignaled(TimeSpan timeout, CancellationToken canc
136159

137160
public async Task<SemaphoreWaitResult> WaitSignaledAsync(TimeSpan timeout, CancellationToken cancellationToken)
138161
{
139-
var (tokenSourceLinked, signalTokenSource, signaled) = GetLinkedTokenAndCheckForSignaled(cancellationToken);
162+
using var cancellationContext = GetCancellationTokenContext(cancellationToken);
140163

141-
if (signaled)
164+
if (cancellationContext.IsSignaled)
142165
{
143166
return SemaphoreWaitResult.Signaled;
144167
}
145168

146169
try
147170
{
148-
var entered = await _semaphore.WaitAsync(timeout, tokenSourceLinked.Token).ConfigureAwait(false);
171+
var entered = await _semaphore.WaitAsync(timeout, cancellationContext.CancellationToken).ConfigureAwait(false);
149172

150173
return entered ? SemaphoreWaitResult.Entered : SemaphoreWaitResult.TimedOut;
151174
}
152175
catch (OperationCanceledException)
153176
{
154-
if (IsSignaled(signalTokenSource.Token, cancellationToken))
177+
if (IsSignaled(cancellationContext.SignalCancellationTokenSource, cancellationToken))
155178
{
156179
// Request task rescheduling, to avoid resuming execution on Signal thread
157180
await TaskExtensions.YieldNoContext();
@@ -174,31 +197,46 @@ public void Release()
174197
public void Dispose()
175198
{
176199
_semaphore.Dispose();
200+
_signalCancellationTokenSource.Dispose();
177201
}
178202

179-
private (CancellationTokenSource TokenSourceLinked, CancellationTokenSource SignalTokenSource, bool Signaled) GetLinkedTokenAndCheckForSignaled(CancellationToken cancellationToken)
203+
private CancellationContext GetCancellationTokenContext(CancellationToken cancellationToken)
180204
{
181205
var signalTokenSource = _signalCancellationTokenSource;
182206

183-
if (IsSignaled(signalTokenSource.Token, cancellationToken))
207+
if (IsSignaled(signalTokenSource, cancellationToken))
184208
{
185-
return (default, default, true);
209+
return CancellationContext.Signaled;
186210
}
187211

188-
var tokenSourceLinked = CancellationTokenSource.CreateLinkedTokenSource(
189-
signalTokenSource.Token,
190-
cancellationToken);
212+
try
213+
{
214+
var cancellationLinkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(
215+
signalTokenSource.Token,
216+
cancellationToken);
191217

192-
return (tokenSourceLinked, signalTokenSource, false);
218+
return new CancellationContext(false, cancellationLinkedTokenSource, signalTokenSource);
219+
}
220+
catch (ObjectDisposedException)
221+
{
222+
// signalTokenSource was disposed, it will happen only when cancellation was requested for signalTokenSource or on Dispose
223+
return CancellationContext.Signaled;
224+
}
193225
}
194226

195-
#pragma warning disable CA1068 // CancellationToken parameters must come last
196-
private bool IsSignaled(CancellationToken signalToken, CancellationToken cancellationToken)
197-
#pragma warning restore CA1068 // CancellationToken parameters must come last
227+
private bool IsSignaled(CancellationTokenSource signalTokenSource, CancellationToken cancellationToken)
198228
{
199229
cancellationToken.ThrowIfCancellationRequested();
200230

201-
return signalToken.IsCancellationRequested;
231+
try
232+
{
233+
return signalTokenSource.Token.IsCancellationRequested;
234+
}
235+
catch (ObjectDisposedException)
236+
{
237+
// signalTokenSource was disposed, it will happen only when cancellation was requested for signalTokenSource or on Dispose
238+
return true;
239+
}
202240
}
203241
}
204242
}

0 commit comments

Comments
 (0)