|  | 
| 29 | 29 | //  SOFTWARE. | 
| 30 | 30 | // | 
| 31 | 31 | 
 | 
|  | 32 | +#if compiler(>=6) && !canImport(WinSDK) | 
|  | 33 | + | 
| 32 | 34 | // Backports the Swift 6 type Mutex<Value> to all Darwin platforms | 
| 33 | 35 | 
 | 
| 34 |  | -// @available(macOS, deprecated: 15.0, message: "use Mutex from Synchronization module included with Swift 6") | 
| 35 |  | -// @available(iOS, deprecated: 18.0, message: "use Mutex from Synchronization module included with Swift 6") | 
| 36 |  | -// @available(tvOS, deprecated: 18.0, message: "use Mutex from Synchronization module included with Swift 6") | 
| 37 |  | -// @available(watchOS, deprecated: 11.0, message: "use Mutex from Synchronization module included with Swift 6") | 
| 38 |  | -// @available(visionOS, deprecated: 2.0, message: "use Mutex from Synchronization module included with Swift 6") | 
| 39 |  | -public struct Mutex<Value>: Sendable { | 
| 40 |  | -    let lock: AllocatedLock<Value> // Compatible with OSAllocatedUnfairLock iOS 16+ | 
| 41 |  | -} | 
|  | 36 | +@available(macOS, introduced: 13.0, deprecated: 15.0, message: "use Mutex from Synchronization module") | 
|  | 37 | +@available(iOS, introduced: 16.0, deprecated: 18.0, message: "use Mutex from Synchronization module") | 
|  | 38 | +@available(tvOS, introduced: 18.0, deprecated: 15.0, message: "use Mutex from Synchronization module") | 
|  | 39 | +@available(watchOS, introduced: 11.0, deprecated: 15.0, message: "use Mutex from Synchronization module") | 
|  | 40 | +@available(visionOS, introduced: 2.0, deprecated: 15.0, message: "use Mutex from Synchronization module") | 
|  | 41 | +public struct Mutex<Value: ~Copyable>: @unchecked Sendable, ~Copyable { | 
|  | 42 | +    let storage: Storage<Value> | 
| 42 | 43 | 
 | 
| 43 |  | -#if compiler(>=6) | 
| 44 |  | -public extension Mutex { | 
| 45 |  | -    init(_ initialValue: consuming sending Value) { | 
| 46 |  | -        self.lock = AllocatedLock(uncheckedState: initialValue) | 
|  | 44 | +    public init(_ initialValue: consuming sending Value) { | 
|  | 45 | +        self.storage = Storage(initialValue) | 
| 47 | 46 |     } | 
| 48 | 47 | 
 | 
| 49 |  | -    borrowing func withLock<Result, E: Error>( | 
|  | 48 | +    public borrowing func withLock<Result, E: Error>( | 
| 50 | 49 |         _ body: (inout sending Value) throws(E) -> sending Result | 
| 51 | 50 |     ) throws(E) -> sending Result { | 
| 52 |  | -        do { | 
| 53 |  | -            return try lock.withLockUnchecked { value in | 
| 54 |  | -                nonisolated(unsafe) var copy = value | 
| 55 |  | -                defer { value = copy } | 
| 56 |  | -                return try Transferring(body(©)) | 
| 57 |  | -            }.value | 
| 58 |  | -        } catch let error as E { | 
| 59 |  | -            throw error | 
| 60 |  | -        } catch { | 
| 61 |  | -            preconditionFailure("cannot occur") | 
| 62 |  | -        } | 
| 63 |  | -    } | 
| 64 |  | - | 
| 65 |  | -    borrowing func withLockIfAvailable<Result, E>( | 
|  | 51 | +        storage.lock() | 
|  | 52 | +        defer { storage.unlock() } | 
|  | 53 | +        return try body(&storage.value) | 
|  | 54 | +    } | 
|  | 55 | + | 
|  | 56 | +    public borrowing func withLockIfAvailable<Result, E: Error>( | 
| 66 | 57 |         _ body: (inout sending Value) throws(E) -> sending Result | 
| 67 |  | -    ) throws(E) -> sending Result? where E: Error { | 
| 68 |  | -        do { | 
| 69 |  | -            return try lock.withLockIfAvailableUnchecked { value in | 
| 70 |  | -                nonisolated(unsafe) var copy = value | 
| 71 |  | -                defer { value = copy } | 
| 72 |  | -                return try Transferring(body(©)) | 
| 73 |  | -            }?.value | 
| 74 |  | -        } catch let error as E { | 
| 75 |  | -            throw error | 
| 76 |  | -        } catch { | 
| 77 |  | -            preconditionFailure("cannot occur") | 
| 78 |  | -        } | 
|  | 58 | +    ) throws(E) -> sending Result? { | 
|  | 59 | +        guard storage.tryLock() else { return nil } | 
|  | 60 | +        defer { storage.unlock() } | 
|  | 61 | +        return try body(&storage.value) | 
| 79 | 62 |     } | 
| 80 | 63 | } | 
| 81 |  | -private struct Transferring<T> { | 
| 82 |  | -    nonisolated(unsafe) var value: T | 
| 83 | 64 | 
 | 
| 84 |  | -    init(_ value: T) { | 
| 85 |  | -        self.value = value | 
|  | 65 | +#elseif compiler(>=6) && canImport(WinSDK) | 
|  | 66 | + | 
|  | 67 | +// Windows doesn't support ~Copyable yet | 
|  | 68 | + | 
|  | 69 | +public struct Mutex<Value>: @unchecked Sendable { | 
|  | 70 | +    let storage: Storage<Value> | 
|  | 71 | + | 
|  | 72 | +    public init(_ initialValue: consuming sending Value) { | 
|  | 73 | +        self.storage = Storage(initialValue) | 
|  | 74 | +    } | 
|  | 75 | + | 
|  | 76 | +    public borrowing func withLock<Result, E: Error>( | 
|  | 77 | +        _ body: (inout sending Value) throws(E) -> sending Result | 
|  | 78 | +    ) throws(E) -> sending Result { | 
|  | 79 | +        storage.lock() | 
|  | 80 | +        defer { storage.unlock() } | 
|  | 81 | +        return try body(&storage.value) | 
|  | 82 | +    } | 
|  | 83 | + | 
|  | 84 | +    public borrowing func withLockIfAvailable<Result, E: Error>( | 
|  | 85 | +        _ body: (inout sending Value) throws(E) -> sending Result | 
|  | 86 | +    ) throws(E) -> sending Result? { | 
|  | 87 | +        guard storage.tryLock() else { return nil } | 
|  | 88 | +        defer { storage.unlock() } | 
|  | 89 | +        return try body(&storage.value) | 
| 86 | 90 |     } | 
| 87 | 91 | } | 
|  | 92 | + | 
| 88 | 93 | #else | 
| 89 |  | -public extension Mutex { | 
| 90 |  | -    init(_ initialValue: consuming Value) { | 
| 91 |  | -        self.lock = AllocatedLock(uncheckedState: initialValue) | 
|  | 94 | + | 
|  | 95 | +// Use Swift 5 compatible version | 
|  | 96 | + | 
|  | 97 | +public struct Mutex<Value>: @unchecked Sendable { | 
|  | 98 | +    let storage: Storage<Value> | 
|  | 99 | + | 
|  | 100 | +    public init(_ initialValue: Value) { | 
|  | 101 | +        self.storage = Storage(initialValue) | 
| 92 | 102 |     } | 
| 93 | 103 | 
 | 
| 94 |  | -    borrowing func withLock<Result>( | 
|  | 104 | +    public borrowing func withLock<Result>( | 
| 95 | 105 |         _ body: (inout Value) throws -> Result | 
| 96 | 106 |     ) rethrows -> Result { | 
| 97 |  | -        try lock.withLockUnchecked { | 
| 98 |  | -            return try body(&$0) | 
| 99 |  | -        } | 
|  | 107 | +        storage.lock() | 
|  | 108 | +        defer { storage.unlock() } | 
|  | 109 | +        return try body(&storage.value) | 
| 100 | 110 |     } | 
| 101 | 111 | 
 | 
| 102 |  | -    borrowing func withLockIfAvailable<Result>( | 
|  | 112 | +    public borrowing func withLockIfAvailable<Result>( | 
| 103 | 113 |         _ body: (inout Value) throws -> Result | 
| 104 | 114 |     ) rethrows -> Result? { | 
| 105 |  | -        try lock.withLockIfAvailableUnchecked { | 
| 106 |  | -            return try body(&$0) | 
| 107 |  | -        } | 
|  | 115 | +        guard storage.tryLock() else { return nil } | 
|  | 116 | +        defer { storage.unlock() } | 
|  | 117 | +        return try body(&storage.value) | 
| 108 | 118 |     } | 
| 109 | 119 | } | 
|  | 120 | + | 
|  | 121 | +#endif | 
|  | 122 | + | 
|  | 123 | +#if canImport(Darwin) | 
|  | 124 | + | 
|  | 125 | +import struct os.os_unfair_lock_t | 
|  | 126 | +import struct os.os_unfair_lock | 
|  | 127 | +import func os.os_unfair_lock_lock | 
|  | 128 | +import func os.os_unfair_lock_unlock | 
|  | 129 | +import func os.os_unfair_lock_trylock | 
|  | 130 | + | 
|  | 131 | +final class Storage<Value: ~Copyable> { | 
|  | 132 | +    private let _lock: os_unfair_lock_t | 
|  | 133 | + | 
|  | 134 | +    var value: Value | 
|  | 135 | + | 
|  | 136 | +    init(_ initialValue: consuming Value) { | 
|  | 137 | +        self._lock = .allocate(capacity: 1) | 
|  | 138 | +        self._lock.initialize(to: os_unfair_lock()) | 
|  | 139 | +        self.value = initialValue | 
|  | 140 | +    } | 
|  | 141 | + | 
|  | 142 | +    func lock() { | 
|  | 143 | +        os_unfair_lock_lock(_lock) | 
|  | 144 | +    } | 
|  | 145 | + | 
|  | 146 | +    func unlock() { | 
|  | 147 | +        os_unfair_lock_unlock(_lock) | 
|  | 148 | +    } | 
|  | 149 | + | 
|  | 150 | +    func tryLock() -> Bool { | 
|  | 151 | +        os_unfair_lock_trylock(_lock) | 
|  | 152 | +    } | 
|  | 153 | + | 
|  | 154 | +    deinit { | 
|  | 155 | +        self._lock.deinitialize(count: 1) | 
|  | 156 | +        self._lock.deallocate() | 
|  | 157 | +    } | 
|  | 158 | +} | 
|  | 159 | + | 
|  | 160 | + | 
|  | 161 | +#elseif canImport(Glibc) || canImport(Musl) || canImport(Bionic) | 
|  | 162 | + | 
|  | 163 | +#if canImport(Musl) | 
|  | 164 | +import Musl | 
|  | 165 | +#elseif canImport(Bionic) | 
|  | 166 | +import Android | 
|  | 167 | +#else | 
|  | 168 | +import Glibc | 
|  | 169 | +#endif | 
|  | 170 | + | 
|  | 171 | +final class Storage<Value: ~Copyable> { | 
|  | 172 | +    private let _lock: UnsafeMutablePointer<pthread_mutex_t> | 
|  | 173 | + | 
|  | 174 | +    var value: Value | 
|  | 175 | + | 
|  | 176 | +    init(_ initialValue: consuming Value) { | 
|  | 177 | +        var attr = pthread_mutexattr_t() | 
|  | 178 | +        pthread_mutexattr_init(&attr) | 
|  | 179 | +        self._lock = .allocate(capacity: 1) | 
|  | 180 | +        let err = pthread_mutex_init(self._lock, &attr) | 
|  | 181 | +        precondition(err == 0, "pthread_mutex_init error: \(err)") | 
|  | 182 | +        self.value = initialValue | 
|  | 183 | +    } | 
|  | 184 | + | 
|  | 185 | +    func lock() { | 
|  | 186 | +        let err = pthread_mutex_lock(_lock) | 
|  | 187 | +        precondition(err == 0, "pthread_mutex_lock error: \(err)") | 
|  | 188 | +    } | 
|  | 189 | + | 
|  | 190 | +    func unlock() { | 
|  | 191 | +        let err = pthread_mutex_unlock(_lock) | 
|  | 192 | +        precondition(err == 0, "pthread_mutex_unlock error: \(err)") | 
|  | 193 | +    } | 
|  | 194 | + | 
|  | 195 | +    func tryLock() -> Bool { | 
|  | 196 | +        pthread_mutex_trylock(_lock) == 0 | 
|  | 197 | +    } | 
|  | 198 | + | 
|  | 199 | +    deinit { | 
|  | 200 | +        let err = pthread_mutex_destroy(self._lock) | 
|  | 201 | +        precondition(err == 0, "pthread_mutex_destroy error: \(err)") | 
|  | 202 | +        self._lock.deallocate() | 
|  | 203 | +    } | 
|  | 204 | +} | 
|  | 205 | + | 
|  | 206 | + | 
|  | 207 | +#elseif canImport(WinSDK) | 
|  | 208 | + | 
|  | 209 | +import ucrt | 
|  | 210 | +import WinSDK | 
|  | 211 | + | 
|  | 212 | +final class Storage<Value> { | 
|  | 213 | +    private let _lock: UnsafeMutablePointer<SRWLOCK> | 
|  | 214 | + | 
|  | 215 | +    var value: Value | 
|  | 216 | + | 
|  | 217 | +    init(_ initialValue: Value) { | 
|  | 218 | +        self._lock = .allocate(capacity: 1) | 
|  | 219 | +        InitializeSRWLock(self._lock) | 
|  | 220 | +        self.value = initialValue | 
|  | 221 | +    } | 
|  | 222 | + | 
|  | 223 | +    func lock() { | 
|  | 224 | +        AcquireSRWLockExclusive(_lock) | 
|  | 225 | +    } | 
|  | 226 | + | 
|  | 227 | +    func unlock() { | 
|  | 228 | +        ReleaseSRWLockExclusive(_lock) | 
|  | 229 | +    } | 
|  | 230 | + | 
|  | 231 | +    func tryLock() -> Bool { | 
|  | 232 | +        TryAcquireSRWLockExclusive(_lock) != 0 | 
|  | 233 | +    } | 
|  | 234 | +} | 
|  | 235 | + | 
| 110 | 236 | #endif | 
0 commit comments