Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions Sources/OneWay/Store.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ where R.Action: Sendable, R.State: Sendable & Equatable {
private var actionQueue: [Action] = []
private var bindingTask: Task<Void, Never>?
private var tasks: [TaskID: Task<Void, Never>] = [:]
private var cancellables: [_EffectID: Set<TaskID>] = [:]
private var cancellables: [EffectIDWrapper: Set<TaskID>] = [:]

/// Initializes a store from a reducer and an initial state.
///
Expand Down Expand Up @@ -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
Expand All @@ -94,19 +95,20 @@ 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:
break
}
}
actionQueue = []
isProcessing = false
}

Expand Down Expand Up @@ -140,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) {
Expand Down
2 changes: 1 addition & 1 deletion Tests/OneWayTests/StoreTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down