Skip to content

Commit bc16ab4

Browse files
committed
swift-mutex update
1 parent 4131696 commit bc16ab4

File tree

4 files changed

+384
-37
lines changed

4 files changed

+384
-37
lines changed

Sources/Mutex.swift

Lines changed: 129 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,46 @@
3131

3232
#if compiler(>=6)
3333

34-
#if canImport(Darwin)
35-
// Backports the Synchronization.Mutex API for earlier Darwin platforms
34+
#if !canImport(WinSDK)
35+
36+
// Backports the Swift 6 type Mutex<Value> to all Darwin platforms
37+
struct Mutex<Value: ~Copyable>: ~Copyable {
38+
let storage: Storage<Value>
39+
40+
init(_ initialValue: consuming sending Value) {
41+
self.storage = Storage(initialValue)
42+
}
43+
44+
borrowing func withLock<Result, E: Error>(
45+
_ body: (inout sending Value) throws(E) -> sending Result
46+
) throws(E) -> sending Result {
47+
storage.lock()
48+
defer { storage.unlock() }
49+
return try body(&storage.value)
50+
}
51+
52+
borrowing func withLockIfAvailable<Result, E: Error>(
53+
_ body: (inout sending Value) throws(E) -> sending Result
54+
) throws(E) -> sending Result? {
55+
guard storage.tryLock() else { return nil }
56+
defer { storage.unlock() }
57+
return try body(&storage.value)
58+
}
59+
}
60+
61+
extension Mutex: @unchecked Sendable where Value: ~Copyable { }
62+
63+
#else
64+
65+
// Windows doesn't support ~Copyable yet
3666

37-
@usableFromInline
3867
struct Mutex<Value>: @unchecked Sendable {
39-
let storage: Storage
68+
let storage: Storage<Value>
4069

41-
@usableFromInline
4270
init(_ initialValue: consuming sending Value) {
4371
self.storage = Storage(initialValue)
4472
}
4573

46-
@usableFromInline
4774
borrowing func withLock<Result, E: Error>(
4875
_ body: (inout sending Value) throws(E) -> sending Result
4976
) throws(E) -> sending Result {
@@ -52,59 +79,127 @@ struct Mutex<Value>: @unchecked Sendable {
5279
return try body(&storage.value)
5380
}
5481

55-
@usableFromInline
56-
borrowing func withLockIfAvailable<Result, E>(
82+
borrowing func withLockIfAvailable<Result, E: Error>(
5783
_ body: (inout sending Value) throws(E) -> sending Result
58-
) throws(E) -> sending Result? where E: Error {
84+
) throws(E) -> sending Result? {
5985
guard storage.tryLock() else { return nil }
6086
defer { storage.unlock() }
6187
return try body(&storage.value)
6288
}
6389
}
6490

91+
#endif
92+
93+
#if canImport(Darwin)
94+
6595
import struct os.os_unfair_lock_t
6696
import struct os.os_unfair_lock
6797
import func os.os_unfair_lock_lock
6898
import func os.os_unfair_lock_unlock
6999
import func os.os_unfair_lock_trylock
70100

71-
extension Mutex {
101+
final class Storage<Value: ~Copyable> {
102+
private let _lock: os_unfair_lock_t
103+
var value: Value
72104

73-
final class Storage {
74-
private let _lock: os_unfair_lock_t
105+
init(_ initialValue: consuming Value) {
106+
self._lock = .allocate(capacity: 1)
107+
self._lock.initialize(to: os_unfair_lock())
108+
self.value = initialValue
109+
}
75110

76-
var value: Value
111+
func lock() {
112+
os_unfair_lock_lock(_lock)
113+
}
114+
115+
func unlock() {
116+
os_unfair_lock_unlock(_lock)
117+
}
118+
119+
func tryLock() -> Bool {
120+
os_unfair_lock_trylock(_lock)
121+
}
122+
123+
deinit {
124+
self._lock.deinitialize(count: 1)
125+
self._lock.deallocate()
126+
}
127+
}
128+
129+
#elseif canImport(Glibc) || canImport(Musl) || canImport(Bionic)
130+
131+
#if canImport(Musl)
132+
import Musl
133+
#elseif canImport(Bionic)
134+
import Android
135+
#else
136+
import Glibc
137+
#endif
77138

78-
init(_ initialValue: Value) {
79-
self._lock = .allocate(capacity: 1)
80-
self._lock.initialize(to: os_unfair_lock())
81-
self.value = initialValue
82-
}
139+
final class Storage<Value: ~Copyable> {
140+
private let _lock: UnsafeMutablePointer<pthread_mutex_t>
141+
142+
var value: Value
143+
144+
init(_ initialValue: consuming Value) {
145+
var attr = pthread_mutexattr_t()
146+
pthread_mutexattr_init(&attr)
147+
self._lock = .allocate(capacity: 1)
148+
let err = pthread_mutex_init(self._lock, &attr)
149+
precondition(err == 0, "pthread_mutex_init error: \(err)")
150+
self.value = initialValue
151+
}
83152

84-
func lock() {
85-
os_unfair_lock_lock(_lock)
86-
}
153+
func lock() {
154+
let err = pthread_mutex_lock(_lock)
155+
precondition(err == 0, "pthread_mutex_lock error: \(err)")
156+
}
87157

88-
func unlock() {
89-
os_unfair_lock_unlock(_lock)
90-
}
158+
func unlock() {
159+
let err = pthread_mutex_unlock(_lock)
160+
precondition(err == 0, "pthread_mutex_unlock error: \(err)")
161+
}
91162

92-
func tryLock() -> Bool {
93-
os_unfair_lock_trylock(_lock)
94-
}
163+
func tryLock() -> Bool {
164+
pthread_mutex_trylock(_lock) == 0
165+
}
95166

96-
deinit {
97-
self._lock.deinitialize(count: 1)
98-
self._lock.deallocate()
99-
}
167+
deinit {
168+
let err = pthread_mutex_destroy(self._lock)
169+
precondition(err == 0, "pthread_mutex_destroy error: \(err)")
170+
self._lock.deallocate()
100171
}
101172
}
102173

103-
#elseif canImport(Synchronization)
174+
#elseif canImport(WinSDK)
175+
176+
import ucrt
177+
import WinSDK
178+
179+
final class Storage<Value> {
180+
private let _lock: UnsafeMutablePointer<SRWLOCK>
181+
182+
var value: Value
183+
184+
init(_ initialValue: Value) {
185+
self._lock = .allocate(capacity: 1)
186+
InitializeSRWLock(self._lock)
187+
self.value = initialValue
188+
}
104189

105-
import Synchronization
190+
func lock() {
191+
AcquireSRWLockExclusive(_lock)
192+
}
106193

107-
typealias Mutex = Synchronization.Mutex
194+
func unlock() {
195+
ReleaseSRWLockExclusive(_lock)
196+
}
197+
198+
func tryLock() -> Bool {
199+
TryAcquireSRWLockExclusive(_lock) != 0
200+
}
201+
}
108202

109203
#endif
204+
110205
#endif

Sources/MutexSwift5.swift

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
//
2+
// MutexSwift5.swift
3+
// swift-mutex
4+
//
5+
// Created by Simon Whitty on 03/06/2025.
6+
// Copyright 2025 Simon Whitty
7+
//
8+
// Distributed under the permissive MIT license
9+
// Get the latest version from here:
10+
//
11+
// https://github.com/swhitty/swift-mutex
12+
//
13+
// Permission is hereby granted, free of charge, to any person obtaining a copy
14+
// of this software and associated documentation files (the "Software"), to deal
15+
// in the Software without restriction, including without limitation the rights
16+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17+
// copies of the Software, and to permit persons to whom the Software is
18+
// furnished to do so, subject to the following conditions:
19+
//
20+
// The above copyright notice and this permission notice shall be included in all
21+
// copies or substantial portions of the Software.
22+
//
23+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29+
// SOFTWARE.
30+
//
31+
32+
#if compiler(<6.0)
33+
34+
// Backports the Swift 6 type Mutex<Value> to Swift 5
35+
36+
struct Mutex<Value>: @unchecked Sendable {
37+
let storage: Storage<Value>
38+
39+
init(_ initialValue: Value) {
40+
self.storage = Storage(initialValue)
41+
}
42+
43+
borrowing func withLock<Result>(
44+
_ body: (inout Value) throws -> Result
45+
) rethrows -> Result {
46+
storage.lock()
47+
defer { storage.unlock() }
48+
return try body(&storage.value)
49+
}
50+
51+
borrowing func withLockIfAvailable<Result>(
52+
_ body: (inout Value) throws -> Result
53+
) rethrows -> Result? {
54+
guard storage.tryLock() else { return nil }
55+
defer { storage.unlock() }
56+
return try body(&storage.value)
57+
}
58+
}
59+
60+
#if canImport(Darwin)
61+
62+
import struct os.os_unfair_lock_t
63+
import struct os.os_unfair_lock
64+
import func os.os_unfair_lock_lock
65+
import func os.os_unfair_lock_unlock
66+
import func os.os_unfair_lock_trylock
67+
68+
final class Storage<Value> {
69+
private let _lock: os_unfair_lock_t
70+
var value: Value
71+
72+
init(_ initialValue: consuming Value) {
73+
self._lock = .allocate(capacity: 1)
74+
self._lock.initialize(to: os_unfair_lock())
75+
self.value = initialValue
76+
}
77+
78+
func lock() {
79+
os_unfair_lock_lock(_lock)
80+
}
81+
82+
func unlock() {
83+
os_unfair_lock_unlock(_lock)
84+
}
85+
86+
func tryLock() -> Bool {
87+
os_unfair_lock_trylock(_lock)
88+
}
89+
90+
deinit {
91+
self._lock.deinitialize(count: 1)
92+
self._lock.deallocate()
93+
}
94+
}
95+
#elseif canImport(Glibc) || canImport(Musl) || canImport(Bionic)
96+
97+
#if canImport(Musl)
98+
import Musl
99+
#elseif canImport(Bionic)
100+
import Android
101+
#else
102+
import Glibc
103+
#endif
104+
105+
final class Storage<Value> {
106+
private let _lock: UnsafeMutablePointer<pthread_mutex_t>
107+
var value: Value
108+
109+
init(_ initialValue: consuming Value) {
110+
var attr = pthread_mutexattr_t()
111+
pthread_mutexattr_init(&attr)
112+
self._lock = .allocate(capacity: 1)
113+
let err = pthread_mutex_init(self._lock, &attr)
114+
precondition(err == 0, "pthread_mutex_init error: \(err)")
115+
self.value = initialValue
116+
}
117+
118+
func lock() {
119+
let err = pthread_mutex_lock(_lock)
120+
precondition(err == 0, "pthread_mutex_lock error: \(err)")
121+
}
122+
123+
func unlock() {
124+
let err = pthread_mutex_unlock(_lock)
125+
precondition(err == 0, "pthread_mutex_unlock error: \(err)")
126+
}
127+
128+
func tryLock() -> Bool {
129+
pthread_mutex_trylock(_lock) == 0
130+
}
131+
132+
deinit {
133+
let err = pthread_mutex_destroy(self._lock)
134+
precondition(err == 0, "pthread_mutex_destroy error: \(err)")
135+
self._lock.deallocate()
136+
}
137+
}
138+
#elseif canImport(WinSDK)
139+
140+
import ucrt
141+
import WinSDK
142+
143+
final class Storage<Value> {
144+
private let _lock: UnsafeMutablePointer<SRWLOCK>
145+
146+
var value: Value
147+
148+
init(_ initialValue: Value) {
149+
self._lock = .allocate(capacity: 1)
150+
InitializeSRWLock(self._lock)
151+
self.value = initialValue
152+
}
153+
154+
func lock() {
155+
AcquireSRWLockExclusive(_lock)
156+
}
157+
158+
func unlock() {
159+
ReleaseSRWLockExclusive(_lock)
160+
}
161+
162+
func tryLock() -> Bool {
163+
TryAcquireSRWLockExclusive(_lock) != 0
164+
}
165+
}
166+
167+
#endif
168+
169+
#endif

0 commit comments

Comments
 (0)