Skip to content

Commit 564a857

Browse files
committed
Ensures proper disposal of cancellation token sources
Ensures that cancellation token sources created for timeouts are properly disposed of, preventing potential resource leaks.
1 parent 59cdbfe commit 564a857

1 file changed

Lines changed: 43 additions & 30 deletions

File tree

src/Foundatio/Resilience/ResiliencePolicy.cs

Lines changed: 43 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -96,49 +96,62 @@ public async ValueTask<T> ExecuteAsync<T>(Func<CancellationToken, ValueTask<T>>
9696
var startTime = _timeProvider.GetUtcNow();
9797
var linkedCancellationToken = cancellationToken;
9898
var timeoutToken = CancellationToken.None;
99+
CancellationTokenSource timeoutCts = null;
100+
CancellationTokenSource linkedCts = null;
101+
99102
if (Timeout > TimeSpan.Zero)
100103
{
101-
timeoutToken = new CancellationTokenSource(Timeout).Token;
102-
linkedCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutToken).Token;
104+
timeoutCts = new CancellationTokenSource(Timeout);
105+
timeoutToken = timeoutCts.Token;
106+
linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutToken);
107+
linkedCancellationToken = linkedCts.Token;
103108
}
104109

105-
do
110+
try
106111
{
107-
try
108-
{
109-
if (attempts > 1)
110-
_logger?.LogInformation("Retrying {Attempts} attempt after {Duration:g}...", attempts.ToOrdinal(), _timeProvider.GetUtcNow().Subtract(startTime));
111-
112-
CircuitBreaker?.BeforeCall();
113-
var result = await action(linkedCancellationToken).AnyContext();
114-
CircuitBreaker?.RecordCallSuccess();
115-
return result;
116-
}
117-
catch (BrokenCircuitException)
112+
do
118113
{
119-
throw;
120-
}
121-
catch (Exception ex)
122-
{
123-
if (ex is TaskCanceledException && timeoutToken.IsCancellationRequested)
124-
throw new TimeoutException($"Operation timed out after {Timeout:g}.");
114+
try
115+
{
116+
if (attempts > 1)
117+
_logger?.LogInformation("Retrying {Attempts} attempt after {Duration:g}...", attempts.ToOrdinal(), _timeProvider.GetUtcNow().Subtract(startTime));
118+
119+
CircuitBreaker?.BeforeCall();
120+
var result = await action(linkedCancellationToken).AnyContext();
121+
CircuitBreaker?.RecordCallSuccess();
122+
return result;
123+
}
124+
catch (BrokenCircuitException)
125+
{
126+
throw;
127+
}
128+
catch (Exception ex)
129+
{
130+
if (ex is TaskCanceledException && timeoutToken.IsCancellationRequested)
131+
throw new TimeoutException($"Operation timed out after {Timeout:g}.");
125132

126-
CircuitBreaker?.RecordCallFailure(ex);
133+
CircuitBreaker?.RecordCallFailure(ex);
127134

128-
if (attempts >= MaxAttempts || (ShouldRetry != null && !ShouldRetry(attempts, ex)) || UnhandledExceptions.Contains(ex.GetType()))
129-
throw;
135+
if (attempts >= MaxAttempts || (ShouldRetry != null && !ShouldRetry(attempts, ex)) || UnhandledExceptions.Contains(ex.GetType()))
136+
throw;
130137

131-
_logger?.LogError(ex, "Retry error: {Message}", ex.Message);
138+
_logger?.LogError(ex, "Retry error: {Message}", ex.Message);
132139

133-
await _timeProvider.SafeDelay(GetAttemptDelay(attempts), linkedCancellationToken).AnyContext();
140+
await _timeProvider.SafeDelay(GetAttemptDelay(attempts), linkedCancellationToken).AnyContext();
134141

135-
ThrowIfTimedOut(startTime);
136-
}
142+
ThrowIfTimedOut(startTime);
143+
}
137144

138-
attempts++;
139-
} while (attempts <= MaxAttempts && !linkedCancellationToken.IsCancellationRequested);
145+
attempts++;
146+
} while (attempts <= MaxAttempts && !linkedCancellationToken.IsCancellationRequested);
140147

141-
throw new OperationCanceledException("Operation was canceled", linkedCancellationToken);
148+
throw new OperationCanceledException("Operation was canceled", linkedCancellationToken);
149+
}
150+
finally
151+
{
152+
linkedCts?.Dispose();
153+
timeoutCts?.Dispose();
154+
}
142155
}
143156

144157
public ResiliencePolicy Clone(int? maxAttempts = null, TimeSpan? timeout = null, TimeSpan? delay = null, Func<int, TimeSpan> getDelay = null)

0 commit comments

Comments
 (0)