blob: a557aabf16696764428accf0ef735f39c7c0f2d7 (
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
|
//
// ResultOperation.swift
// Operations
//
// Created by pronebird on 23/03/2022.
// Copyright © 2025 Mullvad VPN AB. All rights reserved.
//
import Foundation
/// Base class for operations producing result.
open class ResultOperation<Success: Sendable>: AsyncOperation, OutputOperation, @unchecked Sendable {
public typealias CompletionHandler = (sending Result<Success, Error>) -> Void
private let nslock = NSLock()
private var _output: Success?
private var _completionQueue: DispatchQueue?
private var _completionHandler: CompletionHandler?
private var pendingFinish = false
public var result: Result<Success, Error>? {
nslock.lock()
defer { nslock.unlock() }
return _output.map { .success($0) } ?? error.map { .failure($0) }
}
public var output: Success? {
nslock.lock()
defer { nslock.unlock() }
return _output
}
public var completionQueue: DispatchQueue? {
get {
nslock.lock()
defer { nslock.unlock() }
return _completionQueue
}
set {
nslock.lock()
defer { nslock.unlock() }
_completionQueue = newValue
}
}
public var completionHandler: CompletionHandler? {
get {
nslock.lock()
defer { nslock.unlock() }
return _completionHandler
}
set {
nslock.lock()
defer { nslock.unlock() }
if !pendingFinish {
_completionHandler = newValue
}
}
}
override public init(dispatchQueue: DispatchQueue?) {
super.init(dispatchQueue: dispatchQueue)
}
public init(
dispatchQueue: DispatchQueue?,
completionQueue: DispatchQueue?,
completionHandler: CompletionHandler?
) {
_completionQueue = completionQueue
_completionHandler = completionHandler
super.init(dispatchQueue: dispatchQueue)
}
@available(*, unavailable)
override public func finish() {
_finish(result: .failure(OperationError.cancelled))
}
@available(*, unavailable)
override public func finish(error: Error?) {
_finish(result: .failure(error ?? OperationError.cancelled))
}
open func finish(result: Result<Success, Error>) {
_finish(result: result)
}
private func _finish(result: Result<Success, Error>) {
nslock.lock()
// Bail if operation is already finishing.
guard !pendingFinish else {
nslock.unlock()
return
}
// Mark that operation is pending finish.
pendingFinish = true
// Copy completion handler.
nonisolated(unsafe) let completionHandler = _completionHandler
// Unset completion handler.
_completionHandler = nil
// Copy completion value.
if case let .success(output) = result {
_output = output
}
// Copy completion queue.
let completionQueue = _completionQueue
nslock.unlock()
dispatchAsyncOn(completionQueue) {
completionHandler?(result)
var error: Error?
if case let .failure(failure) = result {
error = failure
}
// Finish operation.
super.finish(error: error)
}
}
private func dispatchAsyncOn(_ queue: DispatchQueue?, _ block: @escaping @Sendable () -> Void) {
guard let queue else {
block()
return
}
queue.async(execute: block)
}
}
|