Skip to content
Merged
Show file tree
Hide file tree
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
18 changes: 16 additions & 2 deletions src/Polly/Bulkhead/BulkheadEngine.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#nullable enable

namespace Polly.Bulkhead;

internal static class BulkheadEngine
Expand Down Expand Up @@ -26,12 +27,25 @@ internal static TResult Implementation<TResult>(
}
finally
{
maxParallelizationSemaphore.Release();
SafeRelease(maxParallelizationSemaphore);
}
}
finally
{
maxQueuedActionsSemaphore.Release();
SafeRelease(maxQueuedActionsSemaphore);
}

[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
static void SafeRelease(SemaphoreSlim semaphore)
{
try
{
semaphore.Release();
}
catch (ObjectDisposedException)
{
// Ignore - this can happen if the caller disposed the semaphore
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public void Ctor_Isolated(bool isolated)
[Theory]
public async Task IsolateAsync_NotInitialized_Ok(bool closedAfter)
{
var cancellationToken = CancellationToken.None;
var cancellationToken = TestCancellation.Token;
var control = new CircuitBreakerManualControl();
await control.IsolateAsync(cancellationToken);
if (closedAfter)
Expand All @@ -56,14 +56,14 @@ public async Task ResetAsync_NotInitialized_Ok()
{
var control = new CircuitBreakerManualControl();

await Should.NotThrowAsync(() => control.CloseAsync());
await Should.NotThrowAsync(() => control.CloseAsync(TestCancellation.Token));
}

[Fact]
public async Task Initialize_Twice_Ok()
{
int called = 0;
var cancellationToken = CancellationToken.None;
var cancellationToken = TestCancellation.Token;
var control = new CircuitBreakerManualControl();
control.Initialize(_ => Task.CompletedTask, _ => Task.CompletedTask);
control.Initialize(_ => { called++; return Task.CompletedTask; }, _ => { called++; return Task.CompletedTask; });
Expand All @@ -78,7 +78,7 @@ public async Task Initialize_Twice_Ok()
public async Task Initialize_DisposeRegistration_ShuldBeCancelled()
{
int called = 0;
var cancellationToken = CancellationToken.None;
var cancellationToken = TestCancellation.Token;
var control = new CircuitBreakerManualControl();
var reg = control.Initialize(_ => { called++; return Task.CompletedTask; }, _ => { called++; return Task.CompletedTask; });

Expand All @@ -96,7 +96,7 @@ public async Task Initialize_DisposeRegistration_ShuldBeCancelled()
[Fact]
public async Task Initialize_Ok()
{
var cancellationToken = CancellationToken.None;
var cancellationToken = TestCancellation.Token;
var control = new CircuitBreakerManualControl();
var isolateCalled = false;
var resetCalled = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class CircuitBreakerOptionsTests
public async Task ShouldHandle_EnsureDefaults()
{
var options = new CircuitBreakerStrategyOptions();
var context = ResilienceContextPool.Shared.Get();
var context = ResilienceContextPool.Shared.Get(TestCancellation.Token);

(await options.ShouldHandle(new CircuitBreakerPredicateArguments<object>(context, Outcome.FromResult<object>("dummy")))).ShouldBe(false);
(await options.ShouldHandle(new CircuitBreakerPredicateArguments<object>(context, Outcome.FromException<object>(new OperationCanceledException())))).ShouldBe(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public static class CircuitBreakerPredicateArgumentsTests
public static void Ctor_Ok()
{
var args = new CircuitBreakerPredicateArguments<int>(
ResilienceContextPool.Shared.Get(CancellationToken.None),
ResilienceContextPool.Shared.Get(TestCancellation.Token),
Outcome.FromResult(1));

args.Context.ShouldNotBeNull();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public void AddCircuitBreaker_Validation()
[Fact]
public void AddCircuitBreaker_IntegrationTest()
{
var cancellationToken = CancellationToken.None;
var cancellationToken = TestCancellation.Token;
int opened = 0;
int closed = 0;
int halfOpened = 0;
Expand Down Expand Up @@ -120,7 +120,7 @@ public void AddCircuitBreaker_IntegrationTest()
[Fact]
public void AddCircuitBreaker_IntegrationTest_WithBreakDurationGenerator()
{
var cancellationToken = CancellationToken.None;
var cancellationToken = TestCancellation.Token;
int opened = 0;
int closed = 0;
int halfOpened = 0;
Expand Down Expand Up @@ -184,7 +184,7 @@ public void AddCircuitBreaker_IntegrationTest_WithBreakDurationGenerator()
[Fact]
public async Task AddCircuitBreakers_WithIsolatedManualControl_ShouldBeIsolated()
{
var cancellationToken = CancellationToken.None;
var cancellationToken = TestCancellation.Token;
var manualControl = new CircuitBreakerManualControl();
await manualControl.IsolateAsync(cancellationToken);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public CircuitBreakerResilienceStrategyTests()
null);
}

private static CancellationToken CancellationToken => CancellationToken.None;
private static CancellationToken CancellationToken => TestCancellation.Token;

[Fact]
public void Ctor_Ok() =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public void Ctor_EnsureDefaults()
public async Task IsolateAsync_Ok()
{
// arrange
var cancellationToken = CancellationToken.None;
var cancellationToken = TestCancellation.Token;
bool called = false;
_options.OnOpened = args =>
{
Expand Down Expand Up @@ -69,7 +69,7 @@ public async Task IsolateAsync_Ok()
public async Task BreakAsync_Ok()
{
// arrange
var cancellationToken = CancellationToken.None;
var cancellationToken = TestCancellation.Token;
bool called = false;
_options.OnClosed = args =>
{
Expand Down Expand Up @@ -102,7 +102,7 @@ public async Task BreakAsync_Ok()
[Fact]
public async Task Disposed_EnsureThrows()
{
var context = ResilienceContextPool.Shared.Get();
var context = ResilienceContextPool.Shared.Get(TestCancellation.Token);

var controller = CreateController();
controller.Dispose();
Expand All @@ -121,7 +121,7 @@ public async Task Disposed_EnsureThrows()
[Fact]
public async Task OnActionPreExecute_CircuitOpenedByValue()
{
var context = ResilienceContextPool.Shared.Get();
var context = ResilienceContextPool.Shared.Get(TestCancellation.Token);
using var controller = CreateController();

await OpenCircuit(controller, Outcome.FromResult(99));
Expand All @@ -138,7 +138,7 @@ public async Task OnActionPreExecute_CircuitOpenedByValue()
public async Task OnActionPreExecute_CircuitOpened_EnsureExceptionStackTraceDoesNotGrow(bool innerException)
{
var stacks = new List<string>();
var context = ResilienceContextPool.Shared.Get();
var context = ResilienceContextPool.Shared.Get(TestCancellation.Token);
using var controller = CreateController();

await OpenCircuit(
Expand Down Expand Up @@ -175,7 +175,7 @@ await OpenCircuit(
[Fact]
public async Task HalfOpen_EnsureBreakDuration()
{
var context = ResilienceContextPool.Shared.Get();
var context = ResilienceContextPool.Shared.Get(TestCancellation.Token);
using var controller = CreateController();

await TransitionToState(controller, CircuitState.HalfOpen, context);
Expand All @@ -187,7 +187,7 @@ public async Task HalfOpen_EnsureBreakDuration()
[Theory]
public async Task HalfOpen_EnsureCorrectStateTransitionAfterExecution(bool success)
{
var context = ResilienceContextPool.Shared.Get();
var context = ResilienceContextPool.Shared.Get(TestCancellation.Token);
using var controller = CreateController();

await TransitionToState(controller, CircuitState.HalfOpen, context);
Expand All @@ -213,7 +213,7 @@ public async Task HalfOpen_EnsureCorrectStateTransitionAfterExecution(bool succe
[Fact]
public async Task OnActionPreExecute_CircuitOpenedByException()
{
var context = ResilienceContextPool.Shared.Get();
var context = ResilienceContextPool.Shared.Get(TestCancellation.Token);
using var controller = CreateController();

await OpenCircuit(controller, Outcome.FromException<int>(new InvalidOperationException()));
Expand All @@ -227,7 +227,7 @@ public async Task OnActionPreExecute_CircuitOpenedByException()
public async Task OnActionFailure_EnsureLock()
{
// arrange
var cancellationToken = CancellationToken.None;
var cancellationToken = TestCancellation.Token;
var context = ResilienceContextPool.Shared.Get(cancellationToken);

using var executing = new ManualResetEvent(false);
Expand All @@ -244,9 +244,9 @@ public async Task OnActionFailure_EnsureLock()
using var controller = CreateController();

// act
var executeAction = Task.Run(() => controller.OnHandledOutcomeAsync(Outcome.FromResult(0), context));
var executeAction = Task.Run(() => controller.OnHandledOutcomeAsync(Outcome.FromResult(0), context), cancellationToken);
executing.WaitOne();
var executeAction2 = Task.Run(() => controller.OnHandledOutcomeAsync(Outcome.FromResult(0), context));
var executeAction2 = Task.Run(() => controller.OnHandledOutcomeAsync(Outcome.FromResult(0), context), cancellationToken);

// assert
#pragma warning disable xUnit1031 // Do not use blocking task operations in test method
Expand All @@ -261,7 +261,7 @@ public async Task OnActionFailure_EnsureLock()
public async Task OnActionPreExecute_HalfOpen()
{
// arrange
var context = ResilienceContextPool.Shared.Get();
var context = ResilienceContextPool.Shared.Get(TestCancellation.Token);
var called = false;
_options.OnHalfOpened = _ =>
{
Expand Down Expand Up @@ -293,7 +293,7 @@ public async Task OnActionPreExecute_HalfOpen()
public async Task OnActionSuccess_EnsureCorrectBehavior(CircuitState state, CircuitState expectedState)
{
// arrange
var context = ResilienceContextPool.Shared.Get();
var context = ResilienceContextPool.Shared.Get(TestCancellation.Token);
var called = false;
_options.OnClosed = args =>
{
Expand Down Expand Up @@ -329,7 +329,7 @@ public async Task OnActionSuccess_EnsureCorrectBehavior(CircuitState state, Circ
public async Task OnActionFailureAsync_EnsureCorrectBehavior(CircuitState state, CircuitState expectedState, bool shouldBreak)
{
// arrange
var context = ResilienceContextPool.Shared.Get();
var context = ResilienceContextPool.Shared.Get(TestCancellation.Token);
var called = false;
_options.OnOpened = args =>
{
Expand Down Expand Up @@ -370,7 +370,7 @@ public async Task OnActionFailureAsync_EnsureCorrectBehavior(CircuitState state,
public async Task OnActionFailureAsync_EnsureBreakDurationGeneration()
{
// arrange
var context = ResilienceContextPool.Shared.Get();
var context = ResilienceContextPool.Shared.Get(TestCancellation.Token);
_options.BreakDurationGenerator = static args =>
{
args.FailureCount.ShouldBe(1);
Expand Down Expand Up @@ -402,7 +402,7 @@ public async Task OnActionFailureAsync_EnsureBreakDurationGeneration()
public async Task BreakDurationGenerator_EnsureHalfOpenAttempts()
{
// arrange
var context = ResilienceContextPool.Shared.Get();
var context = ResilienceContextPool.Shared.Get(TestCancellation.Token);
var halfOpenAttempts = new List<int>();

_options.BreakDurationGenerator = args =>
Expand Down Expand Up @@ -440,7 +440,7 @@ public async Task BreakDurationGenerator_EnsureHalfOpenAttempts()
public async Task OnActionFailureAsync_EnsureBreakDurationNotOverflow(bool overflow)
{
// arrange
var context = ResilienceContextPool.Shared.Get();
var context = ResilienceContextPool.Shared.Get(TestCancellation.Token);
using var controller = CreateController();
var shouldBreak = true;
await TransitionToState(controller, CircuitState.HalfOpen, context);
Expand Down Expand Up @@ -475,7 +475,7 @@ public async Task OnActionFailureAsync_EnsureBreakDurationNotOverflow(bool overf
public async Task OnActionFailureAsync_VoidResult_EnsureBreakingExceptionNotSet()
{
// arrange
var context = ResilienceContextPool.Shared.Get();
var context = ResilienceContextPool.Shared.Get(TestCancellation.Token);
using var controller = CreateController();
bool shouldBreak = true;
await TransitionToState(controller, CircuitState.Open, context);
Expand All @@ -497,7 +497,7 @@ public async Task OnActionFailureAsync_VoidResult_EnsureBreakingExceptionNotSet(
[Fact]
public async Task Flow_Closed_HalfOpen_Closed()
{
var context = ResilienceContextPool.Shared.Get();
var context = ResilienceContextPool.Shared.Get(TestCancellation.Token);
using var controller = CreateController();

await TransitionToState(controller, CircuitState.HalfOpen, context);
Expand All @@ -512,7 +512,7 @@ public async Task Flow_Closed_HalfOpen_Closed()
[Fact]
public async Task Flow_Closed_HalfOpen_Open_HalfOpen_Closed()
{
var cancellationToken = CancellationToken.None;
var cancellationToken = TestCancellation.Token;
var context = ResilienceContextPool.Shared.Get(cancellationToken);

using var controller = CreateController();
Expand Down Expand Up @@ -550,7 +550,7 @@ public async Task Flow_Closed_HalfOpen_Open_HalfOpen_Closed()
[Fact]
public async Task ExecuteScheduledTask_Async_Ok()
{
var cancellationToken = CancellationToken.None;
var cancellationToken = TestCancellation.Token;
var context = ResilienceContextPool.Shared.Get(cancellationToken);

var source = new TaskCompletionSource<string>();
Expand Down Expand Up @@ -605,7 +605,7 @@ private async Task OpenCircuit(CircuitStateController<int> controller, Outcome<i

await controller.OnHandledOutcomeAsync(
outcome ?? Outcome.FromResult(10),
ResilienceContextPool.Shared.Get().Initialize<int>(true));
ResilienceContextPool.Shared.Get(TestCancellation.Token).Initialize<int>(true));
}

private void AdvanceTime(TimeSpan timespan) => _timeProvider.Advance(timespan);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace Polly.Core.Tests.CircuitBreaker.Controller;

public class ScheduledTaskExecutorTests
{
private static CancellationToken CancellationToken => CancellationToken.None;
private static CancellationToken CancellationToken => TestCancellation.Token;

[Fact]
public async Task ScheduleTask_Success_EnsureExecuted()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public static class OnCircuitClosedArgumentsTests
public static void Ctor_Ok()
{
// Arrange
var context = ResilienceContextPool.Shared.Get();
var context = ResilienceContextPool.Shared.Get(TestCancellation.Token);

// Act
var args = new OnCircuitClosedArguments<int>(context, Outcome.FromResult(1), true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public static class OnCircuitHalfOpenedArgumentsTests
public static void Ctor_Ok()
{
// Arrange
var context = ResilienceContextPool.Shared.Get();
var context = ResilienceContextPool.Shared.Get(TestCancellation.Token);

// Act
var target = new OnCircuitHalfOpenedArguments(context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public static class OnCircuitOpenedArgumentsTests
public static void Ctor_Ok()
{
// Arrange
var context = ResilienceContextPool.Shared.Get();
var context = ResilienceContextPool.Shared.Get(TestCancellation.Token);

// Act
var args = new OnCircuitOpenedArguments<int>(context, Outcome.FromResult(1), TimeSpan.FromSeconds(2), true);
Expand Down
2 changes: 1 addition & 1 deletion test/Polly.Core.Tests/Fallback/FallbackHandlerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public class FallbackHandlerTests
public async Task GenerateAction_Generic_Ok()
{
var handler = FallbackHelper.CreateHandler(_ => true, () => Outcome.FromResult("secondary"));
var context = ResilienceContextPool.Shared.Get();
var context = ResilienceContextPool.Shared.Get(TestCancellation.Token);
var outcome = await handler.ActionGenerator(new FallbackActionArguments<string>(context, Outcome.FromResult("primary")))!;

outcome.Result.ShouldBe("secondary");
Expand Down
Loading
Loading