4
4
using OpenFeature . Constant ;
5
5
using OpenFeature . Model ;
6
6
7
-
8
7
namespace OpenFeature ;
9
8
10
9
/// <summary>
11
10
/// This class manages the collection of providers, both default and named, contained by the API.
12
11
/// </summary>
13
12
internal sealed partial class ProviderRepository : IAsyncDisposable
14
13
{
15
- private ILogger _logger = NullLogger < EventExecutor > . Instance ;
14
+ private ILogger _logger = NullLogger < ProviderRepository > . Instance ;
16
15
17
16
private FeatureProvider _defaultProvider = new NoOpFeatureProvider ( ) ;
18
17
19
- private readonly ConcurrentDictionary < string , FeatureProvider > _featureProviders =
20
- new ConcurrentDictionary < string , FeatureProvider > ( ) ;
18
+ private readonly ConcurrentDictionary < string , FeatureProvider > _featureProviders = new ( ) ;
21
19
22
20
/// The reader/writer locks is not disposed because the singleton instance should never be disposed.
23
21
///
@@ -29,7 +27,7 @@ internal sealed partial class ProviderRepository : IAsyncDisposable
29
27
/// The second is that a concurrent collection doesn't provide any ordering, so we could check a provider
30
28
/// as it was being added or removed such as two concurrent calls to SetProvider replacing multiple instances
31
29
/// of that provider under different names.
32
- private readonly ReaderWriterLockSlim _providersLock = new ReaderWriterLockSlim ( ) ;
30
+ private readonly ReaderWriterLockSlim _providersLock = new ( ) ;
33
31
34
32
public async ValueTask DisposeAsync ( )
35
33
{
@@ -53,11 +51,13 @@ public async ValueTask DisposeAsync()
53
51
/// called if an error happens during the initialization of the provider, only called if the provider needed
54
52
/// initialization
55
53
/// </param>
56
- public async Task SetProviderAsync (
54
+ /// <param name="cancellationToken">a cancellation token to cancel the operation</param>
55
+ internal async Task SetProviderAsync (
57
56
FeatureProvider ? featureProvider ,
58
57
EvaluationContext context ,
59
58
Func < FeatureProvider , Task > ? afterInitSuccess = null ,
60
- Func < FeatureProvider , Exception , Task > ? afterInitError = null )
59
+ Func < FeatureProvider , Exception , Task > ? afterInitError = null ,
60
+ CancellationToken cancellationToken = default )
61
61
{
62
62
// Cannot unset the feature provider.
63
63
if ( featureProvider == null )
@@ -79,22 +79,23 @@ public async Task SetProviderAsync(
79
79
this . _defaultProvider = featureProvider ;
80
80
// We want to allow shutdown to happen concurrently with initialization, and the caller to not
81
81
// wait for it.
82
- _ = this . ShutdownIfUnusedAsync ( oldProvider ) ;
82
+ _ = this . ShutdownIfUnusedAsync ( oldProvider , cancellationToken ) ;
83
83
}
84
84
finally
85
85
{
86
86
this . _providersLock . ExitWriteLock ( ) ;
87
87
}
88
88
89
- await InitProviderAsync ( this . _defaultProvider , context , afterInitSuccess , afterInitError )
89
+ await InitProviderAsync ( this . _defaultProvider , context , afterInitSuccess , afterInitError , cancellationToken )
90
90
. ConfigureAwait ( false ) ;
91
91
}
92
92
93
93
private static async Task InitProviderAsync (
94
94
FeatureProvider ? newProvider ,
95
95
EvaluationContext context ,
96
96
Func < FeatureProvider , Task > ? afterInitialization ,
97
- Func < FeatureProvider , Exception , Task > ? afterError )
97
+ Func < FeatureProvider , Exception , Task > ? afterError ,
98
+ CancellationToken cancellationToken = default )
98
99
{
99
100
if ( newProvider == null )
100
101
{
@@ -104,7 +105,7 @@ private static async Task InitProviderAsync(
104
105
{
105
106
try
106
107
{
107
- await newProvider . InitializeAsync ( context ) . ConfigureAwait ( false ) ;
108
+ await newProvider . InitializeAsync ( context , cancellationToken ) . ConfigureAwait ( false ) ;
108
109
if ( afterInitialization != null )
109
110
{
110
111
await afterInitialization . Invoke ( newProvider ) . ConfigureAwait ( false ) ;
@@ -134,15 +135,15 @@ private static async Task InitProviderAsync(
134
135
/// initialization
135
136
/// </param>
136
137
/// <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 ,
138
139
FeatureProvider ? featureProvider ,
139
140
EvaluationContext context ,
140
141
Func < FeatureProvider , Task > ? afterInitSuccess = null ,
141
142
Func < FeatureProvider , Exception , Task > ? afterInitError = null ,
142
143
CancellationToken cancellationToken = default )
143
144
{
144
145
// Cannot set a provider for a null domain.
145
- if ( domain == null )
146
+ if ( string . IsNullOrWhiteSpace ( domain ) )
146
147
{
147
148
return ;
148
149
}
@@ -166,21 +167,21 @@ public async Task SetProviderAsync(string? domain,
166
167
167
168
// We want to allow shutdown to happen concurrently with initialization, and the caller to not
168
169
// wait for it.
169
- _ = this . ShutdownIfUnusedAsync ( oldProvider ) ;
170
+ _ = this . ShutdownIfUnusedAsync ( oldProvider , cancellationToken ) ;
170
171
}
171
172
finally
172
173
{
173
174
this . _providersLock . ExitWriteLock ( ) ;
174
175
}
175
176
176
- await InitProviderAsync ( featureProvider , context , afterInitSuccess , afterInitError ) . ConfigureAwait ( false ) ;
177
+ await InitProviderAsync ( featureProvider , context , afterInitSuccess , afterInitError , cancellationToken ) . ConfigureAwait ( false ) ;
177
178
}
178
179
179
180
/// <remarks>
180
181
/// Shutdown the feature provider if it is unused. This must be called within a write lock of the _providersLock.
181
182
/// </remarks>
182
183
private async Task ShutdownIfUnusedAsync (
183
- FeatureProvider ? targetProvider )
184
+ FeatureProvider ? targetProvider , CancellationToken cancellationToken = default )
184
185
{
185
186
if ( ReferenceEquals ( this . _defaultProvider , targetProvider ) )
186
187
{
@@ -192,7 +193,7 @@ private async Task ShutdownIfUnusedAsync(
192
193
return ;
193
194
}
194
195
195
- await this . SafeShutdownProviderAsync ( targetProvider ) . ConfigureAwait ( false ) ;
196
+ await this . SafeShutdownProviderAsync ( targetProvider , cancellationToken ) . ConfigureAwait ( false ) ;
196
197
}
197
198
198
199
/// <remarks>
@@ -204,7 +205,7 @@ private async Task ShutdownIfUnusedAsync(
204
205
/// it would not be meaningful to emit an error.
205
206
/// </para>
206
207
/// </remarks>
207
- private async Task SafeShutdownProviderAsync ( FeatureProvider ? targetProvider )
208
+ private async Task SafeShutdownProviderAsync ( FeatureProvider ? targetProvider , CancellationToken cancellationToken = default )
208
209
{
209
210
if ( targetProvider == null )
210
211
{
@@ -213,15 +214,15 @@ private async Task SafeShutdownProviderAsync(FeatureProvider? targetProvider)
213
214
214
215
try
215
216
{
216
- await targetProvider . ShutdownAsync ( ) . ConfigureAwait ( false ) ;
217
+ await targetProvider . ShutdownAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
217
218
}
218
219
catch ( Exception ex )
219
220
{
220
221
this . ErrorShuttingDownProvider ( targetProvider . GetMetadata ( ) ? . Name , ex ) ;
221
222
}
222
223
}
223
224
224
- public FeatureProvider GetProvider ( )
225
+ internal FeatureProvider GetProvider ( )
225
226
{
226
227
this . _providersLock . EnterReadLock ( ) ;
227
228
try
@@ -234,16 +235,16 @@ public FeatureProvider GetProvider()
234
235
}
235
236
}
236
237
237
- public FeatureProvider GetProvider ( string ? domain )
238
+ internal FeatureProvider GetProvider ( string ? domain )
238
239
{
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 )
241
243
{
242
244
return this . GetProvider ( ) ;
243
245
}
244
246
#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 ) )
247
248
{
248
249
return this . GetProvider ( ) ;
249
250
}
@@ -254,7 +255,7 @@ public FeatureProvider GetProvider(string? domain)
254
255
: this . GetProvider ( ) ;
255
256
}
256
257
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 )
258
259
{
259
260
var providers = new HashSet < FeatureProvider > ( ) ;
260
261
this . _providersLock . EnterWriteLock ( ) ;
@@ -278,7 +279,7 @@ public async Task ShutdownAsync(Action<FeatureProvider, Exception>? afterError =
278
279
foreach ( var targetProvider in providers )
279
280
{
280
281
// 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 ) ;
282
283
}
283
284
}
284
285
0 commit comments