<PackageReference Include="Polly" Version="5.6.1" />

CircuitStateController<TResult>

abstract class CircuitStateController<TResult> : ICircuitController<TResult>
using Polly.Utilities; using System; using System.Threading; namespace Polly.CircuitBreaker { internal abstract class CircuitStateController<TResult> : ICircuitController<TResult> { protected readonly TimeSpan _durationOfBreak; protected long _blockedTill; protected CircuitState _circuitState; protected DelegateResult<TResult> _lastOutcome; protected readonly Action<DelegateResult<TResult>, TimeSpan, Context> _onBreak; protected readonly Action<Context> _onReset; protected readonly Action _onHalfOpen; protected readonly object _lock = new object(); public CircuitState CircuitState { get { if (_circuitState != CircuitState.Open) return _circuitState; using (TimedLock.Lock(_lock)) { if (_circuitState == CircuitState.Open && !IsInAutomatedBreak_NeedsLock) { _circuitState = CircuitState.HalfOpen; _onHalfOpen(); } return _circuitState; } } } public Exception LastException { get { using (TimedLock.Lock(_lock)) { DelegateResult<TResult> lastOutcome = _lastOutcome; return (lastOutcome != null) ? lastOutcome.Exception : null; } } } public TResult LastHandledResult { get { using (TimedLock.Lock(_lock)) return (_lastOutcome != null) ? _lastOutcome.Result : default(TResult); } } protected bool IsInAutomatedBreak_NeedsLock => SystemClock.UtcNow().Ticks < _blockedTill; protected CircuitStateController(TimeSpan durationOfBreak, Action<DelegateResult<TResult>, TimeSpan, Context> onBreak, Action<Context> onReset, Action onHalfOpen) { _durationOfBreak = durationOfBreak; _onBreak = onBreak; _onReset = onReset; _onHalfOpen = onHalfOpen; _circuitState = CircuitState.Closed; Reset(); } public void Isolate() { using (TimedLock.Lock(_lock)) { _lastOutcome = new DelegateResult<TResult>(new IsolatedCircuitException("The circuit is manually held open and is not allowing calls.")); BreakFor_NeedsLock(TimeSpan.MaxValue, Context.None); _circuitState = CircuitState.Isolated; } } protected void Break_NeedsLock(Context context) { BreakFor_NeedsLock(_durationOfBreak, context); } private void BreakFor_NeedsLock(TimeSpan durationOfBreak, Context context) { DateTime dateTime; long ticks; if (!(durationOfBreak > DateTime.MaxValue - SystemClock.UtcNow())) { dateTime = SystemClock.UtcNow() + durationOfBreak; ticks = dateTime.Ticks; } else { dateTime = DateTime.MaxValue; ticks = dateTime.Ticks; } _blockedTill = ticks; _circuitState = CircuitState.Open; _onBreak(_lastOutcome, durationOfBreak, context); } public void Reset() { OnCircuitReset(Context.None); } protected void ResetInternal_NeedsLock(Context context) { _blockedTill = DateTime.MinValue.Ticks; _lastOutcome = null; CircuitState circuitState = _circuitState; _circuitState = CircuitState.Closed; if (circuitState != 0) _onReset(context); } protected bool PermitHalfOpenCircuitTest() { long blockedTill = _blockedTill; DateTime dateTime = SystemClock.UtcNow(); if (dateTime.Ticks >= blockedTill) { ref long blockedTill2 = ref _blockedTill; dateTime = SystemClock.UtcNow(); return Interlocked.CompareExchange(ref blockedTill2, dateTime.Ticks + _durationOfBreak.Ticks, blockedTill) == blockedTill; } return false; } private BrokenCircuitException GetBreakingException() { if (_lastOutcome.Exception == null) return new BrokenCircuitException<TResult>("The circuit is now open and is not allowing calls.", _lastOutcome.Result); return new BrokenCircuitException("The circuit is now open and is not allowing calls.", _lastOutcome.Exception); } public void OnActionPreExecute() { switch (CircuitState) { case CircuitState.Closed: break; case CircuitState.HalfOpen: if (!PermitHalfOpenCircuitTest()) throw GetBreakingException(); break; case CircuitState.Open: throw GetBreakingException(); case CircuitState.Isolated: throw new IsolatedCircuitException("The circuit is manually held open and is not allowing calls."); default: throw new InvalidOperationException("Unhandled CircuitState."); } } public abstract void OnActionSuccess(Context context); public abstract void OnActionFailure(DelegateResult<TResult> outcome, Context context); public abstract void OnCircuitReset(Context context); } }