summaryrefslogtreecommitdiffhomepage
path: root/ios/Operations/AsyncOperationQueue.swift
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2022-09-25 16:34:23 +0200
committerAndrej Mihajlov <and@mullvad.net>2022-09-26 16:35:38 +0200
commitf389956c2cd884df142adbf00ff2ac7e2f69c2b2 (patch)
tree5f7d4869324760eb4ba0107c0356edc89efd1457 /ios/Operations/AsyncOperationQueue.swift
parent2e83b1ca27ff243a615ff10c94c20840b38dfd45 (diff)
downloadmullvadvpn-f389956c2cd884df142adbf00ff2ac7e2f69c2b2.tar.xz
mullvadvpn-f389956c2cd884df142adbf00ff2ac7e2f69c2b2.zip
Move AsyncOperation into Operations static library and add separate tests
Diffstat (limited to 'ios/Operations/AsyncOperationQueue.swift')
-rw-r--r--ios/Operations/AsyncOperationQueue.swift98
1 files changed, 98 insertions, 0 deletions
diff --git a/ios/Operations/AsyncOperationQueue.swift b/ios/Operations/AsyncOperationQueue.swift
new file mode 100644
index 0000000000..1c62038671
--- /dev/null
+++ b/ios/Operations/AsyncOperationQueue.swift
@@ -0,0 +1,98 @@
+//
+// AsyncOperationQueue.swift
+// MullvadVPN
+//
+// Created by pronebird on 30/05/2022.
+// Copyright © 2022 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+
+public class AsyncOperationQueue: OperationQueue {
+ override public func addOperation(_ operation: Operation) {
+ if let operation = operation as? AsyncOperation {
+ let categories = operation.conditions
+ .filter { condition in
+ return condition.isMutuallyExclusive
+ }
+ .map { condition in
+ return condition.name
+ }
+
+ if !categories.isEmpty {
+ ExclusivityManager.shared.addOperation(operation, categories: Set(categories))
+ }
+
+ super.addOperation(operation)
+
+ operation.didEnqueue()
+ } else {
+ super.addOperation(operation)
+ }
+ }
+
+ override public func addOperations(_ operations: [Operation], waitUntilFinished wait: Bool) {
+ for operation in operations {
+ addOperation(operation)
+ }
+
+ if wait {
+ for operation in operations {
+ operation.waitUntilFinished()
+ }
+ }
+ }
+}
+
+private final class ExclusivityManager {
+ static let shared = ExclusivityManager()
+
+ private var operationsByCategory = [String: [Operation]]()
+ private let nslock = NSLock()
+
+ private init() {}
+
+ func addOperation(_ operation: AsyncOperation, categories: Set<String>) {
+ nslock.lock()
+ defer { nslock.unlock() }
+
+ for category in categories {
+ var operations = operationsByCategory[category] ?? []
+
+ if let lastOperation = operations.last {
+ operation.addDependency(lastOperation)
+ }
+
+ operations.append(operation)
+
+ operationsByCategory[category] = operations
+
+ let blockObserver = OperationBlockObserver(didFinish: { [weak self] op, error in
+ self?.removeOperation(op, categories: categories)
+ })
+
+ operation.addObserver(blockObserver)
+ }
+ }
+
+ private func removeOperation(_ operation: Operation, categories: Set<String>) {
+ nslock.lock()
+ defer { nslock.unlock() }
+
+ for category in categories {
+ guard var operations = operationsByCategory[category] else {
+ continue
+ }
+
+ if let index = operations.firstIndex(of: operation) {
+ operations.remove(at: index)
+ }
+
+ if operations.isEmpty {
+ operationsByCategory.removeValue(forKey: category)
+ } else {
+ operationsByCategory[category] = operations
+ }
+ }
+ }
+}