Skip to content

Commit e7ef2a2

Browse files
committed
swift-mutex
1 parent 10aab78 commit e7ef2a2

File tree

7 files changed

+550
-184
lines changed

7 files changed

+550
-184
lines changed

Sources/IdentifiableContinuation.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public func withIdentifiableContinuation<T>(
5252
onCancel handler: @Sendable (IdentifiableContinuation<T, Never>.ID) -> Void
5353
) async -> T {
5454
let id = IdentifiableContinuation<T, Never>.ID()
55-
let state = AllocatedLock(initialState: (isStarted: false, isCancelled: false))
55+
let state = Mutex((isStarted: false, isCancelled: false))
5656
nonisolated(unsafe) let body = body
5757
return await withTaskCancellationHandler {
5858
await withCheckedContinuation(isolation: isolation, function: function) {
@@ -99,7 +99,7 @@ public func withIdentifiableThrowingContinuation<T>(
9999
onCancel handler: @Sendable (IdentifiableContinuation<T, any Error>.ID) -> Void
100100
) async throws -> T {
101101
let id = IdentifiableContinuation<T, any Error>.ID()
102-
let state = AllocatedLock(initialState: (isStarted: false, isCancelled: false))
102+
let state = Mutex((isStarted: false, isCancelled: false))
103103
nonisolated(unsafe) let body = body
104104
return try await withTaskCancellationHandler {
105105
try await withCheckedThrowingContinuation(isolation: isolation, function: function) {
@@ -148,7 +148,7 @@ public func withIdentifiableContinuation<T>(
148148
onCancel handler: @Sendable (IdentifiableContinuation<T, Never>.ID) -> Void
149149
) async -> T {
150150
let id = IdentifiableContinuation<T, Never>.ID()
151-
let state = AllocatedLock(initialState: (isStarted: false, isCancelled: false))
151+
let state = Mutex((isStarted: false, isCancelled: false))
152152
return await withTaskCancellationHandler {
153153
await withCheckedContinuation(function: function) {
154154
let continuation = IdentifiableContinuation(id: id, continuation: $0)
@@ -197,7 +197,7 @@ public func withIdentifiableThrowingContinuation<T>(
197197
onCancel handler: @Sendable (IdentifiableContinuation<T, any Error>.ID) -> Void
198198
) async throws -> T {
199199
let id = IdentifiableContinuation<T, any Error>.ID()
200-
let state = AllocatedLock(initialState: (isStarted: false, isCancelled: false))
200+
let state = Mutex((isStarted: false, isCancelled: false))
201201
return try await withTaskCancellationHandler {
202202
try await withCheckedThrowingContinuation(function: function) {
203203
let continuation = IdentifiableContinuation(id: id, continuation: $0)

Sources/AllocatedLock.swift renamed to Sources/Mutex.swift

Lines changed: 71 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
//
2-
// AllocatedLock.swift
3-
// AllocatedLock
2+
// Mutex.swift
3+
// swift-mutex
44
//
55
// Created by Simon Whitty on 10/04/2023.
66
// Copyright 2023 Simon Whitty
77
//
88
// Distributed under the permissive MIT license
99
// Get the latest version from here:
1010
//
11-
// https://github.com/swhitty/AllocatedLock
11+
// https://github.com/swhitty/swift-mutex
1212
//
1313
// Permission is hereby granted, free of charge, to any person obtaining a copy
1414
// of this software and associated documentation files (the "Software"), to deal
@@ -29,82 +29,99 @@
2929
// SOFTWARE.
3030
//
3131

32-
// Backports the Swift interface around os_unfair_lock_t available in recent Darwin platforms
33-
//
32+
// Backports the Swift 6.0 Mutex API
3433
@usableFromInline
35-
struct AllocatedLock<State>: @unchecked Sendable {
36-
37-
@usableFromInline
34+
package struct Mutex<Value>: @unchecked Sendable {
3835
let storage: Storage
36+
}
37+
38+
#if compiler(>=6)
39+
package extension Mutex {
3940

4041
@usableFromInline
41-
init(initialState: State) {
42-
self.storage = Storage(initialState: initialState)
42+
init(_ initialValue: consuming sending Value) {
43+
self.storage = Storage(initialValue)
4344
}
4445

45-
@inlinable
46-
func withLock<R>(_ body: @Sendable (inout State) throws -> R) rethrows -> R where R: Sendable {
46+
@usableFromInline
47+
borrowing func withLock<Result, E: Error>(
48+
_ body: (inout sending Value) throws(E) -> sending Result
49+
) throws(E) -> sending Result {
4750
storage.lock()
4851
defer { storage.unlock() }
49-
return try body(&storage.state)
52+
return try body(&storage.value)
5053
}
51-
}
52-
53-
extension AllocatedLock where State == Void {
5454

55-
init() {
56-
self.storage = Storage(initialState: ())
55+
@usableFromInline
56+
borrowing func withLockIfAvailable<Result, E>(
57+
_ body: (inout sending Value) throws(E) -> sending Result
58+
) throws(E) -> sending Result? where E: Error {
59+
guard storage.tryLock() else { return nil }
60+
defer { storage.unlock() }
61+
return try body(&storage.value)
5762
}
63+
}
64+
#else
65+
package extension Mutex {
5866

59-
@inlinable @available(*, noasync)
60-
func lock() {
61-
storage.lock()
67+
@usableFromInline
68+
init(_ initialValue: Value) {
69+
self.storage = Storage(initialValue)
6270
}
6371

64-
@inlinable @available(*, noasync)
65-
func unlock() {
66-
storage.unlock()
72+
@usableFromInline
73+
borrowing func withLock<Result>(
74+
_ body: (inout Value) throws -> Result
75+
) rethrows -> Result {
76+
storage.lock()
77+
defer { storage.unlock() }
78+
return try body(&storage.value)
6779
}
6880

69-
@inlinable
70-
func withLock<R>(_ body: @Sendable () throws -> R) rethrows -> R where R: Sendable {
71-
storage.lock()
81+
@usableFromInline
82+
borrowing func withLockIfAvailable<Result>(
83+
_ body: (inout Value) throws -> Result
84+
) rethrows -> Result? {
85+
guard storage.tryLock() else { return nil }
7286
defer { storage.unlock() }
73-
return try body()
87+
return try body(&storage.value)
7488
}
7589
}
90+
#endif
7691

7792
#if canImport(Darwin)
7893

7994
import struct os.os_unfair_lock_t
8095
import struct os.os_unfair_lock
8196
import func os.os_unfair_lock_lock
8297
import func os.os_unfair_lock_unlock
98+
import func os.os_unfair_lock_trylock
99+
100+
extension Mutex {
83101

84-
extension AllocatedLock {
85-
@usableFromInline
86102
final class Storage {
87103
private let _lock: os_unfair_lock_t
88104

89-
@usableFromInline
90-
var state: State
105+
var value: Value
91106

92-
init(initialState: State) {
107+
init(_ initialValue: Value) {
93108
self._lock = .allocate(capacity: 1)
94109
self._lock.initialize(to: os_unfair_lock())
95-
self.state = initialState
110+
self.value = initialValue
96111
}
97112

98-
@usableFromInline
99113
func lock() {
100114
os_unfair_lock_lock(_lock)
101115
}
102116

103-
@usableFromInline
104117
func unlock() {
105118
os_unfair_lock_unlock(_lock)
106119
}
107120

121+
func tryLock() -> Bool {
122+
os_unfair_lock_trylock(_lock)
123+
}
124+
108125
deinit {
109126
self._lock.deinitialize(count: 1)
110127
self._lock.deallocate()
@@ -116,35 +133,36 @@ extension AllocatedLock {
116133

117134
import Glibc
118135

119-
extension AllocatedLock {
120-
@usableFromInline
136+
extension Mutex {
137+
121138
final class Storage {
122139
private let _lock: UnsafeMutablePointer<pthread_mutex_t>
123140

124-
@usableFromInline
125-
var state: State
141+
var value: Value
126142

127-
init(initialState: State) {
143+
init(_ initialValue: Value) {
128144
var attr = pthread_mutexattr_t()
129145
pthread_mutexattr_init(&attr)
130146
self._lock = .allocate(capacity: 1)
131147
let err = pthread_mutex_init(self._lock, &attr)
132148
precondition(err == 0, "pthread_mutex_init error: \(err)")
133-
self.state = initialState
149+
self.value = initialValue
134150
}
135151

136-
@usableFromInline
137152
func lock() {
138153
let err = pthread_mutex_lock(_lock)
139154
precondition(err == 0, "pthread_mutex_lock error: \(err)")
140155
}
141156

142-
@usableFromInline
143157
func unlock() {
144158
let err = pthread_mutex_unlock(_lock)
145159
precondition(err == 0, "pthread_mutex_unlock error: \(err)")
146160
}
147161

162+
func tryLock() -> Bool {
163+
pthread_mutex_trylock(_lock) == 0
164+
}
165+
148166
deinit {
149167
let err = pthread_mutex_destroy(self._lock)
150168
precondition(err == 0, "pthread_mutex_destroy error: \(err)")
@@ -158,29 +176,30 @@ extension AllocatedLock {
158176
import ucrt
159177
import WinSDK
160178

161-
extension AllocatedLock {
162-
@usableFromInline
179+
extension Mutex {
180+
163181
final class Storage {
164182
private let _lock: UnsafeMutablePointer<SRWLOCK>
165183

166-
@usableFromInline
167-
var state: State
184+
var value: Value
168185

169-
init(initialState: State) {
186+
init(_ initialValue: Value) {
170187
self._lock = .allocate(capacity: 1)
171188
InitializeSRWLock(self._lock)
172-
self.state = initialState
189+
self.value = initialValue
173190
}
174191

175-
@usableFromInline
176192
func lock() {
177193
AcquireSRWLockExclusive(_lock)
178194
}
179195

180-
@usableFromInline
181196
func unlock() {
182197
ReleaseSRWLockExclusive(_lock)
183198
}
199+
200+
func tryLock() -> Bool {
201+
TryAcquireSRWLockExclusive(_lock)
202+
}
184203
}
185204
}
186205

Tests/AllocatedLockTests.swift

Lines changed: 0 additions & 92 deletions
This file was deleted.

0 commit comments

Comments
 (0)