Skip to content

Commit 34a1589

Browse files
committed
Mutex should be ~Copyable
1 parent c7c4773 commit 34a1589

File tree

1 file changed

+60
-53
lines changed

1 file changed

+60
-53
lines changed

Sources/Mutex.swift

Lines changed: 60 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -29,82 +29,89 @@
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")
78-
}
60+
guard lock.lockIfAvailable() else { return nil }
61+
defer { lock.unlock() }
62+
return try body(&storage.value)
7963
}
80-
}
81-
private struct Transferring<T> {
82-
nonisolated(unsafe) var value: T
8364

84-
init(_ value: T) {
85-
self.value = value
65+
private final class Storage {
66+
var value: Value
67+
68+
init(_ initialValue: consuming Value) {
69+
self.value = initialValue
70+
}
8671
}
8772
}
73+
#elseif compiler(>=6)
74+
75+
// Use Swift 6 Mutex directly
76+
77+
import Synchronization
78+
typealias Mutext = Synchronization.Mutex
79+
8880
#else
89-
public extension Mutex {
90-
init(_ initialValue: consuming Value) {
91-
self.lock = AllocatedLock(uncheckedState: initialValue)
81+
82+
// Use Swift 5 compatible version
83+
84+
public struct Mutex<Value>: @unchecked Sendable {
85+
let lock = AllocatedLock()
86+
private let storage: Storage
87+
88+
public init(_ initialValue: consuming Value) {
89+
self.storage = Storage(initialValue)
9290
}
9391

94-
borrowing func withLock<Result>(
92+
public borrowing func withLock<Result>(
9593
_ body: (inout Value) throws -> Result
96-
) rethrows -> Result {
97-
try lock.withLockUnchecked {
98-
return try body(&$0)
99-
}
94+
) throws -> Result {
95+
lock.lock()
96+
defer { lock.unlock() }
97+
return try body(&storage.value)
10098
}
10199

102-
borrowing func withLockIfAvailable<Result>(
100+
public borrowing func withLockIfAvailable<Result>(
103101
_ body: (inout Value) throws -> Result
104-
) rethrows -> Result? {
105-
try lock.withLockIfAvailableUnchecked {
106-
return try body(&$0)
102+
) throws -> Result? {
103+
guard lock.lockIfAvailable() else { return nil }
104+
defer { lock.unlock() }
105+
return try body(&storage.value)
106+
}
107+
108+
private final class Storage {
109+
var value: Value
110+
111+
init(_ initialValue: consuming Value) {
112+
self.value = initialValue
107113
}
108114
}
109115
}
116+
110117
#endif

0 commit comments

Comments
 (0)