diff --git a/src/OpenFeature/Api.cs b/src/OpenFeature/Api.cs index d80590a2..6f13cac2 100644 --- a/src/OpenFeature/Api.cs +++ b/src/OpenFeature/Api.cs @@ -42,11 +42,10 @@ private Api() { } /// /// The provider cannot be set to null. Attempting to set the provider to null has no effect. /// Implementation of - /// The to cancel any async side effects. - public async Task SetProviderAsync(FeatureProvider featureProvider, CancellationToken cancellationToken = default) + public async Task SetProviderAsync(FeatureProvider featureProvider) { this._eventExecutor.RegisterDefaultFeatureProvider(featureProvider); - await this._repository.SetProviderAsync(featureProvider, this.GetContext(), cancellationToken: cancellationToken).ConfigureAwait(false); + await this._repository.SetProviderAsync(featureProvider, this.GetContext()).ConfigureAwait(false); } @@ -56,15 +55,14 @@ public async Task SetProviderAsync(FeatureProvider featureProvider, Cancellation /// /// Name of client /// Implementation of - /// The to cancel any async side effects. - public async Task SetProviderAsync(string clientName, FeatureProvider featureProvider, CancellationToken cancellationToken = default) + public async Task SetProviderAsync(string clientName, FeatureProvider featureProvider) { if (string.IsNullOrWhiteSpace(clientName)) { throw new ArgumentNullException(nameof(clientName)); } this._eventExecutor.RegisterClientFeatureProvider(clientName, featureProvider); - await this._repository.SetProviderAsync(clientName, featureProvider, this.GetContext(), cancellationToken: cancellationToken).ConfigureAwait(false); + await this._repository.SetProviderAsync(clientName, featureProvider, this.GetContext()).ConfigureAwait(false); } /// @@ -227,17 +225,18 @@ public EvaluationContext GetContext() /// Once shut down is complete, API is reset and ready to use again. /// /// - /// The to cancel any async side effects. - public async Task ShutdownAsync(CancellationToken cancellationToken = default) + public async Task ShutdownAsync() { - await this._repository.ShutdownAsync(cancellationToken: cancellationToken).ConfigureAwait(false); - await this._eventExecutor.ShutdownAsync(cancellationToken).ConfigureAwait(false); - this._evaluationContext = EvaluationContext.Empty; - this._hooks.Clear(); + await using (this._eventExecutor.ConfigureAwait(false)) + await using (this._repository.ConfigureAwait(false)) + { + this._evaluationContext = EvaluationContext.Empty; + this._hooks.Clear(); - // TODO: make these lazy to avoid extra allocations on the common cleanup path? - this._eventExecutor = new EventExecutor(); - this._repository = new ProviderRepository(); + // TODO: make these lazy to avoid extra allocations on the common cleanup path? + this._eventExecutor = new EventExecutor(); + this._repository = new ProviderRepository(); + } } /// diff --git a/src/OpenFeature/EventExecutor.cs b/src/OpenFeature/EventExecutor.cs index 47c30f66..886a47b6 100644 --- a/src/OpenFeature/EventExecutor.cs +++ b/src/OpenFeature/EventExecutor.cs @@ -12,7 +12,7 @@ namespace OpenFeature { internal delegate Task ShutdownDelegate(CancellationToken cancellationToken); - internal sealed partial class EventExecutor + internal sealed partial class EventExecutor : IAsyncDisposable { private readonly object _lockObj = new object(); public readonly Channel EventChannel = Channel.CreateBounded(1); @@ -32,6 +32,8 @@ public EventExecutor() eventProcessing.Start(); } + public ValueTask DisposeAsync() => new(this.ShutdownAsync()); + internal void SetLogger(ILogger logger) => this._logger = logger; internal void AddApiLevelHandler(ProviderEventTypes eventType, EventHandlerDelegate handler) @@ -317,7 +319,7 @@ private void InvokeEventHandler(EventHandlerDelegate eventHandler, Event e) } } - public async Task ShutdownAsync(CancellationToken cancellationToken = default) + public async Task ShutdownAsync() { this.EventChannel.Writer.Complete(); await this.EventChannel.Reader.Completion.ConfigureAwait(false); diff --git a/src/OpenFeature/ProviderRepository.cs b/src/OpenFeature/ProviderRepository.cs index a21f8679..7934da1c 100644 --- a/src/OpenFeature/ProviderRepository.cs +++ b/src/OpenFeature/ProviderRepository.cs @@ -62,15 +62,13 @@ public async ValueTask DisposeAsync() /// initialization /// /// called after a provider is shutdown, can be used to remove event handlers - /// The to cancel any async side effects. - public async ValueTask SetProviderAsync( + public async Task SetProviderAsync( FeatureProvider? featureProvider, EvaluationContext context, Action? afterSet = null, Action? afterInitialization = null, Action? afterError = null, - Action? afterShutdown = null, - CancellationToken cancellationToken = default) + Action? afterShutdown = null) { // Cannot unset the feature provider. if (featureProvider == null) @@ -94,7 +92,7 @@ public async ValueTask SetProviderAsync( // We want to allow shutdown to happen concurrently with initialization, and the caller to not // wait for it. #pragma warning disable CS4014 - this.ShutdownIfUnusedAsync(oldProvider, afterShutdown, afterError, cancellationToken); + this.ShutdownIfUnusedAsync(oldProvider, afterShutdown, afterError); #pragma warning restore CS4014 } finally @@ -102,16 +100,15 @@ public async ValueTask SetProviderAsync( this._providersLock.ExitWriteLock(); } - await InitProviderAsync(this._defaultProvider, context, afterInitialization, afterError, cancellationToken) + await InitProviderAsync(this._defaultProvider, context, afterInitialization, afterError) .ConfigureAwait(false); } - private static async ValueTask InitProviderAsync( + private static async Task InitProviderAsync( FeatureProvider? newProvider, EvaluationContext context, Action? afterInitialization, - Action? afterError, - CancellationToken cancellationToken) + Action? afterError) { if (newProvider == null) { @@ -121,7 +118,7 @@ private static async ValueTask InitProviderAsync( { try { - await newProvider.InitializeAsync(context, cancellationToken).ConfigureAwait(false); + await newProvider.InitializeAsync(context).ConfigureAwait(false); afterInitialization?.Invoke(newProvider); } catch (Exception ex) @@ -156,7 +153,7 @@ private static async ValueTask InitProviderAsync( /// /// called after a provider is shutdown, can be used to remove event handlers /// The to cancel any async side effects. - public async ValueTask SetProviderAsync(string clientName, + public async Task SetProviderAsync(string clientName, FeatureProvider? featureProvider, EvaluationContext context, Action? afterSet = null, @@ -192,7 +189,7 @@ public async ValueTask SetProviderAsync(string clientName, // We want to allow shutdown to happen concurrently with initialization, and the caller to not // wait for it. #pragma warning disable CS4014 - this.ShutdownIfUnusedAsync(oldProvider, afterShutdown, afterError, cancellationToken); + this.ShutdownIfUnusedAsync(oldProvider, afterShutdown, afterError); #pragma warning restore CS4014 } finally @@ -200,17 +197,16 @@ public async ValueTask SetProviderAsync(string clientName, this._providersLock.ExitWriteLock(); } - await InitProviderAsync(featureProvider, context, afterInitialization, afterError, cancellationToken).ConfigureAwait(false); + await InitProviderAsync(featureProvider, context, afterInitialization, afterError).ConfigureAwait(false); } /// /// Shutdown the feature provider if it is unused. This must be called within a write lock of the _providersLock. /// - private async ValueTask ShutdownIfUnusedAsync( + private async Task ShutdownIfUnusedAsync( FeatureProvider? targetProvider, Action? afterShutdown, - Action? afterError, - CancellationToken cancellationToken) + Action? afterError) { if (ReferenceEquals(this._defaultProvider, targetProvider)) { @@ -222,7 +218,7 @@ private async ValueTask ShutdownIfUnusedAsync( return; } - await SafeShutdownProviderAsync(targetProvider, afterShutdown, afterError, cancellationToken).ConfigureAwait(false); + await SafeShutdownProviderAsync(targetProvider, afterShutdown, afterError).ConfigureAwait(false); } /// @@ -234,10 +230,9 @@ private async ValueTask ShutdownIfUnusedAsync( /// it would not be meaningful to emit an error. /// /// - private static async ValueTask SafeShutdownProviderAsync(FeatureProvider? targetProvider, + private static async Task SafeShutdownProviderAsync(FeatureProvider? targetProvider, Action? afterShutdown, - Action? afterError, - CancellationToken cancellationToken) + Action? afterError) { if (targetProvider == null) { @@ -246,7 +241,7 @@ private static async ValueTask SafeShutdownProviderAsync(FeatureProvider? target try { - await targetProvider.ShutdownAsync(cancellationToken).ConfigureAwait(false); + await targetProvider.ShutdownAsync().ConfigureAwait(false); afterShutdown?.Invoke(targetProvider); } catch (Exception ex) @@ -288,7 +283,7 @@ public FeatureProvider GetProvider(string? clientName) : this.GetProvider(); } - public async ValueTask ShutdownAsync(Action? afterError = null, CancellationToken cancellationToken = default) + public async Task ShutdownAsync(Action? afterError = null, CancellationToken cancellationToken = default) { var providers = new HashSet(); this._providersLock.EnterWriteLock(); @@ -312,7 +307,7 @@ public async ValueTask ShutdownAsync(Action? afterEr foreach (var targetProvider in providers) { // We don't need to take any actions after shutdown. - await SafeShutdownProviderAsync(targetProvider, null, afterError, cancellationToken).ConfigureAwait(false); + await SafeShutdownProviderAsync(targetProvider, null, afterError).ConfigureAwait(false); } } }