Skip to content

Commit e13e34a

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 e13e34a

File tree

4 files changed

+121
-32
lines changed

4 files changed

+121
-32
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: 89 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -26,80 +26,126 @@
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
4544

46-
/// A threading lock based on `libpthread` instead of `libdispatch`.
45+
/// A reader/writer threading lock based on `libpthread` instead of `libdispatch`.
4746
///
48-
/// This object provides a lock on top of a single `pthread_mutex_t`. This kind
47+
/// This object provides a lock on top of a single `pthread_rwlock_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+
public final class ReadWriteLock: @unchecked Sendable {
52+
#if canImport(WASILibc)
53+
// WASILibc is single threaded, provides no locks
54+
#elseif os(Windows)
55+
fileprivate let rwlock: UnsafeMutablePointer<SRWLOCK> =
56+
UnsafeMutablePointer.allocate(capacity: 1)
57+
fileprivate var shared: Bool = true
58+
#else
59+
fileprivate let rwlock: UnsafeMutablePointer<pthread_rwlock_t> =
60+
UnsafeMutablePointer.allocate(capacity: 1)
61+
#endif
5462

5563
/// Create a new lock.
5664
public init() {
65+
#if canImport(WASILibc)
66+
// WASILibc is single threaded, provides no locks
67+
#elseif os(Windows)
68+
InitializeSRWLock(self.rwlock)
69+
#else
5770
let err = pthread_rwlock_init(self.rwlock, nil)
58-
precondition(err == 0, "pthread_rwlock_init failed with error \(err)")
71+
precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)")
72+
#endif
5973
}
6074

6175
deinit {
76+
#if canImport(WASILibc)
77+
// WASILibc is single threaded, provides no locks
78+
#elseif os(Windows)
79+
// SRWLOCK does not need to be free'd
80+
self.rwlock.deallocate()
81+
#else
6282
let err = pthread_rwlock_destroy(self.rwlock)
63-
precondition(err == 0, "pthread_rwlock_destroy failed with error \(err)")
83+
precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)")
6484
self.rwlock.deallocate()
85+
#endif
6586
}
6687

6788
/// Acquire a reader lock.
6889
///
69-
/// Whenever possible, consider using `withLock` instead of this method and
70-
/// `unlock`, to simplify lock handling.
90+
/// Whenever possible, consider using `withReaderLock` instead of this
91+
/// method and `unlock`, to simplify lock handling.
7192
public func lockRead() {
93+
#if canImport(WASILibc)
94+
// WASILibc is single threaded, provides no locks
95+
#elseif os(Windows)
96+
AcquireSRWLockShared(self.rwlock)
97+
self.shared = true
98+
#else
7299
let err = pthread_rwlock_rdlock(self.rwlock)
73-
precondition(err == 0, "pthread_rwlock_rdlock failed with error \(err)")
100+
precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)")
101+
#endif
74102
}
75103

76104
/// Acquire a writer lock.
77105
///
78-
/// Whenever possible, consider using `withLock` instead of this method and
79-
/// `unlock`, to simplify lock handling.
106+
/// Whenever possible, consider using `withWriterLock` instead of this
107+
/// method and `unlock`, to simplify lock handling.
80108
public func lockWrite() {
109+
#if canImport(WASILibc)
110+
// WASILibc is single threaded, provides no locks
111+
#elseif os(Windows)
112+
AcquireSRWLockExclusive(self.rwlock)
113+
self.shared = false
114+
#else
81115
let err = pthread_rwlock_wrlock(self.rwlock)
82-
precondition(err == 0, "pthread_rwlock_wrlock failed with error \(err)")
116+
precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)")
117+
#endif
83118
}
84119

85120
/// Release the lock.
86121
///
87-
/// Whenever possible, consider using `withLock` instead of this method and
88-
/// `lock`, to simplify lock handling.
122+
/// Whenever possible, consider using `withReaderLock` and `withWriterLock`
123+
/// instead of this method and `lockRead` and `lockWrite`, to simplify lock
124+
/// handling.
89125
public func unlock() {
126+
#if canImport(WASILibc)
127+
// WASILibc is single threaded, provides no locks
128+
#elseif os(Windows)
129+
if self.shared {
130+
ReleaseSRWLockShared(self.rwlock)
131+
} else {
132+
ReleaseSRWLockExclusive(self.rwlock)
133+
}
134+
#else
90135
let err = pthread_rwlock_unlock(self.rwlock)
91-
precondition(err == 0, "pthread_rwlock_unlock failed with error \(err)")
136+
precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)")
137+
#endif
92138
}
93139
}
94140

95141
extension ReadWriteLock {
96142
/// Acquire the reader lock for the duration of the given block.
97143
///
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.
144+
/// This convenience method should be preferred to `lockRead` and `unlock`
145+
/// in most situations, as it ensures that the lock will be released
146+
/// regardless of how `body` exits.
101147
///
102-
/// - Parameter body: The block to execute while holding the lock.
148+
/// - Parameter body: The block to execute while holding the reader lock.
103149
/// - Returns: The value returned by the block.
104150
@inlinable
105151
public func withReaderLock<T>(_ body: () throws -> T) rethrows -> T {
@@ -112,11 +158,11 @@ extension ReadWriteLock {
112158

113159
/// Acquire the writer lock for the duration of the given block.
114160
///
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.
161+
/// This convenience method should be preferred to `lockWrite` and `unlock`
162+
/// in most situations, as it ensures that the lock will be released
163+
/// regardless of how `body` exits.
118164
///
119-
/// - Parameter body: The block to execute while holding the lock.
165+
/// - Parameter body: The block to execute while holding the writer lock.
120166
/// - Returns: The value returned by the block.
121167
@inlinable
122168
public func withWriterLock<T>(_ body: () throws -> T) rethrows -> T {
@@ -126,6 +172,18 @@ extension ReadWriteLock {
126172
}
127173
return try body()
128174
}
175+
176+
// specialise Void return (for performance)
177+
@inlinable
178+
internal func withReaderLockVoid(_ body: () throws -> Void) rethrows {
179+
try self.withReaderLock(body)
180+
}
181+
182+
// specialise Void return (for performance)
183+
@inlinable
184+
internal func withWriterLockVoid(_ body: () throws -> Void) rethrows {
185+
try self.withWriterLock(body)
186+
}
129187
}
130188

131189
/// A wrapper providing locked access to a value.

Sources/Tracing/TracingTime.swift

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ import WASILibc
2929
#error("Unsupported runtime")
3030
#endif
3131

32-
#if os(WASI)
32+
#if os(Windows)
33+
import WinSDK
34+
#elseif os(WASI)
3335
import _CWASI
3436
#endif
3537

@@ -110,6 +112,18 @@ public struct DefaultTracerClock {
110112

111113
/// Returns the current instant in time.
112114
public var now: Self.Instant {
115+
#if os(Windows)
116+
var fileTime = FILETIME()
117+
GetSystemTimePreciseAsFileTime(&fileTime)
118+
119+
let fileTime64 = (UInt64(fileTime.dwHighDateTime) << 32) | UInt64(fileTime.dwLowDateTime)
120+
121+
let windowsToUnixEpochIn100ns: UInt64 = 116_444_736_000_000_000
122+
let unixTime100ns = fileTime64 &- windowsToUnixEpochIn100ns
123+
let nowNanos = unixTime100ns &* 100
124+
125+
return Instant(nanosecondsSinceEpoch: nowNanos)
126+
#else // not Windows
113127
var ts = timespec()
114128
#if os(WASI)
115129
CWASI_clock_gettime_realtime(&ts)
@@ -122,5 +136,6 @@ public struct DefaultTracerClock {
122136
let nowNanos = UInt64(ts.tv_sec) &* 1_000_000_000 &+ UInt64(ts.tv_nsec)
123137

124138
return Instant(nanosecondsSinceEpoch: nowNanos)
139+
#endif // Windows
125140
}
126141
}

0 commit comments

Comments
 (0)