<PackageReference Include="Polly.Core" Version="8.0.0-alpha.3" />

CircuitStateController<T>

Thread-safe controller that holds and manages the circuit breaker state transitions.
using Polly.Telemetry; using System; using System.Runtime.CompilerServices; using System.Threading.Tasks; namespace Polly.CircuitBreaker { [System.Runtime.CompilerServices.NullableContext(1)] [System.Runtime.CompilerServices.Nullable(0)] internal sealed class CircuitStateController<[System.Runtime.CompilerServices.Nullable(2)] T> : IDisposable { private readonly object _lock = new object(); private readonly ScheduledTaskExecutor _executor = new ScheduledTaskExecutor(); [System.Runtime.CompilerServices.Nullable(new byte[] { 2, 0, 1, 1 })] private readonly Func<OutcomeArguments<T, OnCircuitOpenedArguments>, ValueTask> _onOpened; [System.Runtime.CompilerServices.Nullable(new byte[] { 2, 0, 1, 1 })] private readonly Func<OutcomeArguments<T, OnCircuitClosedArguments>, ValueTask> _onClosed; [System.Runtime.CompilerServices.Nullable(new byte[] { 2, 1 })] private readonly Func<OnCircuitHalfOpenedArguments, ValueTask> _onHalfOpen; private readonly TimeProvider _timeProvider; private readonly ResilienceStrategyTelemetry _telemetry; private readonly CircuitBehavior _behavior; private readonly TimeSpan _breakDuration; private DateTimeOffset _blockedUntil; private CircuitState _circuitState; [System.Runtime.CompilerServices.Nullable(new byte[] { 0, 1 })] private Outcome<object>? _lastOutcome; private BrokenCircuitException _breakingException = new BrokenCircuitException(); private bool _disposed; public CircuitState CircuitState { get { EnsureNotDisposed(); lock (_lock) { return _circuitState; } } } [System.Runtime.CompilerServices.Nullable(2)] public Exception LastException { [System.Runtime.CompilerServices.NullableContext(2)] get { EnsureNotDisposed(); lock (_lock) { ref Outcome<object>? lastOutcome = ref _lastOutcome; return lastOutcome.HasValue ? lastOutcome.GetValueOrDefault().Exception : null; } } } [System.Runtime.CompilerServices.Nullable(new byte[] { 0, 1 })] public Outcome<object>? LastHandledOutcome { [return: System.Runtime.CompilerServices.Nullable(new byte[] { 0, 1 })] get { EnsureNotDisposed(); lock (_lock) { return _lastOutcome; } } } public CircuitStateController(TimeSpan breakDuration, [System.Runtime.CompilerServices.Nullable(new byte[] { 2, 0, 1, 1 })] Func<OutcomeArguments<T, OnCircuitOpenedArguments>, ValueTask> onOpened, [System.Runtime.CompilerServices.Nullable(new byte[] { 2, 0, 1, 1 })] Func<OutcomeArguments<T, OnCircuitClosedArguments>, ValueTask> onClosed, [System.Runtime.CompilerServices.Nullable(new byte[] { 2, 1 })] Func<OnCircuitHalfOpenedArguments, ValueTask> onHalfOpen, CircuitBehavior behavior, TimeProvider timeProvider, ResilienceStrategyTelemetry telemetry) { _breakDuration = breakDuration; _onOpened = onOpened; _onClosed = onClosed; _onHalfOpen = onHalfOpen; _behavior = behavior; _timeProvider = timeProvider; _telemetry = telemetry; } public ValueTask IsolateCircuitAsync(ResilienceContext context) { EnsureNotDisposed(); context.Initialize<T>(false); Task scheduledTask; lock (_lock) { SetLastHandledOutcome_NeedsLock(Outcome.FromException<T>((Exception)new IsolatedCircuitException())); OpenCircuitFor_NeedsLock(Outcome.FromResult<T>(default(T)), TimeSpan.MaxValue, true, context, out scheduledTask); _circuitState = CircuitState.Isolated; } return ExecuteScheduledTaskAsync(scheduledTask, context); } public ValueTask CloseCircuitAsync(ResilienceContext context) { EnsureNotDisposed(); context.Initialize<T>(false); Task scheduledTask; lock (_lock) { CloseCircuit_NeedsLock(Outcome.FromResult<T>(default(T)), true, context, out scheduledTask); } return ExecuteScheduledTaskAsync(scheduledTask, context); } [AsyncStateMachine(typeof(CircuitStateController<>.<OnActionPreExecuteAsync>d__23))] [return: System.Runtime.CompilerServices.Nullable(new byte[] { 0, 0, 1 })] public ValueTask<Outcome<T>?> OnActionPreExecuteAsync(ResilienceContext context) { <OnActionPreExecuteAsync>d__23 stateMachine = default(<OnActionPreExecuteAsync>d__23); stateMachine.<>t__builder = AsyncValueTaskMethodBuilder<Outcome<T>?>.Create(); stateMachine.<>4__this = this; stateMachine.context = context; stateMachine.<>1__state = -1; stateMachine.<>t__builder.Start(ref stateMachine); return stateMachine.<>t__builder.Task; } public ValueTask OnActionSuccessAsync([System.Runtime.CompilerServices.Nullable(new byte[] { 0, 1 })] Outcome<T> outcome, ResilienceContext context) { EnsureNotDisposed(); Task scheduledTask = null; lock (_lock) { _behavior.OnActionSuccess(_circuitState); if (_circuitState == CircuitState.HalfOpen) CloseCircuit_NeedsLock(outcome, false, context, out scheduledTask); } return ExecuteScheduledTaskAsync(scheduledTask, context); } public ValueTask OnActionFailureAsync([System.Runtime.CompilerServices.Nullable(new byte[] { 0, 1 })] Outcome<T> outcome, ResilienceContext context) { EnsureNotDisposed(); Task scheduledTask = null; lock (_lock) { SetLastHandledOutcome_NeedsLock(outcome); _behavior.OnActionFailure(_circuitState, out bool shouldBreak); if (_circuitState == CircuitState.HalfOpen) OpenCircuit_NeedsLock(outcome, false, context, out scheduledTask); else if ((_circuitState == CircuitState.Closed) & shouldBreak) { OpenCircuit_NeedsLock(outcome, false, context, out scheduledTask); } } return ExecuteScheduledTaskAsync(scheduledTask, context); } public void Dispose() { _executor.Dispose(); _disposed = true; } [AsyncStateMachine(typeof(CircuitStateController<>.<ExecuteScheduledTaskAsync>d__27))] private static ValueTask ExecuteScheduledTaskAsync([System.Runtime.CompilerServices.Nullable(2)] Task task, ResilienceContext context) { <ExecuteScheduledTaskAsync>d__27 stateMachine = default(<ExecuteScheduledTaskAsync>d__27); stateMachine.<>t__builder = AsyncValueTaskMethodBuilder.Create(); stateMachine.task = task; stateMachine.context = context; stateMachine.<>1__state = -1; stateMachine.<>t__builder.Start<<ExecuteScheduledTaskAsync>d__27>(ref stateMachine); return stateMachine.<>t__builder.Task; } private static bool IsDateTimeOverflow(DateTimeOffset utcNow, TimeSpan breakDuration) { TimeSpan t = (DateTimeOffset)DateTime.MaxValue - utcNow; return breakDuration > t; } private void EnsureNotDisposed() { if (_disposed) throw new ObjectDisposedException("CircuitStateController"); } private void CloseCircuit_NeedsLock([System.Runtime.CompilerServices.Nullable(new byte[] { 0, 1 })] Outcome<T> outcome, bool manual, ResilienceContext context, [System.Runtime.CompilerServices.Nullable(2)] out Task scheduledTask) { scheduledTask = null; _blockedUntil = DateTimeOffset.MinValue; _lastOutcome = null; CircuitState circuitState = _circuitState; _circuitState = CircuitState.Closed; _behavior.OnCircuitClosed(); if (circuitState != 0) { OutcomeArguments<T, OnCircuitClosedArguments> args = new OutcomeArguments<T, OnCircuitClosedArguments>(context, outcome, new OnCircuitClosedArguments(manual)); _telemetry.Report<OnCircuitClosedArguments, T>("OnCircuitClosed", args); if (_onClosed != null) _executor.ScheduleTask(() => _onClosed(args).AsTask(), context, out scheduledTask); } } private bool PermitHalfOpenCircuitTest_NeedsLock() { DateTimeOffset utcNow = _timeProvider.GetUtcNow(); if (utcNow >= _blockedUntil) { _blockedUntil = utcNow + _breakDuration; return true; } return false; } [System.Runtime.CompilerServices.NullableContext(2)] private void SetLastHandledOutcome_NeedsLock<TResult>([System.Runtime.CompilerServices.Nullable(new byte[] { 0, 1 })] Outcome<TResult> outcome) { this._lastOutcome = outcome.AsOutcome(); Exception exception = outcome.Exception; TResult result; if (exception != null) this._breakingException = new BrokenCircuitException("The circuit is now open and is not allowing calls.", exception); else if (outcome.TryGetResult(out result)) { this._breakingException = new BrokenCircuitException<TResult>("The circuit is now open and is not allowing calls.", result); } } private void OpenCircuit_NeedsLock([System.Runtime.CompilerServices.Nullable(new byte[] { 0, 1 })] Outcome<T> outcome, bool manual, ResilienceContext context, [System.Runtime.CompilerServices.Nullable(2)] out Task scheduledTask) { OpenCircuitFor_NeedsLock(outcome, _breakDuration, manual, context, out scheduledTask); } private void OpenCircuitFor_NeedsLock([System.Runtime.CompilerServices.Nullable(new byte[] { 0, 1 })] Outcome<T> outcome, TimeSpan breakDuration, bool manual, ResilienceContext context, [System.Runtime.CompilerServices.Nullable(2)] out Task scheduledTask) { scheduledTask = null; DateTimeOffset utcNow = _timeProvider.GetUtcNow(); _blockedUntil = (IsDateTimeOverflow(utcNow, breakDuration) ? DateTimeOffset.MaxValue : (utcNow + breakDuration)); CircuitState circuitState = _circuitState; _circuitState = CircuitState.Open; OutcomeArguments<T, OnCircuitOpenedArguments> args = new OutcomeArguments<T, OnCircuitOpenedArguments>(context, outcome, new OnCircuitOpenedArguments(breakDuration, manual)); _telemetry.Report<OnCircuitOpenedArguments, T>("OnCircuitOpened", args); if (_onOpened != null) _executor.ScheduleTask(() => _onOpened(args).AsTask(), context, out scheduledTask); } } }