Skip to content

Commit e2eeebd

Browse files
committed
Add Windows support by fixing Locks.swift to support windows
**Motivation:** Swift is cross platform, including Windows, we should support distributed tracing on any platform **Modifications:** Add windows CI and make necessary changes **Result:** Windows support <!-- After your change, what will change. -->
1 parent d9c3031 commit e2eeebd

File tree

3 files changed

+207
-50
lines changed

3 files changed

+207
-50
lines changed

.github/workflows/main.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ jobs:
1818
linux_6_2_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
1919
linux_nightly_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
2020
linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
21+
windows_6_0_enabled: true
22+
windows_6_1_enabled: true
23+
windows_nightly_next_enabled: true
24+
windows_nightly_main_enabled: true
25+
windows_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
26+
windows_6_1_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
27+
windows_nightly_next_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
28+
windows_nightly_main_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
2129

2230
benchmarks:
2331
name: Benchmarks

.github/workflows/pull_request.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ jobs:
4040
linux_6_2_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
4141
linux_nightly_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
4242
linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
43+
windows_6_0_enabled: true
44+
windows_6_1_enabled: true
45+
windows_nightly_next_enabled: true
46+
windows_nightly_main_enabled: true
47+
windows_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
48+
windows_6_1_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
49+
windows_nightly_next_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
50+
windows_nightly_main_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
4351

4452
benchmarks:
4553
name: Benchmarks

Sources/Instrumentation/Locks.swift

Lines changed: 191 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,18 @@
2626
//
2727
//===----------------------------------------------------------------------===//
2828

29-
#if canImport(Darwin)
29+
#if canImport(WASILibc)
30+
// No locking on WASILibc
31+
#elseif canImport(Darwin)
3032
import Darwin
33+
#elseif os(Windows)
34+
import WinSDK
3135
#elseif canImport(Glibc)
3236
import Glibc
3337
#elseif canImport(Android)
3438
import Android
3539
#elseif canImport(Musl)
3640
import Musl
37-
#elseif canImport(WASILibc)
38-
import WASILibc
39-
#if canImport(wasi_pthread)
40-
import wasi_pthread
41-
#endif
4241
#else
4342
#error("Unsupported runtime")
4443
#endif
@@ -47,62 +46,211 @@ import wasi_pthread
4746
///
4847
/// This object provides a lock on top of a single `pthread_mutex_t`. This kind
4948
/// of lock is safe to use with `libpthread`-based threading models, such as the
50-
/// one used by NIO.
51-
@_spi(Locking) // Use the `package` access modifier once min Swift version is increased.
52-
public final class ReadWriteLock {
53-
private let rwlock: UnsafeMutablePointer<pthread_rwlock_t> = UnsafeMutablePointer.allocate(capacity: 1)
49+
/// one used by NIO. On Windows, the lock is based on the substantially similar
50+
/// `SRWLOCK` type.
51+
internal final class Lock: @unchecked Sendable {
52+
#if canImport(WASILibc)
53+
// WASILibc is single threaded, provides no locks
54+
#elseif os(Windows)
55+
fileprivate let mutex: UnsafeMutablePointer<SRWLOCK> =
56+
UnsafeMutablePointer.allocate(capacity: 1)
57+
#else
58+
fileprivate let mutex: UnsafeMutablePointer<pthread_mutex_t> =
59+
UnsafeMutablePointer.allocate(capacity: 1)
60+
#endif
61+
62+
/// Create a new lock.
63+
public init() {
64+
#if canImport(WASILibc)
65+
// WASILibc is single threaded, provides no locks
66+
#elseif os(Windows)
67+
InitializeSRWLock(self.mutex)
68+
#else
69+
var attr = pthread_mutexattr_t()
70+
pthread_mutexattr_init(&attr)
71+
pthread_mutexattr_settype(&attr, .init(PTHREAD_MUTEX_ERRORCHECK))
72+
73+
let err = pthread_mutex_init(self.mutex, &attr)
74+
precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
75+
#endif
76+
}
77+
78+
deinit {
79+
#if canImport(WASILibc)
80+
// WASILibc is single threaded, provides no locks
81+
#elseif os(Windows)
82+
// SRWLOCK does not need to be free'd
83+
self.mutex.deallocate()
84+
#else
85+
let err = pthread_mutex_destroy(self.mutex)
86+
precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
87+
self.mutex.deallocate()
88+
#endif
89+
}
90+
91+
/// Acquire the lock.
92+
///
93+
/// Whenever possible, consider using `withLock` instead of this method and
94+
/// `unlock`, to simplify lock handling.
95+
public func lock() {
96+
#if canImport(WASILibc)
97+
// WASILibc is single threaded, provides no locks
98+
#elseif os(Windows)
99+
AcquireSRWLockExclusive(self.mutex)
100+
#else
101+
let err = pthread_mutex_lock(self.mutex)
102+
precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
103+
#endif
104+
}
105+
106+
/// Release the lock.
107+
///
108+
/// Whenever possible, consider using `withLock` instead of this method and
109+
/// `lock`, to simplify lock handling.
110+
public func unlock() {
111+
#if canImport(WASILibc)
112+
// WASILibc is single threaded, provides no locks
113+
#elseif os(Windows)
114+
ReleaseSRWLockExclusive(self.mutex)
115+
#else
116+
let err = pthread_mutex_unlock(self.mutex)
117+
precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
118+
#endif
119+
}
120+
}
121+
122+
extension Lock {
123+
/// Acquire the lock for the duration of the given block.
124+
///
125+
/// This convenience method should be preferred to `lock` and `unlock` in
126+
/// most situations, as it ensures that the lock will be released regardless
127+
/// of how `body` exits.
128+
///
129+
/// - Parameter body: The block to execute while holding the lock.
130+
/// - Returns: The value returned by the block.
131+
@inlinable
132+
internal func withLock<T>(_ body: () throws -> T) rethrows -> T {
133+
self.lock()
134+
defer {
135+
self.unlock()
136+
}
137+
return try body()
138+
}
139+
140+
// specialise Void return (for performance)
141+
@inlinable
142+
internal func withLockVoid(_ body: () throws -> Void) rethrows {
143+
try self.withLock(body)
144+
}
145+
}
146+
147+
/// A reader/writer threading lock based on `libpthread` instead of `libdispatch`.
148+
///
149+
/// This object provides a lock on top of a single `pthread_rwlock_t`. This kind
150+
/// of lock is safe to use with `libpthread`-based threading models, such as the
151+
/// one used by NIO. On Windows, the lock is based on the substantially similar
152+
/// `SRWLOCK` type.
153+
internal final class ReadWriteLock: @unchecked Sendable {
154+
#if canImport(WASILibc)
155+
// WASILibc is single threaded, provides no locks
156+
#elseif os(Windows)
157+
fileprivate let rwlock: UnsafeMutablePointer<SRWLOCK> =
158+
UnsafeMutablePointer.allocate(capacity: 1)
159+
fileprivate var shared: Bool = true
160+
#else
161+
fileprivate let rwlock: UnsafeMutablePointer<pthread_rwlock_t> =
162+
UnsafeMutablePointer.allocate(capacity: 1)
163+
#endif
54164

55165
/// Create a new lock.
56166
public init() {
167+
#if canImport(WASILibc)
168+
// WASILibc is single threaded, provides no locks
169+
#elseif os(Windows)
170+
InitializeSRWLock(self.rwlock)
171+
#else
57172
let err = pthread_rwlock_init(self.rwlock, nil)
58-
precondition(err == 0, "pthread_rwlock_init failed with error \(err)")
173+
precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)")
174+
#endif
59175
}
60176

61177
deinit {
178+
#if canImport(WASILibc)
179+
// WASILibc is single threaded, provides no locks
180+
#elseif os(Windows)
181+
// SRWLOCK does not need to be free'd
182+
self.rwlock.deallocate()
183+
#else
62184
let err = pthread_rwlock_destroy(self.rwlock)
63-
precondition(err == 0, "pthread_rwlock_destroy failed with error \(err)")
185+
precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)")
64186
self.rwlock.deallocate()
187+
#endif
65188
}
66189

67190
/// Acquire a reader lock.
68191
///
69-
/// Whenever possible, consider using `withLock` instead of this method and
70-
/// `unlock`, to simplify lock handling.
71-
public func lockRead() {
192+
/// Whenever possible, consider using `withReaderLock` instead of this
193+
/// method and `unlock`, to simplify lock handling.
194+
fileprivate func lockRead() {
195+
#if canImport(WASILibc)
196+
// WASILibc is single threaded, provides no locks
197+
#elseif os(Windows)
198+
AcquireSRWLockShared(self.rwlock)
199+
self.shared = true
200+
#else
72201
let err = pthread_rwlock_rdlock(self.rwlock)
73-
precondition(err == 0, "pthread_rwlock_rdlock failed with error \(err)")
202+
precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)")
203+
#endif
74204
}
75205

76206
/// Acquire a writer lock.
77207
///
78-
/// Whenever possible, consider using `withLock` instead of this method and
79-
/// `unlock`, to simplify lock handling.
80-
public func lockWrite() {
208+
/// Whenever possible, consider using `withWriterLock` instead of this
209+
/// method and `unlock`, to simplify lock handling.
210+
fileprivate func lockWrite() {
211+
#if canImport(WASILibc)
212+
// WASILibc is single threaded, provides no locks
213+
#elseif os(Windows)
214+
AcquireSRWLockExclusive(self.rwlock)
215+
self.shared = false
216+
#else
81217
let err = pthread_rwlock_wrlock(self.rwlock)
82-
precondition(err == 0, "pthread_rwlock_wrlock failed with error \(err)")
218+
precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)")
219+
#endif
83220
}
84221

85222
/// Release the lock.
86223
///
87-
/// Whenever possible, consider using `withLock` instead of this method and
88-
/// `lock`, to simplify lock handling.
89-
public func unlock() {
224+
/// Whenever possible, consider using `withReaderLock` and `withWriterLock`
225+
/// instead of this method and `lockRead` and `lockWrite`, to simplify lock
226+
/// handling.
227+
fileprivate func unlock() {
228+
#if canImport(WASILibc)
229+
// WASILibc is single threaded, provides no locks
230+
#elseif os(Windows)
231+
if self.shared {
232+
ReleaseSRWLockShared(self.rwlock)
233+
} else {
234+
ReleaseSRWLockExclusive(self.rwlock)
235+
}
236+
#else
90237
let err = pthread_rwlock_unlock(self.rwlock)
91-
precondition(err == 0, "pthread_rwlock_unlock failed with error \(err)")
238+
precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)")
239+
#endif
92240
}
93241
}
94242

95243
extension ReadWriteLock {
96244
/// Acquire the reader lock for the duration of the given block.
97245
///
98-
/// This convenience method should be preferred to `lock` and `unlock` in
99-
/// most situations, as it ensures that the lock will be released regardless
100-
/// of how `body` exits.
246+
/// This convenience method should be preferred to `lockRead` and `unlock`
247+
/// in most situations, as it ensures that the lock will be released
248+
/// regardless of how `body` exits.
101249
///
102-
/// - Parameter body: The block to execute while holding the lock.
250+
/// - Parameter body: The block to execute while holding the reader lock.
103251
/// - Returns: The value returned by the block.
104252
@inlinable
105-
public func withReaderLock<T>(_ body: () throws -> T) rethrows -> T {
253+
internal func withReaderLock<T>(_ body: () throws -> T) rethrows -> T {
106254
self.lockRead()
107255
defer {
108256
self.unlock()
@@ -112,37 +260,30 @@ extension ReadWriteLock {
112260

113261
/// Acquire the writer lock for the duration of the given block.
114262
///
115-
/// This convenience method should be preferred to `lock` and `unlock` in
116-
/// most situations, as it ensures that the lock will be released regardless
117-
/// of how `body` exits.
263+
/// This convenience method should be preferred to `lockWrite` and `unlock`
264+
/// in most situations, as it ensures that the lock will be released
265+
/// regardless of how `body` exits.
118266
///
119-
/// - Parameter body: The block to execute while holding the lock.
267+
/// - Parameter body: The block to execute while holding the writer lock.
120268
/// - Returns: The value returned by the block.
121269
@inlinable
122-
public func withWriterLock<T>(_ body: () throws -> T) rethrows -> T {
270+
internal func withWriterLock<T>(_ body: () throws -> T) rethrows -> T {
123271
self.lockWrite()
124272
defer {
125273
self.unlock()
126274
}
127275
return try body()
128276
}
129-
}
130277

131-
/// A wrapper providing locked access to a value.
132-
///
133-
/// Marked as @unchecked Sendable due to the synchronization being
134-
/// performed manually using locks.
135-
@_spi(Locking) // Use the `package` access modifier once min Swift version is increased.
136-
public final class LockedValueBox<Value: Sendable>: @unchecked Sendable {
137-
private let lock = ReadWriteLock()
138-
private var value: Value
139-
public init(_ value: Value) {
140-
self.value = value
278+
// specialise Void return (for performance)
279+
@inlinable
280+
internal func withReaderLockVoid(_ body: () throws -> Void) rethrows {
281+
try self.withReaderLock(body)
141282
}
142283

143-
public func withValue<R>(_ work: (inout Value) throws -> R) rethrows -> R {
144-
try self.lock.withWriterLock {
145-
try work(&self.value)
146-
}
284+
// specialise Void return (for performance)
285+
@inlinable
286+
internal func withWriterLockVoid(_ body: () throws -> Void) rethrows {
287+
try self.withWriterLock(body)
147288
}
148-
}
289+
}

0 commit comments

Comments
 (0)