44using OpenFeature . Constant ;
55using OpenFeature . Model ;
66
7-
87namespace OpenFeature ;
98
109/// <summary>
1110/// This class manages the collection of providers, both default and named, contained by the API.
1211/// </summary>
1312internal sealed partial class ProviderRepository : IAsyncDisposable
1413{
15- private ILogger _logger = NullLogger < EventExecutor > . Instance ;
14+ private ILogger _logger = NullLogger < ProviderRepository > . Instance ;
1615
1716 private FeatureProvider _defaultProvider = new NoOpFeatureProvider ( ) ;
1817
19- private readonly ConcurrentDictionary < string , FeatureProvider > _featureProviders =
20- new ConcurrentDictionary < string , FeatureProvider > ( ) ;
18+ private readonly ConcurrentDictionary < string , FeatureProvider > _featureProviders = new ( ) ;
2119
2220 /// The reader/writer locks is not disposed because the singleton instance should never be disposed.
2321 ///
@@ -29,7 +27,7 @@ internal sealed partial class ProviderRepository : IAsyncDisposable
2927 /// The second is that a concurrent collection doesn't provide any ordering, so we could check a provider
3028 /// as it was being added or removed such as two concurrent calls to SetProvider replacing multiple instances
3129 /// of that provider under different names.
32- private readonly ReaderWriterLockSlim _providersLock = new ReaderWriterLockSlim ( ) ;
30+ private readonly ReaderWriterLockSlim _providersLock = new ( ) ;
3331
3432 public async ValueTask DisposeAsync ( )
3533 {
@@ -53,11 +51,13 @@ public async ValueTask DisposeAsync()
5351 /// called if an error happens during the initialization of the provider, only called if the provider needed
5452 /// initialization
5553 /// </param>
56- public async Task SetProviderAsync (
54+ /// <param name="cancellationToken">a cancellation token to cancel the operation</param>
55+ internal async Task SetProviderAsync (
5756 FeatureProvider ? featureProvider ,
5857 EvaluationContext context ,
5958 Func < FeatureProvider , Task > ? afterInitSuccess = null ,
60- Func < FeatureProvider , Exception , Task > ? afterInitError = null )
59+ Func < FeatureProvider , Exception , Task > ? afterInitError = null ,
60+ CancellationToken cancellationToken = default )
6161 {
6262 // Cannot unset the feature provider.
6363 if ( featureProvider == null )
@@ -79,22 +79,23 @@ public async Task SetProviderAsync(
7979 this . _defaultProvider = featureProvider ;
8080 // We want to allow shutdown to happen concurrently with initialization, and the caller to not
8181 // wait for it.
82- _ = this . ShutdownIfUnusedAsync ( oldProvider ) ;
82+ _ = this . ShutdownIfUnusedAsync ( oldProvider , cancellationToken ) ;
8383 }
8484 finally
8585 {
8686 this . _providersLock . ExitWriteLock ( ) ;
8787 }
8888
89- await InitProviderAsync ( this . _defaultProvider , context , afterInitSuccess , afterInitError )
89+ await InitProviderAsync ( this . _defaultProvider , context , afterInitSuccess , afterInitError , cancellationToken )
9090 . ConfigureAwait ( false ) ;
9191 }
9292
9393 private static async Task InitProviderAsync (
9494 FeatureProvider ? newProvider ,
9595 EvaluationContext context ,
9696 Func < FeatureProvider , Task > ? afterInitialization ,
97- Func < FeatureProvider , Exception , Task > ? afterError )
97+ Func < FeatureProvider , Exception , Task > ? afterError ,
98+ CancellationToken cancellationToken = default )
9899 {
99100 if ( newProvider == null )
100101 {
@@ -104,7 +105,7 @@ private static async Task InitProviderAsync(
104105 {
105106 try
106107 {
107- await newProvider . InitializeAsync ( context ) . ConfigureAwait ( false ) ;
108+ await newProvider . InitializeAsync ( context , cancellationToken ) . ConfigureAwait ( false ) ;
108109 if ( afterInitialization != null )
109110 {
110111 await afterInitialization . Invoke ( newProvider ) . ConfigureAwait ( false ) ;
@@ -134,15 +135,15 @@ private static async Task InitProviderAsync(
134135 /// initialization
135136 /// </param>
136137 /// <param name="cancellationToken">The <see cref="CancellationToken"/> to cancel any async side effects.</param>
137- public async Task SetProviderAsync ( string ? domain ,
138+ internal async Task SetProviderAsync ( string domain ,
138139 FeatureProvider ? featureProvider ,
139140 EvaluationContext context ,
140141 Func < FeatureProvider , Task > ? afterInitSuccess = null ,
141142 Func < FeatureProvider , Exception , Task > ? afterInitError = null ,
142143 CancellationToken cancellationToken = default )
143144 {
144145 // Cannot set a provider for a null domain.
145- if ( domain == null )
146+ if ( string . IsNullOrWhiteSpace ( domain ) )
146147 {
147148 return ;
148149 }
@@ -166,21 +167,21 @@ public async Task SetProviderAsync(string? domain,
166167
167168 // We want to allow shutdown to happen concurrently with initialization, and the caller to not
168169 // wait for it.
169- _ = this . ShutdownIfUnusedAsync ( oldProvider ) ;
170+ _ = this . ShutdownIfUnusedAsync ( oldProvider , cancellationToken ) ;
170171 }
171172 finally
172173 {
173174 this . _providersLock . ExitWriteLock ( ) ;
174175 }
175176
176- await InitProviderAsync ( featureProvider , context , afterInitSuccess , afterInitError ) . ConfigureAwait ( false ) ;
177+ await InitProviderAsync ( featureProvider , context , afterInitSuccess , afterInitError , cancellationToken ) . ConfigureAwait ( false ) ;
177178 }
178179
179180 /// <remarks>
180181 /// Shutdown the feature provider if it is unused. This must be called within a write lock of the _providersLock.
181182 /// </remarks>
182183 private async Task ShutdownIfUnusedAsync (
183- FeatureProvider ? targetProvider )
184+ FeatureProvider ? targetProvider , CancellationToken cancellationToken = default )
184185 {
185186 if ( ReferenceEquals ( this . _defaultProvider , targetProvider ) )
186187 {
@@ -192,7 +193,7 @@ private async Task ShutdownIfUnusedAsync(
192193 return ;
193194 }
194195
195- await this . SafeShutdownProviderAsync ( targetProvider ) . ConfigureAwait ( false ) ;
196+ await this . SafeShutdownProviderAsync ( targetProvider , cancellationToken ) . ConfigureAwait ( false ) ;
196197 }
197198
198199 /// <remarks>
@@ -204,7 +205,7 @@ private async Task ShutdownIfUnusedAsync(
204205 /// it would not be meaningful to emit an error.
205206 /// </para>
206207 /// </remarks>
207- private async Task SafeShutdownProviderAsync ( FeatureProvider ? targetProvider )
208+ private async Task SafeShutdownProviderAsync ( FeatureProvider ? targetProvider , CancellationToken cancellationToken = default )
208209 {
209210 if ( targetProvider == null )
210211 {
@@ -213,15 +214,15 @@ private async Task SafeShutdownProviderAsync(FeatureProvider? targetProvider)
213214
214215 try
215216 {
216- await targetProvider . ShutdownAsync ( ) . ConfigureAwait ( false ) ;
217+ await targetProvider . ShutdownAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
217218 }
218219 catch ( Exception ex )
219220 {
220221 this . ErrorShuttingDownProvider ( targetProvider . GetMetadata ( ) ? . Name , ex ) ;
221222 }
222223 }
223224
224- public FeatureProvider GetProvider ( )
225+ internal FeatureProvider GetProvider ( )
225226 {
226227 this . _providersLock . EnterReadLock ( ) ;
227228 try
@@ -234,16 +235,16 @@ public FeatureProvider GetProvider()
234235 }
235236 }
236237
237- public FeatureProvider GetProvider ( string ? domain )
238+ internal FeatureProvider GetProvider ( string ? domain )
238239 {
239- #if NET6_0_OR_GREATER
240- if ( string . IsNullOrEmpty ( domain ) )
240+ #if NETFRAMEWORK || NETSTANDARD
241+ // This is a workaround for the issue in .NET Framework where string.IsNullOrEmpty is not nullable compatible.
242+ if ( domain == null )
241243 {
242244 return this . GetProvider ( ) ;
243245 }
244246#else
245- // This is a workaround for the issue in .NET Framework where string.IsNullOrEmpty is not nullable compatible.
246- if ( domain == null || string . IsNullOrEmpty ( domain ) )
247+ if ( string . IsNullOrWhiteSpace ( domain ) )
247248 {
248249 return this . GetProvider ( ) ;
249250 }
@@ -254,7 +255,7 @@ public FeatureProvider GetProvider(string? domain)
254255 : this . GetProvider ( ) ;
255256 }
256257
257- public async Task ShutdownAsync ( Action < FeatureProvider , Exception > ? afterError = null , CancellationToken cancellationToken = default )
258+ internal async Task ShutdownAsync ( Action < FeatureProvider , Exception > ? afterError = null , CancellationToken cancellationToken = default )
258259 {
259260 var providers = new HashSet < FeatureProvider > ( ) ;
260261 this . _providersLock . EnterWriteLock ( ) ;
@@ -278,7 +279,7 @@ public async Task ShutdownAsync(Action<FeatureProvider, Exception>? afterError =
278279 foreach ( var targetProvider in providers )
279280 {
280281 // We don't need to take any actions after shutdown.
281- await this . SafeShutdownProviderAsync ( targetProvider ) . ConfigureAwait ( false ) ;
282+ await this . SafeShutdownProviderAsync ( targetProvider , cancellationToken ) . ConfigureAwait ( false ) ;
282283 }
283284 }
284285
0 commit comments