11import Combine
22import Foundation
33
4- /// Simple async semaphore for serializing operations
5- private actor AsyncSemaphore {
6- private var isAvailable = true
7- private var waiters : [ CheckedContinuation < Void , Never > ] = [ ]
8-
9- func wait( ) async {
10- if isAvailable {
11- isAvailable = false
12- return
13- }
14-
15- await withCheckedContinuation { continuation in
16- waiters. append ( continuation)
17- }
18- }
19-
20- func signal( ) {
21- if let waiter = waiters. first {
22- waiters. removeFirst ( )
23- waiter. resume ( )
24- } else {
25- isAvailable = true
4+ /// Simple serial async task queue for serializing operations
5+ private actor AsyncSerialQueue {
6+ private var last : Task < Void , Never > ? = nil
7+
8+ /// Runs the given operation after previously enqueued work completes.
9+ func run( _ operation: @Sendable @escaping ( ) async -> Void ) async {
10+ let previous = last
11+ let task = Task {
12+ _ = await previous? . result
13+ await operation ( )
2614 }
15+ last = task
16+ await task. value
2717 }
2818}
2919
@@ -32,7 +22,7 @@ private actor AsyncSemaphore {
3222public class OpenFeatureAPI {
3323 private let eventHandler = EventHandler ( )
3424 private let queue = DispatchQueue ( label: " com.openfeature.providerDescriptor.queue " )
35- private let contextUpdateSemaphore = AsyncSemaphore ( )
25+ private let contextUpdateQueue = AsyncSerialQueue ( )
3626
3727 private( set) var providerSubject = CurrentValueSubject < FeatureProvider ? , Never > ( nil )
3828 private( set) var evaluationContext : EvaluationContext ?
@@ -196,26 +186,24 @@ public class OpenFeatureAPI {
196186 }
197187
198188 private func updateContext( evaluationContext: EvaluationContext ) async {
199- // Ensure only ONE updateContext operation runs at a time
200- await contextUpdateSemaphore. wait ( )
201- defer { Task { await contextUpdateSemaphore. signal ( ) } }
202-
203- do {
204- let oldContext = self . evaluationContext
205- self . evaluationContext = evaluationContext
206- self . providerStatus = . reconciling
207- eventHandler. send ( . reconciling( nil ) )
208-
209- try await self . providerSubject. value? . onContextSet (
210- oldContext: oldContext,
211- newContext: evaluationContext
212- )
213-
214- self . providerStatus = . ready
215- eventHandler. send ( . contextChanged( nil ) )
216- } catch {
217- self . providerStatus = . error
218- eventHandler. send ( . error( ProviderEventDetails ( message: error. localizedDescription) ) )
189+ await contextUpdateQueue. run { [ self ] in
190+ do {
191+ let oldContext = self . evaluationContext
192+ self . evaluationContext = evaluationContext
193+ self . providerStatus = . reconciling
194+ eventHandler. send ( . reconciling( nil ) )
195+
196+ try await self . providerSubject. value? . onContextSet (
197+ oldContext: oldContext,
198+ newContext: evaluationContext
199+ )
200+
201+ self . providerStatus = . ready
202+ eventHandler. send ( . contextChanged( nil ) )
203+ } catch {
204+ self . providerStatus = . error
205+ eventHandler. send ( . error( ProviderEventDetails ( message: error. localizedDescription) ) )
206+ }
219207 }
220208 }
221209
0 commit comments