Skip to content

Commit 8aa6ae9

Browse files
committed
Mutex should be ~Copyable
1 parent c7c4773 commit 8aa6ae9

File tree

1 file changed

+76
-49
lines changed

1 file changed

+76
-49
lines changed

Sources/Mutex.swift

Lines changed: 76 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -29,82 +29,109 @@
2929
// SOFTWARE.
3030
//
3131

32+
#if canImport(Darwin) && compiler(>=6)
33+
3234
// Backports the Swift 6 type Mutex<Value> to all Darwin platforms
3335

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+
package let lock = AllocatedLock()
43+
private let storage: Storage
4244

43-
#if compiler(>=6)
44-
public extension Mutex {
45-
init(_ initialValue: consuming sending Value) {
46-
self.lock = AllocatedLock(uncheckedState: initialValue)
45+
public init(_ initialValue: consuming sending Value) {
46+
self.storage = Storage(initialValue)
4747
}
4848

49-
borrowing func withLock<Result, E: Error>(
49+
public borrowing func withLock<Result, E: Error>(
5050
_ body: (inout sending Value) throws(E) -> sending Result
5151
) 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(&copy))
57-
}.value
58-
} catch let error as E {
59-
throw error
60-
} catch {
61-
preconditionFailure("cannot occur")
62-
}
52+
lock.lock()
53+
defer { lock.unlock() }
54+
return try body(&storage.value)
6355
}
6456

65-
borrowing func withLockIfAvailable<Result, E>(
57+
public borrowing func withLockIfAvailable<Result, E>(
6658
_ body: (inout sending Value) throws(E) -> sending Result
6759
) 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(&copy))
73-
}?.value
74-
} catch let error as E {
75-
throw error
76-
} catch {
77-
preconditionFailure("cannot occur")
60+
guard lock.lockIfAvailable() else { return nil }
61+
defer { lock.unlock() }
62+
return try body(&storage.value)
63+
}
64+
65+
private final class Storage {
66+
var value: Value
67+
68+
init(_ initialValue: consuming Value) {
69+
self.value = initialValue
7870
}
7971
}
8072
}
81-
private struct Transferring<T> {
82-
nonisolated(unsafe) var value: T
73+
#elseif compiler(>=6)
74+
75+
// Use Swift 6 Mutex directly
76+
77+
import Synchronization
78+
typealias Mutext = Synchronization.Mutex
79+
80+
public struct Mutex<Value: ~Copyable>: @unchecked Sendable, ~Copyable {
81+
private let inner: Synchronization.Mutex<Value>
82+
83+
public init(_ initialValue: consuming sending Value) {
84+
self.inner = .init(initialValue)
85+
}
86+
87+
public borrowing func withLock<Result, E: Error>(
88+
_ body: (inout sending Value) throws(E) -> sending Result
89+
) throws(E) -> sending Result {
90+
inner.withLock(body)
91+
}
8392

84-
init(_ value: T) {
85-
self.value = value
93+
public borrowing func withLockIfAvailable<Result, E>(
94+
_ body: (inout sending Value) throws(E) -> sending Result
95+
) throws(E) -> sending Result? where E: Error {
96+
inner.withLockIfAvailable(body)
8697
}
8798
}
99+
88100
#else
89-
public extension Mutex {
90-
init(_ initialValue: consuming Value) {
91-
self.lock = AllocatedLock(uncheckedState: initialValue)
101+
102+
// Use Swift 5 compatible version
103+
104+
public struct Mutex<Value>: @unchecked Sendable {
105+
let lock = AllocatedLock()
106+
private let storage: Storage
107+
108+
public init(_ initialValue: consuming Value) {
109+
self.storage = Storage(initialValue)
92110
}
93111

94-
borrowing func withLock<Result>(
112+
public borrowing func withLock<Result>(
95113
_ body: (inout Value) throws -> Result
96114
) rethrows -> Result {
97-
try lock.withLockUnchecked {
98-
return try body(&$0)
99-
}
115+
lock.lock()
116+
defer { lock.unlock() }
117+
return try body(&storage.value)
100118
}
101119

102-
borrowing func withLockIfAvailable<Result>(
120+
public borrowing func withLockIfAvailable<Result>(
103121
_ body: (inout Value) throws -> Result
104122
) rethrows -> Result? {
105-
try lock.withLockIfAvailableUnchecked {
106-
return try body(&$0)
123+
guard lock.lockIfAvailable() else { return nil }
124+
defer { lock.unlock() }
125+
return try body(&storage.value)
126+
}
127+
128+
private final class Storage {
129+
var value: Value
130+
131+
init(_ initialValue: consuming Value) {
132+
self.value = initialValue
107133
}
108134
}
109135
}
136+
110137
#endif

0 commit comments

Comments
 (0)