1 |
efrain |
1 |
//
|
|
|
2 |
// Protected.swift
|
|
|
3 |
//
|
|
|
4 |
// Copyright (c) 2014-2020 Alamofire Software Foundation (http://alamofire.org/)
|
|
|
5 |
//
|
|
|
6 |
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
7 |
// of this software and associated documentation files (the "Software"), to deal
|
|
|
8 |
// in the Software without restriction, including without limitation the rights
|
|
|
9 |
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
10 |
// copies of the Software, and to permit persons to whom the Software is
|
|
|
11 |
// furnished to do so, subject to the following conditions:
|
|
|
12 |
//
|
|
|
13 |
// The above copyright notice and this permission notice shall be included in
|
|
|
14 |
// all copies or substantial portions of the Software.
|
|
|
15 |
//
|
|
|
16 |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
17 |
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
18 |
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
19 |
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
20 |
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
21 |
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
22 |
// THE SOFTWARE.
|
|
|
23 |
//
|
|
|
24 |
|
|
|
25 |
import Foundation
|
|
|
26 |
|
|
|
27 |
private protocol Lock {
|
|
|
28 |
func lock()
|
|
|
29 |
func unlock()
|
|
|
30 |
}
|
|
|
31 |
|
|
|
32 |
extension Lock {
|
|
|
33 |
/// Executes a closure returning a value while acquiring the lock.
|
|
|
34 |
///
|
|
|
35 |
/// - Parameter closure: The closure to run.
|
|
|
36 |
///
|
|
|
37 |
/// - Returns: The value the closure generated.
|
|
|
38 |
func around<T>(_ closure: () throws -> T) rethrows -> T {
|
|
|
39 |
lock(); defer { unlock() }
|
|
|
40 |
return try closure()
|
|
|
41 |
}
|
|
|
42 |
|
|
|
43 |
/// Execute a closure while acquiring the lock.
|
|
|
44 |
///
|
|
|
45 |
/// - Parameter closure: The closure to run.
|
|
|
46 |
func around(_ closure: () throws -> Void) rethrows {
|
|
|
47 |
lock(); defer { unlock() }
|
|
|
48 |
try closure()
|
|
|
49 |
}
|
|
|
50 |
}
|
|
|
51 |
|
|
|
52 |
#if os(Linux) || os(Windows)
|
|
|
53 |
|
|
|
54 |
extension NSLock: Lock {}
|
|
|
55 |
|
|
|
56 |
#endif
|
|
|
57 |
|
|
|
58 |
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
|
|
|
59 |
/// An `os_unfair_lock` wrapper.
|
|
|
60 |
final class UnfairLock: Lock {
|
|
|
61 |
private let unfairLock: os_unfair_lock_t
|
|
|
62 |
|
|
|
63 |
init() {
|
|
|
64 |
unfairLock = .allocate(capacity: 1)
|
|
|
65 |
unfairLock.initialize(to: os_unfair_lock())
|
|
|
66 |
}
|
|
|
67 |
|
|
|
68 |
deinit {
|
|
|
69 |
unfairLock.deinitialize(count: 1)
|
|
|
70 |
unfairLock.deallocate()
|
|
|
71 |
}
|
|
|
72 |
|
|
|
73 |
fileprivate func lock() {
|
|
|
74 |
os_unfair_lock_lock(unfairLock)
|
|
|
75 |
}
|
|
|
76 |
|
|
|
77 |
fileprivate func unlock() {
|
|
|
78 |
os_unfair_lock_unlock(unfairLock)
|
|
|
79 |
}
|
|
|
80 |
}
|
|
|
81 |
#endif
|
|
|
82 |
|
|
|
83 |
/// A thread-safe wrapper around a value.
|
|
|
84 |
@propertyWrapper
|
|
|
85 |
@dynamicMemberLookup
|
|
|
86 |
final class Protected<T> {
|
|
|
87 |
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
|
|
|
88 |
private let lock = UnfairLock()
|
|
|
89 |
#elseif os(Linux) || os(Windows)
|
|
|
90 |
private let lock = NSLock()
|
|
|
91 |
#endif
|
|
|
92 |
private var value: T
|
|
|
93 |
|
|
|
94 |
init(_ value: T) {
|
|
|
95 |
self.value = value
|
|
|
96 |
}
|
|
|
97 |
|
|
|
98 |
/// The contained value. Unsafe for anything more than direct read or write.
|
|
|
99 |
var wrappedValue: T {
|
|
|
100 |
get { lock.around { value } }
|
|
|
101 |
set { lock.around { value = newValue } }
|
|
|
102 |
}
|
|
|
103 |
|
|
|
104 |
var projectedValue: Protected<T> { self }
|
|
|
105 |
|
|
|
106 |
init(wrappedValue: T) {
|
|
|
107 |
value = wrappedValue
|
|
|
108 |
}
|
|
|
109 |
|
|
|
110 |
/// Synchronously read or transform the contained value.
|
|
|
111 |
///
|
|
|
112 |
/// - Parameter closure: The closure to execute.
|
|
|
113 |
///
|
|
|
114 |
/// - Returns: The return value of the closure passed.
|
|
|
115 |
func read<U>(_ closure: (T) throws -> U) rethrows -> U {
|
|
|
116 |
try lock.around { try closure(self.value) }
|
|
|
117 |
}
|
|
|
118 |
|
|
|
119 |
/// Synchronously modify the protected value.
|
|
|
120 |
///
|
|
|
121 |
/// - Parameter closure: The closure to execute.
|
|
|
122 |
///
|
|
|
123 |
/// - Returns: The modified value.
|
|
|
124 |
@discardableResult
|
|
|
125 |
func write<U>(_ closure: (inout T) throws -> U) rethrows -> U {
|
|
|
126 |
try lock.around { try closure(&self.value) }
|
|
|
127 |
}
|
|
|
128 |
|
|
|
129 |
subscript<Property>(dynamicMember keyPath: WritableKeyPath<T, Property>) -> Property {
|
|
|
130 |
get { lock.around { value[keyPath: keyPath] } }
|
|
|
131 |
set { lock.around { value[keyPath: keyPath] = newValue } }
|
|
|
132 |
}
|
|
|
133 |
|
|
|
134 |
subscript<Property>(dynamicMember keyPath: KeyPath<T, Property>) -> Property {
|
|
|
135 |
lock.around { value[keyPath: keyPath] }
|
|
|
136 |
}
|
|
|
137 |
}
|
|
|
138 |
|
|
|
139 |
extension Protected where T == Request.MutableState {
|
|
|
140 |
/// Attempts to transition to the passed `State`.
|
|
|
141 |
///
|
|
|
142 |
/// - Parameter state: The `State` to attempt transition to.
|
|
|
143 |
///
|
|
|
144 |
/// - Returns: Whether the transition occurred.
|
|
|
145 |
func attemptToTransitionTo(_ state: Request.State) -> Bool {
|
|
|
146 |
lock.around {
|
|
|
147 |
guard value.state.canTransitionTo(state) else { return false }
|
|
|
148 |
|
|
|
149 |
value.state = state
|
|
|
150 |
|
|
|
151 |
return true
|
|
|
152 |
}
|
|
|
153 |
}
|
|
|
154 |
|
|
|
155 |
/// Perform a closure while locked with the provided `Request.State`.
|
|
|
156 |
///
|
|
|
157 |
/// - Parameter perform: The closure to perform while locked.
|
|
|
158 |
func withState(perform: (Request.State) -> Void) {
|
|
|
159 |
lock.around { perform(value.state) }
|
|
|
160 |
}
|
|
|
161 |
}
|