summaryrefslogtreecommitdiffhomepage
path: root/ios/MullvadVPN/Operations/ExclusivityController.swift
blob: 9e185168691676d14cab54cd8e0ba59b00784d1e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
//
//  ExclusivityController.swift
//  MullvadVPN
//
//  Created by pronebird on 06/07/2020.
//  Copyright © 2020 Mullvad VPN AB. All rights reserved.
//

import Foundation

class ExclusivityController<Category> where Category: Hashable {
    private let operationQueue: OperationQueue
    private let lock = NSRecursiveLock()

    private var operations: [Category: [Operation]] = [:]
    private var observers: [Operation: NSObjectProtocol] = [:]

    init(operationQueue: OperationQueue) {
        self.operationQueue = operationQueue
    }

    func addOperation(_ operation: Operation, categories: [Category]) {
        addOperations([operation], categories: categories)
    }

    func addOperations(_ operations: [Operation], categories: [Category]) {
        lock.withCriticalBlock {
            for operation in operations {
                for category in categories {
                    addDependencies(operation: operation, category: category)
                }

                observers[operation] = operation.observe(\.isFinished, options: [.initial, .new]) { [weak self] (op, change) in
                    if let isFinished = change.newValue, isFinished {
                        self?.operationDidFinish(op, categories: categories)
                    }
                }
            }

            operationQueue.addOperations(operations, waitUntilFinished: false)
        }
    }

    private func addDependencies(operation: Operation, category: Category) {
        var exclusiveOperations = self.operations[category] ?? []

        if let dependency = exclusiveOperations.last, !operation.dependencies.contains(dependency) {
            operation.addDependency(dependency)
        }

        exclusiveOperations.append(operation)
        self.operations[category] = exclusiveOperations
    }

    private func operationDidFinish(_ operation: Operation, categories: [Category]) {
        lock.withCriticalBlock {
            for category in categories {
                var exclusiveOperations = self.operations[category] ?? []

                exclusiveOperations.removeAll { (storedOperation) -> Bool in
                    return operation == storedOperation
                }

                self.operations[category] = exclusiveOperations
            }
            self.observers.removeValue(forKey: operation)
        }
    }
}