Skip to content

Commit 21a4413

Browse files
committed
Mutex should be ~Copyable
1 parent c7c4773 commit 21a4413

File tree

2 files changed

+91
-53
lines changed

2 files changed

+91
-53
lines changed

.github/workflows/build.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ jobs:
7171

7272
linux_swift_6_1:
7373
runs-on: ubuntu-latest
74-
container: swift:6.1
74+
container: swift:6.1.2
7575
steps:
7676
- name: Checkout
7777
uses: actions/checkout@v4
@@ -123,7 +123,7 @@ jobs:
123123

124124
linux_swift_6_1_musl:
125125
runs-on: ubuntu-latest
126-
container: swift:6.1
126+
container: swift:6.1.2
127127
steps:
128128
- name: Checkout
129129
uses: actions/checkout@v4
@@ -146,7 +146,7 @@ jobs:
146146
- name: Install Swift
147147
uses: SwiftyLab/setup-swift@latest
148148
with:
149-
swift-version: "6.1.0"
149+
swift-version: "6.1.2"
150150
- name: Version
151151
run: swift --version
152152
- name: Build

Sources/Mutex.swift

Lines changed: 88 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -29,82 +29,120 @@
2929
// SOFTWARE.
3030
//
3131

32+
#if compiler(>=6) && !canImport(WinSDK)
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: Error>(
6658
_ 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(&copy))
73-
}?.value
74-
} catch let error as E {
75-
throw error
76-
} catch {
77-
preconditionFailure("cannot occur")
59+
) throws(E) -> sending Result? {
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
8373

84-
init(_ value: T) {
85-
self.value = value
74+
#elseif compiler(>=6) && canImport(WinSDK)
75+
76+
// Windows doesn't support <Value: ~Copyable> yet
77+
78+
public struct Mutex<Value>: @unchecked Sendable, ~Copyable {
79+
package let lock = AllocatedLock()
80+
private let storage: Storage
81+
82+
public init(_ initialValue: consuming sending Value) {
83+
self.storage = Storage(initialValue)
84+
}
85+
86+
public borrowing func withLock<Result, E: Error>(
87+
_ body: (inout sending Value) throws(E) -> sending Result
88+
) throws(E) -> sending Result {
89+
lock.lock()
90+
defer { lock.unlock() }
91+
return try body(&storage.value)
92+
}
93+
94+
public borrowing func withLockIfAvailable<Result, E: Error>(
95+
_ body: (inout sending Value) throws(E) -> sending Result
96+
) throws(E) -> sending Result? {
97+
guard lock.lockIfAvailable() else { return nil }
98+
defer { lock.unlock() }
99+
return try body(&storage.value)
100+
}
101+
102+
private final class Storage {
103+
var value: Value
104+
105+
init(_ initialValue: consuming Value) {
106+
self.value = initialValue
107+
}
86108
}
87109
}
110+
88111
#else
89-
public extension Mutex {
90-
init(_ initialValue: consuming Value) {
91-
self.lock = AllocatedLock(uncheckedState: initialValue)
112+
113+
// Use Swift 5 compatible version
114+
115+
public struct Mutex<Value>: @unchecked Sendable {
116+
let lock = AllocatedLock()
117+
private let storage: Storage
118+
119+
public init(_ initialValue: consuming Value) {
120+
self.storage = Storage(initialValue)
92121
}
93122

94-
borrowing func withLock<Result>(
123+
public borrowing func withLock<Result>(
95124
_ body: (inout Value) throws -> Result
96125
) rethrows -> Result {
97-
try lock.withLockUnchecked {
98-
return try body(&$0)
99-
}
126+
lock.lock()
127+
defer { lock.unlock() }
128+
return try body(&storage.value)
100129
}
101130

102-
borrowing func withLockIfAvailable<Result>(
131+
public borrowing func withLockIfAvailable<Result>(
103132
_ body: (inout Value) throws -> Result
104133
) rethrows -> Result? {
105-
try lock.withLockIfAvailableUnchecked {
106-
return try body(&$0)
134+
guard lock.lockIfAvailable() else { return nil }
135+
defer { lock.unlock() }
136+
return try body(&storage.value)
137+
}
138+
139+
private final class Storage {
140+
var value: Value
141+
142+
init(_ initialValue: consuming Value) {
143+
self.value = initialValue
107144
}
108145
}
109146
}
147+
110148
#endif

0 commit comments

Comments
 (0)