Sources/Concurrency/Executor/ImmediateSerialSequenceExecutor.swift (50 lines of code) (raw):
//
// Copyright (c) 2018. Uber Technologies
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
/// An executor that executes sequences of tasks serially from the
/// caller thread as soon as the execute method is invoked.
///
/// - note: Generally this implementation should only be used for debugging
/// purposes, as debugging highly concurrent task executions can be very
/// challenging. Production code should use `ConcurrentSequenceExecutor`.
/// - seeAlso: `SequenceExecutor`.
/// - seeAlso: `Task`.
public class ImmediateSerialSequenceExecutor: SequenceExecutor {
/// Initializer.
public init() {}
/// Execute a sequence of tasks serially from the given initial task
/// on the caller thread, immediately.
///
/// - parameter initialTask: The root task of the sequence of tasks
/// to be executed.
/// - parameter execution: The execution defining the sequence of tasks.
/// When a task completes its execution, this closure is invoked with
/// the task and its produced result. This closure is invoked from
/// the caller thread serially as each task completes. The tasks provided
/// by this closure are executed serially on the initial caller thread.
/// - returns: The execution handle that allows control and monitoring
/// of the sequence of tasks being executed.
public func executeSequence<SequenceResultType>(from initialTask: Task, with execution: @escaping (Task, Any) -> SequenceExecution<SequenceResultType>) -> SequenceExecutionHandle<SequenceResultType> {
let handle: SequenceExecutionHandleImpl<SequenceResultType> = SequenceExecutionHandleImpl()
execute(initialTask, with: handle, execution)
return handle
}
// MARK: - Private
private func execute<SequenceResultType>(_ task: Task, with sequenceHandle: SequenceExecutionHandleImpl<SequenceResultType>, _ execution: @escaping (Task, Any) -> SequenceExecution<SequenceResultType>) {
guard !sequenceHandle.isCancelled else {
return
}
do {
let result = try task.typeErasedExecute()
let nextExecution = execution(task, result)
switch nextExecution {
case .continueSequence(let nextTask):
self.execute(nextTask, with: sequenceHandle, execution)
case .endOfSequence(let result):
sequenceHandle.sequenceDidComplete(with: result)
}
} catch {
sequenceHandle.sequenceDidError(with: error)
}
}
}
private class SequenceExecutionHandleImpl<SequenceResultType>: SequenceExecutionHandle<SequenceResultType> {
private var didCancel = false
private var result: SequenceResultType?
private var error: Error?
fileprivate var isCancelled: Bool {
return didCancel
}
fileprivate override func await(withTimeout timeout: TimeInterval?) throws -> SequenceResultType {
if let error = self.error {
throw error
} else {
return result!
}
}
fileprivate func sequenceDidComplete(with result: SequenceResultType) {
self.result = result
}
fileprivate func sequenceDidError(with error: Error) {
self.error = error
}
fileprivate override func cancel() {
didCancel = true
}
}