1
1
//
2
2
// This source file is part of the Swift.org open source project
3
3
//
4
- // Copyright (c) 2023 Apple Inc. and the Swift project authors
4
+ // Copyright (c) 2023–2025 Apple Inc. and the Swift project authors
5
5
// Licensed under Apache License v2.0 with Runtime Library Exception
6
6
//
7
7
// See https://swift.org/LICENSE.txt for license information
10
10
11
11
internal import _TestingInternals
12
12
13
+ /// A protocol defining a type, generally platform-specific, that satisfies the
14
+ /// requirements of a lock or mutex.
15
+ protocol Lockable {
16
+ /// Initialize the lock at the given address.
17
+ ///
18
+ /// - Parameters:
19
+ /// - lock: A pointer to uninitialized memory that should be initialized as
20
+ /// an instance of this type.
21
+ static func initializeLock( at lock: UnsafeMutablePointer < Self > )
22
+
23
+ /// Deinitialize the lock at the given address.
24
+ ///
25
+ /// - Parameters:
26
+ /// - lock: A pointer to initialized memory that should be deinitialized.
27
+ static func deinitializeLock( at lock: UnsafeMutablePointer < Self > )
28
+
29
+ /// Acquire the lock at the given address.
30
+ ///
31
+ /// - Parameters:
32
+ /// - lock: The address of the lock to acquire.
33
+ static func unsafelyAcquireLock( at lock: UnsafeMutablePointer < Self > )
34
+
35
+ /// Relinquish the lock at the given address.
36
+ ///
37
+ /// - Parameters:
38
+ /// - lock: The address of the lock to relinquish.
39
+ static func unsafelyRelinquishLock( at lock: UnsafeMutablePointer < Self > )
40
+ }
41
+
42
+ // MARK: -
43
+
13
44
/// A type that wraps a value requiring access from a synchronous caller during
14
45
/// concurrent execution.
15
46
///
@@ -21,67 +52,23 @@ internal import _TestingInternals
21
52
/// concurrency tools.
22
53
///
23
54
/// This type is not part of the public interface of the testing library.
24
- ///
25
- /// - Bug: The state protected by this type should instead be protected using
26
- /// actor isolation, but actor-isolated functions cannot be called from
27
- /// synchronous functions. ([83888717](rdar://83888717))
28
- struct Locked < T> : RawRepresentable , Sendable where T: Sendable {
29
- /// The platform-specific type to use for locking.
30
- ///
31
- /// It would be preferable to implement this lock in Swift, however there is
32
- /// no standard lock or mutex type available across all platforms that is
33
- /// visible in Swift. C11 has a standard `mtx_t` type, but it is not widely
34
- /// supported and so cannot be relied upon.
35
- ///
36
- /// To keep the implementation of this type as simple as possible,
37
- /// `pthread_mutex_t` is used on Apple platforms instead of `os_unfair_lock`
38
- /// or `OSAllocatedUnfairLock`.
39
- #if SWT_TARGET_OS_APPLE || os(Linux) || os(Android) || (os(WASI) && compiler(>=6.1) && _runtime(_multithreaded))
40
- typealias PlatformLock = pthread_mutex_t
41
- #elseif os(FreeBSD) || os(OpenBSD)
42
- typealias PlatformLock = pthread_mutex_t ?
43
- #elseif os(Windows)
44
- typealias PlatformLock = SRWLOCK
45
- #elseif os(WASI)
46
- // No locks on WASI without multithreaded runtime.
47
- typealias PlatformLock = Never
48
- #else
49
- #warning("Platform-specific implementation missing: locking unavailable")
50
- typealias PlatformLock = Never
51
- #endif
52
-
55
+ struct LockedWith < L, T> : RawRepresentable where L: Lockable {
53
56
/// A type providing heap-allocated storage for an instance of ``Locked``.
54
- private final class _Storage : ManagedBuffer < T , PlatformLock > {
57
+ private final class _Storage : ManagedBuffer < T , L > {
55
58
deinit {
56
59
withUnsafeMutablePointerToElements { lock in
57
- #if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) || (os(WASI) && compiler(>=6.1) && _runtime(_multithreaded))
58
- _ = pthread_mutex_destroy ( lock)
59
- #elseif os(Windows)
60
- // No deinitialization needed.
61
- #elseif os(WASI)
62
- // No locks on WASI without multithreaded runtime.
63
- #else
64
- #warning("Platform-specific implementation missing: locking unavailable")
65
- #endif
60
+ L . deinitializeLock ( at: lock)
66
61
}
67
62
}
68
63
}
69
64
70
65
/// Storage for the underlying lock and wrapped value.
71
- private nonisolated ( unsafe) var _storage: ManagedBuffer < T , PlatformLock >
66
+ private nonisolated ( unsafe) var _storage: ManagedBuffer < T , L >
72
67
73
68
init ( rawValue: T ) {
74
69
_storage = _Storage. create ( minimumCapacity: 1 , makingHeaderWith: { _ in rawValue } )
75
70
_storage. withUnsafeMutablePointerToElements { lock in
76
- #if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) || (os(WASI) && compiler(>=6.1) && _runtime(_multithreaded))
77
- _ = pthread_mutex_init ( lock, nil )
78
- #elseif os(Windows)
79
- InitializeSRWLock ( lock)
80
- #elseif os(WASI)
81
- // No locks on WASI without multithreaded runtime.
82
- #else
83
- #warning("Platform-specific implementation missing: locking unavailable")
84
- #endif
71
+ L . initializeLock ( at: lock)
85
72
}
86
73
}
87
74
@@ -103,28 +90,16 @@ struct Locked<T>: RawRepresentable, Sendable where T: Sendable {
103
90
/// concurrency tools.
104
91
nonmutating func withLock< R> ( _ body: ( inout T ) throws -> R ) rethrows -> R {
105
92
try _storage. withUnsafeMutablePointers { rawValue, lock in
106
- #if SWT_TARGET_OS_APPLE || os(Linux) || os(FreeBSD) || os(OpenBSD) || os(Android) || (os(WASI) && compiler(>=6.1) && _runtime(_multithreaded))
107
- _ = pthread_mutex_lock ( lock)
93
+ L . unsafelyAcquireLock ( at: lock)
108
94
defer {
109
- _ = pthread_mutex_unlock ( lock)
95
+ L . unsafelyRelinquishLock ( at : lock)
110
96
}
111
- #elseif os(Windows)
112
- AcquireSRWLockExclusive ( lock)
113
- defer {
114
- ReleaseSRWLockExclusive ( lock)
115
- }
116
- #elseif os(WASI)
117
- // No locks on WASI without multithreaded runtime.
118
- #else
119
- #warning("Platform-specific implementation missing: locking unavailable")
120
- #endif
121
-
122
97
return try body ( & rawValue. pointee)
123
98
}
124
99
}
125
100
126
101
/// Acquire the lock and invoke a function while it is held, yielding both the
127
- /// protected value and a reference to the lock itself .
102
+ /// protected value and a reference to the underlying lock guarding it .
128
103
///
129
104
/// - Parameters:
130
105
/// - body: A closure to invoke while the lock is held.
@@ -134,16 +109,16 @@ struct Locked<T>: RawRepresentable, Sendable where T: Sendable {
134
109
/// - Throws: Whatever is thrown by `body`.
135
110
///
136
111
/// This function is equivalent to ``withLock(_:)`` except that the closure
137
- /// passed to it also takes a reference to the underlying platform lock. This
138
- /// function can be used when platform-specific functionality such as a
139
- /// `pthread_cond_t` is needed. Because the caller has direct access to the
140
- /// lock and is able to unlock and re-lock it, it is unsafe to modify the
141
- /// protected value.
112
+ /// passed to it also takes a reference to the underlying lock guarding this
113
+ /// instance's wrapped value. This function can be used when platform-specific
114
+ /// functionality such as a `pthread_cond_t` is needed. Because the caller has
115
+ /// direct access to the lock and is able to unlock and re-lock it, it is
116
+ /// unsafe to modify the protected value.
142
117
///
143
118
/// - Warning: Callers that unlock the lock _must_ lock it again before the
144
119
/// closure returns. If the lock is not acquired when `body` returns, the
145
120
/// effect is undefined.
146
- nonmutating func withUnsafePlatformLock < R> ( _ body: ( UnsafeMutablePointer < PlatformLock > , T ) throws -> R ) rethrows -> R {
121
+ nonmutating func withUnsafeUnderlyingLock < R> ( _ body: ( UnsafeMutablePointer < L > , T ) throws -> R ) rethrows -> R {
147
122
try withLock { value in
148
123
try _storage. withUnsafeMutablePointerToElements { lock in
149
124
try body ( lock, value)
@@ -152,7 +127,16 @@ struct Locked<T>: RawRepresentable, Sendable where T: Sendable {
152
127
}
153
128
}
154
129
155
- extension Locked where T: AdditiveArithmetic {
130
+ extension LockedWith : Sendable where T: Sendable { }
131
+
132
+ /// A type that wraps a value requiring access from a synchronous caller during
133
+ /// concurrent execution and which uses the default platform-specific lock type
134
+ /// for the current platform.
135
+ typealias Locked < T> = LockedWith < DefaultLock , T >
136
+
137
+ // MARK: - Additions
138
+
139
+ extension LockedWith where T: AdditiveArithmetic {
156
140
/// Add something to the current wrapped value of this instance.
157
141
///
158
142
/// - Parameters:
@@ -168,7 +152,7 @@ extension Locked where T: AdditiveArithmetic {
168
152
}
169
153
}
170
154
171
- extension Locked where T: Numeric {
155
+ extension LockedWith where T: Numeric {
172
156
/// Increment the current wrapped value of this instance.
173
157
///
174
158
/// - Returns: The sum of ``rawValue`` and `1`.
@@ -188,7 +172,7 @@ extension Locked where T: Numeric {
188
172
}
189
173
}
190
174
191
- extension Locked {
175
+ extension LockedWith {
192
176
/// Initialize an instance of this type with a raw value of `nil`.
193
177
init < V> ( ) where T == V ? {
194
178
self . init ( rawValue: nil )
@@ -198,4 +182,9 @@ extension Locked {
198
182
init < K, V> ( ) where T == Dictionary < K , V > {
199
183
self . init ( rawValue: [ : ] )
200
184
}
185
+
186
+ /// Initialize an instance of this type with a raw value of `[]`.
187
+ init < V> ( ) where T == [ V ] {
188
+ self . init ( rawValue: [ ] )
189
+ }
201
190
}
0 commit comments