Skip to content

Commit e5ce709

Browse files
committed
[Observation] Use independent locking since the runtime functions reference a symbol that is not emitted
1 parent 5d36643 commit e5ce709

File tree

3 files changed

+135
-72
lines changed

3 files changed

+135
-72
lines changed

stdlib/public/Observation/Sources/Observation/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
list(APPEND swift_runtime_library_compile_flags -I${SWIFT_SOURCE_DIR}/stdlib/include -I${SWIFT_SOURCE_DIR}/include)
1414

1515
add_swift_target_library(swiftObservation ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB
16-
Locking.cpp
1716
Locking.swift
1817
Observable.swift
1918
ObservationRegistrar.swift

stdlib/public/Observation/Sources/Observation/Locking.cpp

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

stdlib/public/Observation/Sources/Observation/Locking.swift

Lines changed: 135 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -9,52 +9,158 @@
99
//
1010
//===----------------------------------------------------------------------===//
1111

12+
// NOTE: this cant use Synchronization because it is deployed before that was
13+
// introduced and availability wont let it be hidden behind an internal type.
14+
// The Swift internal runtime locking cannot be used since that emits dependent
15+
// symbols that are not provided by this library - so instead it has to re-implement
16+
// all of this on its own...
1217

13-
@_silgen_name("_swift_observation_lock_size")
14-
func _lockSize() -> Int
18+
#if canImport(Darwin)
19+
import Darwin
20+
#elseif canImport(Glibc)
21+
import Glibc
22+
#elseif canImport(Musl)
23+
import Musl
24+
#elseif canImport(WinSDK)
25+
import WinSDK
26+
#elseif canImport(Bionic)
27+
import Bionic
28+
#else
29+
#error("Unsupported platform")
30+
#endif
1531

16-
@_silgen_name("_swift_observation_lock_init")
17-
func _lockInit(_: UnsafeRawPointer)
32+
internal struct Lock {
33+
#if canImport(Darwin)
34+
typealias Primitive = os_unfair_lock
35+
#elseif canImport(Glibc) || canImport(Musl) || canImport(Bionic)
36+
typealias Primitive = pthread_mutex_t
37+
#elseif canImport(WinSDK)
38+
typealias Primitive = SRWLOCK
39+
#else
40+
#error("Unsupported platform")
41+
#endif
1842

19-
@_silgen_name("_swift_observation_lock_lock")
20-
func _lockLock(_: UnsafeRawPointer)
43+
typealias PlatformLock = UnsafeMutablePointer<Primitive>
44+
let platformLock: PlatformLock
2145

22-
@_silgen_name("_swift_observation_lock_unlock")
23-
func _lockUnlock(_: UnsafeRawPointer)
46+
private init(_ platformLock: PlatformLock) {
47+
self.platformLock = platformLock
48+
}
2449

25-
@available(SwiftStdlib 5.9, *)
26-
internal struct _ManagedCriticalState<State> {
27-
final private class LockedBuffer: ManagedBuffer<State, UnsafeRawPointer> { }
50+
fileprivate static func initialize(_ platformLock: PlatformLock) {
51+
#if canImport(Darwin)
52+
platformLock.initialize(to: os_unfair_lock())
53+
#elseif canImport(Glibc) || canImport(Musl) || canImport(Bionic)
54+
let result = pthread_mutex_init(platformLock, nil)
55+
precondition(result == 0, "pthread_mutex_init failed")
56+
#elseif canImport(WinSDK)
57+
InitializeSRWLock(platformLock)
58+
#else
59+
#error("Unsupported platform")
60+
#endif
61+
}
62+
63+
fileprivate static func deinitialize(_ platformLock: PlatformLock) {
64+
#if canImport(Glibc) || canImport(Musl) || canImport(Bionic)
65+
let result = pthread_mutex_destroy(platformLock)
66+
precondition(result == 0, "pthread_mutex_destroy failed")
67+
#endif
68+
platformLock.deinitialize(count: 1)
69+
}
70+
71+
fileprivate static func lock(_ platformLock: PlatformLock) {
72+
#if canImport(Darwin)
73+
os_unfair_lock_lock(platformLock)
74+
#elseif canImport(Glibc) || canImport(Musl) || canImport(Bionic)
75+
pthread_mutex_lock(platformLock)
76+
#elseif canImport(WinSDK)
77+
AcquireSRWLockExclusive(platformLock)
78+
#else
79+
#error("Unsupported platform")
80+
#endif
81+
}
82+
83+
fileprivate static func unlock(_ platformLock: PlatformLock) {
84+
#if canImport(Darwin)
85+
os_unfair_lock_unlock(platformLock)
86+
#elseif canImport(Glibc) || canImport(Musl) || canImport(Bionic)
87+
let result = pthread_mutex_unlock(platformLock)
88+
precondition(result == 0, "pthread_mutex_unlock failed")
89+
#elseif canImport(WinSDK)
90+
ReleaseSRWLockExclusive(platformLock)
91+
#else
92+
#error("Unsupported platform")
93+
#endif
94+
}
95+
96+
static func allocate() -> Lock {
97+
let platformLock = PlatformLock.allocate(capacity: 1)
98+
initialize(platformLock)
99+
return Lock(platformLock)
100+
}
101+
102+
func deinitialize() {
103+
Lock.deinitialize(platformLock)
104+
platformLock.deallocate()
105+
}
106+
107+
func lock() {
108+
Lock.lock(platformLock)
109+
}
110+
111+
func unlock() {
112+
Lock.unlock(platformLock)
113+
}
28114

29-
private let buffer: ManagedBuffer<State, UnsafeRawPointer>
115+
/// Acquire the lock for the duration of the given block.
116+
///
117+
/// This convenience method should be preferred to `lock` and `unlock` in
118+
/// most situations, as it ensures that the lock will be released regardless
119+
/// of how `body` exits.
120+
///
121+
/// - Parameter body: The block to execute while holding the lock.
122+
/// - Returns: The value returned by the block.
123+
func withLock<T>(_ body: () throws -> T) rethrows -> T {
124+
self.lock()
125+
defer {
126+
self.unlock()
127+
}
128+
return try body()
129+
}
130+
131+
// specialise Void return (for performance)
132+
func withLockVoid(_ body: () throws -> Void) rethrows {
133+
try self.withLock(body)
134+
}
135+
}
30136

31-
internal init(_ buffer: ManagedBuffer<State, UnsafeRawPointer>) {
32-
self.buffer = buffer
137+
struct _ManagedCriticalState<State> {
138+
private final class LockedBuffer: ManagedBuffer<State, Lock.Primitive> {
139+
deinit {
140+
withUnsafeMutablePointerToElements { Lock.deinitialize($0) }
141+
}
33142
}
34-
35-
internal init(_ initial: State) {
36-
let roundedSize = (_lockSize() + MemoryLayout<UnsafeRawPointer>.size - 1) / MemoryLayout<UnsafeRawPointer>.size
37-
self.init(LockedBuffer.create(minimumCapacity: Swift.max(roundedSize, 1)) { buffer in
38-
buffer.withUnsafeMutablePointerToElements { _lockInit(UnsafeRawPointer($0)) }
143+
144+
private let buffer: ManagedBuffer<State, Lock.Primitive>
145+
146+
init(_ initial: State) {
147+
buffer = LockedBuffer.create(minimumCapacity: 1) { buffer in
148+
buffer.withUnsafeMutablePointerToElements { Lock.initialize($0) }
39149
return initial
40-
})
150+
}
41151
}
42152

43-
internal func withCriticalRegion<R>(
44-
_ critical: (inout State) throws -> R
45-
) rethrows -> R {
153+
func withCriticalRegion<R>(_ critical: (inout State) throws -> R) rethrows -> R {
46154
try buffer.withUnsafeMutablePointers { header, lock in
47-
_lockLock(UnsafeRawPointer(lock))
48-
defer {
49-
_lockUnlock(UnsafeRawPointer(lock))
50-
}
155+
Lock.lock(lock)
156+
defer { Lock.unlock(lock) }
51157
return try critical(&header.pointee)
52158
}
53159
}
54160
}
55161

56-
@available(SwiftStdlib 5.9, *)
57-
extension _ManagedCriticalState: @unchecked Sendable where State: Sendable { }
162+
extension _ManagedCriticalState: @unchecked Sendable where State: Sendable {}
163+
58164

59165
@available(SwiftStdlib 5.9, *)
60166
extension _ManagedCriticalState: Identifiable {

0 commit comments

Comments
 (0)