Last active
October 28, 2019 12:46
-
-
Save jeudesprits/8814279336d38de8167fd2426a63e995 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class PSGroupOperation: PSOperation { | |
private let lock = UnfairLock() | |
private let internalQueue: PSOperationQueue | |
private let startingOperation = BlockOperation() | |
private let finishingOperation = BlockOperation() | |
private var aggregatedErrors = [Error]() | |
convenience init(underlyingQueue: DispatchQueue = .global(qos: .default), operations: Operation...) { | |
self.init(underlyingQueue: underlyingQueue, operations: operations) | |
} | |
init(underlyingQueue: DispatchQueue = .global(qos: .default), operations: [Operation]) { | |
internalQueue = PSOperationQueue() | |
internalQueue.underlyingQueue = underlyingQueue | |
internalQueue.isSuspended = true | |
super.init() | |
internalQueue.delegate = self | |
internalQueue.addOperation(startingOperation) | |
for operation in operations { internalQueue.addOperation(operation) } | |
} | |
override func cancel() { | |
internalQueue.cancelAllOperations() | |
super.cancel() | |
} | |
override func execute() { | |
internalQueue.isSuspended = false | |
internalQueue.addOperation(finishingOperation) | |
} | |
func addOperation(operation: Operation) { | |
internalQueue.addOperation(operation) | |
} | |
final func aggregate(_ error: Error) { | |
aggregatedErrors.append(error) | |
} | |
func operationDidFinish(_ operation: Operation, with errors: [Error]) { } | |
} | |
extension PSGroupOperation: PSOperationQueueDelegate { | |
final func operationQueue(_: PSOperationQueue, willAdd operation: Operation) { | |
assert( | |
!finishingOperation.isFinished && !finishingOperation.isExecuting, | |
"Сannot add new operations to a group after the group has completed" | |
) | |
// Some operation in this group has produced a new operation to execute. | |
// We want to allow that operation to execute before the group completes, | |
// so we'll make the finishing operation dependent on this newly-produced operation. | |
if operation !== finishingOperation { finishingOperation.addDependency(operation) } | |
// All operations should be dependent on the "startingOperation". | |
// This way, we can guarantee that the conditions for other operations | |
// will not evaluate until just before the operation is about to run. | |
// Otherwise, the conditions could be evaluated at any time, even | |
// before the internal operation queue is unsuspended. | |
if operation !== startingOperation { operation.addDependency(startingOperation) } | |
} | |
final func operationQueue( | |
_ operationQueue: PSOperationQueue, | |
didFinish operation: Operation, | |
with errors: [Error] | |
) { | |
lock.lock() | |
defer { lock.unlock() } | |
aggregatedErrors.append(contentsOf: errors) | |
if operation === finishingOperation { | |
internalQueue.isSuspended = true | |
finish(with: aggregatedErrors) | |
} else if operation !== startingOperation { | |
operationDidFinish(operation, with: errors) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment