From e8544a3305f1b6808ffacfa07e955e4741330bd4 Mon Sep 17 00:00:00 2001 From: SeungYeop Yeom Date: Sat, 4 May 2024 13:50:03 +0900 Subject: [PATCH 1/2] Enhance the processing performance of the store in multi-threaded environments --- Sources/OneWay/Store.swift | 8 +++++--- Tests/OneWayTests/StoreTests.swift | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Sources/OneWay/Store.swift b/Sources/OneWay/Store.swift index cd6842b..788f32a 100644 --- a/Sources/OneWay/Store.swift +++ b/Sources/OneWay/Store.swift @@ -75,10 +75,11 @@ where R.Action: Sendable, R.State: Sendable & Equatable { public func send(_ action: Action) async { actionQueue.append(action) guard !isProcessing else { return } - isProcessing = true - while !actionQueue.isEmpty { - let action = actionQueue.removeFirst() + await Task.yield() + let count = actionQueue.count + for index in Int.zero ..< count { + let action = actionQueue[index] let taskID = TaskID() let effect = reducer.reduce(state: &state, action: action) let task = Task { [weak self, taskID] in @@ -107,6 +108,7 @@ where R.Action: Sendable, R.State: Sendable & Equatable { break } } + actionQueue = [] isProcessing = false } diff --git a/Tests/OneWayTests/StoreTests.swift b/Tests/OneWayTests/StoreTests.swift index b5d5adb..4e22c26 100644 --- a/Tests/OneWayTests/StoreTests.swift +++ b/Tests/OneWayTests/StoreTests.swift @@ -68,7 +68,7 @@ final class StoreTests: XCTestCase { } func test_threadSafeSendingActions() async { - let iterations: Int = 10_000 + let iterations: Int = 100_000 let sut = sut! DispatchQueue.concurrentPerform( iterations: iterations / 2, From bc8fc1da95d5cf335b936f9253007a87edce34c7 Mon Sep 17 00:00:00 2001 From: SeungYeop Yeom Date: Sat, 4 May 2024 14:02:44 +0900 Subject: [PATCH 2/2] Change the name of `_EffectID` to `EffectIDWrapper` --- Sources/OneWay/Store.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Sources/OneWay/Store.swift b/Sources/OneWay/Store.swift index 788f32a..a275683 100644 --- a/Sources/OneWay/Store.swift +++ b/Sources/OneWay/Store.swift @@ -44,7 +44,7 @@ where R.Action: Sendable, R.State: Sendable & Equatable { private var actionQueue: [Action] = [] private var bindingTask: Task? private var tasks: [TaskID: Task] = [:] - private var cancellables: [_EffectID: Set] = [:] + private var cancellables: [EffectIDWrapper: Set] = [:] /// Initializes a store from a reducer and an initial state. /// @@ -95,13 +95,13 @@ where R.Action: Sendable, R.State: Sendable & Equatable { switch effect.method { case let .register(id, cancelInFlight): if cancelInFlight { - let taskIDs = cancellables[_EffectID(id), default: []] + let taskIDs = cancellables[EffectIDWrapper(id), default: []] taskIDs.forEach { removeTask($0) } } - cancellables[_EffectID(id), default: []].insert(taskID) + cancellables[EffectIDWrapper(id), default: []].insert(taskID) case .cancel(let id): - let taskIDs = cancellables[_EffectID(id), default: []] + let taskIDs = cancellables[EffectIDWrapper(id), default: []] taskIDs.forEach { removeTask($0) } case .none: @@ -142,7 +142,7 @@ where R.Action: Sendable, R.State: Sendable & Equatable { } } -private struct _EffectID: Hashable, @unchecked Sendable { +private struct EffectIDWrapper: Hashable, @unchecked Sendable { private let id: AnyHashable fileprivate init(_ id: some Hashable & Sendable) {