@@ -6,9 +6,9 @@ namespace Polly.CircuitBreaker;
66/// <remarks>
77/// The instance of this class can be reused across multiple circuit breakers.
88/// </remarks>
9- public sealed class CircuitBreakerManualControl : IDisposable
9+ public sealed class CircuitBreakerManualControl
1010{
11- private readonly HashSet < Action > _onDispose = new ( ) ;
11+ private readonly object _lock = new ( ) ;
1212 private readonly HashSet < Func < ResilienceContext , Task > > _onIsolate = new ( ) ;
1313 private readonly HashSet < Func < ResilienceContext , Task > > _onReset = new ( ) ;
1414 private bool _isolated ;
@@ -23,21 +23,34 @@ public CircuitBreakerManualControl()
2323 /// <summary>
2424 /// Initializes a new instance of the <see cref="CircuitBreakerManualControl"/> class.
2525 /// </summary>
26- /// <param name="isIsolated">Determines whether the circit breaker is isolated immediately after construction.</param>
26+ /// <param name="isIsolated">Determines whether the circuit breaker is isolated immediately after construction.</param>
2727 public CircuitBreakerManualControl ( bool isIsolated ) => _isolated = isIsolated ;
2828
29- internal void Initialize ( Func < ResilienceContext , Task > onIsolate , Func < ResilienceContext , Task > onReset , Action onDispose )
30- {
31- _onDispose . Add ( onDispose ) ;
32- _onIsolate . Add ( onIsolate ) ;
33- _onReset . Add ( onReset ) ;
29+ internal bool IsEmpty => _onIsolate . Count == 0 ;
3430
35- if ( _isolated )
31+ internal IDisposable Initialize ( Func < ResilienceContext , Task > onIsolate , Func < ResilienceContext , Task > onReset )
32+ {
33+ lock ( _lock )
3634 {
37- var context = ResilienceContextPool . Shared . Get ( ) . Initialize < VoidResult > ( isSynchronous : true ) ;
38-
39- // if the control indicates that circuit breaker should be isolated, we isolate it right away
40- IsolateAsync ( context ) . GetAwaiter ( ) . GetResult ( ) ;
35+ _onIsolate . Add ( onIsolate ) ;
36+ _onReset . Add ( onReset ) ;
37+
38+ if ( _isolated )
39+ {
40+ var context = ResilienceContextPool . Shared . Get ( ) . Initialize < VoidResult > ( isSynchronous : true ) ;
41+
42+ // if the control indicates that circuit breaker should be isolated, we isolate it right away
43+ IsolateAsync ( context ) . GetAwaiter ( ) . GetResult ( ) ;
44+ }
45+
46+ return new RegistrationDisposable ( ( ) =>
47+ {
48+ lock ( _lock )
49+ {
50+ _onIsolate . Remove ( onIsolate ) ;
51+ _onReset . Remove ( onReset ) ;
52+ }
53+ } ) ;
4154 }
4255 }
4356
@@ -54,7 +67,14 @@ internal async Task IsolateAsync(ResilienceContext context)
5467
5568 _isolated = true ;
5669
57- foreach ( var action in _onIsolate )
70+ Func < ResilienceContext , Task > [ ] callbacks ;
71+
72+ lock ( _lock )
73+ {
74+ callbacks = _onIsolate . ToArray ( ) ;
75+ }
76+
77+ foreach ( var action in callbacks )
5878 {
5979 await action ( context ) . ConfigureAwait ( context . ContinueOnCapturedContext ) ;
6080 }
@@ -95,7 +115,14 @@ internal async Task CloseAsync(ResilienceContext context)
95115
96116 context . Initialize < VoidResult > ( isSynchronous : false ) ;
97117
98- foreach ( var action in _onReset )
118+ Func < ResilienceContext , Task > [ ] callbacks ;
119+
120+ lock ( _lock )
121+ {
122+ callbacks = _onReset . ToArray ( ) ;
123+ }
124+
125+ foreach ( var action in callbacks )
99126 {
100127 await action ( context ) . ConfigureAwait ( context . ContinueOnCapturedContext ) ;
101128 }
@@ -121,18 +148,12 @@ public async Task CloseAsync(CancellationToken cancellationToken = default)
121148 }
122149 }
123150
124- /// <summary>
125- /// Disposes the current class.
126- /// </summary>
127- public void Dispose ( )
151+ private class RegistrationDisposable : IDisposable
128152 {
129- foreach ( var action in _onDispose )
130- {
131- action ( ) ;
132- }
153+ private readonly Action _disposeAction ;
154+
155+ public RegistrationDisposable ( Action disposeAction ) => _disposeAction = disposeAction ;
133156
134- _onDispose . Clear ( ) ;
135- _onIsolate . Clear ( ) ;
136- _onReset . Clear ( ) ;
157+ public void Dispose ( ) => _disposeAction ( ) ;
137158 }
138159}
0 commit comments