Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ jobs:
linux_6_2_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
linux_nightly_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
windows_6_0_enabled: true
windows_6_1_enabled: true
windows_nightly_next_enabled: true
windows_nightly_main_enabled: true
windows_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
windows_6_1_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
windows_nightly_next_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
windows_nightly_main_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"

benchmarks:
name: Benchmarks
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ jobs:
linux_6_2_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
linux_nightly_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
windows_6_0_enabled: true
windows_6_1_enabled: true
windows_nightly_next_enabled: true
windows_nightly_main_enabled: true
windows_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
windows_6_1_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
windows_nightly_next_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
windows_nightly_main_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"

benchmarks:
name: Benchmarks
Expand Down
120 changes: 89 additions & 31 deletions Sources/Instrumentation/Locks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,80 +26,126 @@
//
//===----------------------------------------------------------------------===//

#if canImport(Darwin)
#if canImport(WASILibc)
// No locking on WASILibc
#elseif canImport(Darwin)
import Darwin
#elseif os(Windows)
import WinSDK
#elseif canImport(Glibc)
import Glibc
#elseif canImport(Android)
import Android
#elseif canImport(Musl)
import Musl
#elseif canImport(WASILibc)
import WASILibc
#if canImport(wasi_pthread)
import wasi_pthread
#endif
#else
#error("Unsupported runtime")
#endif

/// A threading lock based on `libpthread` instead of `libdispatch`.
/// A reader/writer threading lock based on `libpthread` instead of `libdispatch`.
///
/// This object provides a lock on top of a single `pthread_mutex_t`. This kind
/// This object provides a lock on top of a single `pthread_rwlock_t`. This kind
/// of lock is safe to use with `libpthread`-based threading models, such as the
/// one used by NIO.
@_spi(Locking) // Use the `package` access modifier once min Swift version is increased.
public final class ReadWriteLock {
private let rwlock: UnsafeMutablePointer<pthread_rwlock_t> = UnsafeMutablePointer.allocate(capacity: 1)
/// one used by NIO. On Windows, the lock is based on the substantially similar
/// `SRWLOCK` type.
public final class ReadWriteLock: @unchecked Sendable {
#if canImport(WASILibc)
// WASILibc is single threaded, provides no locks
#elseif os(Windows)
fileprivate let rwlock: UnsafeMutablePointer<SRWLOCK> =
UnsafeMutablePointer.allocate(capacity: 1)
fileprivate var shared: Bool = true
#else
fileprivate let rwlock: UnsafeMutablePointer<pthread_rwlock_t> =
UnsafeMutablePointer.allocate(capacity: 1)
#endif

/// Create a new lock.
public init() {
#if canImport(WASILibc)
// WASILibc is single threaded, provides no locks
#elseif os(Windows)
InitializeSRWLock(self.rwlock)
#else
let err = pthread_rwlock_init(self.rwlock, nil)
precondition(err == 0, "pthread_rwlock_init failed with error \(err)")
precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)")
#endif
}

deinit {
#if canImport(WASILibc)
// WASILibc is single threaded, provides no locks
#elseif os(Windows)
// SRWLOCK does not need to be free'd
self.rwlock.deallocate()
#else
let err = pthread_rwlock_destroy(self.rwlock)
precondition(err == 0, "pthread_rwlock_destroy failed with error \(err)")
precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)")
self.rwlock.deallocate()
#endif
}

/// Acquire a reader lock.
///
/// Whenever possible, consider using `withLock` instead of this method and
/// `unlock`, to simplify lock handling.
/// Whenever possible, consider using `withReaderLock` instead of this
/// method and `unlock`, to simplify lock handling.
public func lockRead() {
#if canImport(WASILibc)
// WASILibc is single threaded, provides no locks
#elseif os(Windows)
AcquireSRWLockShared(self.rwlock)
self.shared = true
#else
let err = pthread_rwlock_rdlock(self.rwlock)
precondition(err == 0, "pthread_rwlock_rdlock failed with error \(err)")
precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)")
#endif
}

/// Acquire a writer lock.
///
/// Whenever possible, consider using `withLock` instead of this method and
/// `unlock`, to simplify lock handling.
/// Whenever possible, consider using `withWriterLock` instead of this
/// method and `unlock`, to simplify lock handling.
public func lockWrite() {
#if canImport(WASILibc)
// WASILibc is single threaded, provides no locks
#elseif os(Windows)
AcquireSRWLockExclusive(self.rwlock)
self.shared = false
#else
let err = pthread_rwlock_wrlock(self.rwlock)
precondition(err == 0, "pthread_rwlock_wrlock failed with error \(err)")
precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)")
#endif
}

/// Release the lock.
///
/// Whenever possible, consider using `withLock` instead of this method and
/// `lock`, to simplify lock handling.
/// Whenever possible, consider using `withReaderLock` and `withWriterLock`
/// instead of this method and `lockRead` and `lockWrite`, to simplify lock
/// handling.
public func unlock() {
#if canImport(WASILibc)
// WASILibc is single threaded, provides no locks
#elseif os(Windows)
if self.shared {
ReleaseSRWLockShared(self.rwlock)
} else {
ReleaseSRWLockExclusive(self.rwlock)
}
#else
let err = pthread_rwlock_unlock(self.rwlock)
precondition(err == 0, "pthread_rwlock_unlock failed with error \(err)")
precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)")
#endif
}
}

extension ReadWriteLock {
/// Acquire the reader lock for the duration of the given block.
///
/// This convenience method should be preferred to `lock` and `unlock` in
/// most situations, as it ensures that the lock will be released regardless
/// of how `body` exits.
/// This convenience method should be preferred to `lockRead` and `unlock`
/// in most situations, as it ensures that the lock will be released
/// regardless of how `body` exits.
///
/// - Parameter body: The block to execute while holding the lock.
/// - Parameter body: The block to execute while holding the reader lock.
/// - Returns: The value returned by the block.
@inlinable
public func withReaderLock<T>(_ body: () throws -> T) rethrows -> T {
Expand All @@ -112,11 +158,11 @@ extension ReadWriteLock {

/// Acquire the writer lock for the duration of the given block.
///
/// This convenience method should be preferred to `lock` and `unlock` in
/// most situations, as it ensures that the lock will be released regardless
/// of how `body` exits.
/// This convenience method should be preferred to `lockWrite` and `unlock`
/// in most situations, as it ensures that the lock will be released
/// regardless of how `body` exits.
///
/// - Parameter body: The block to execute while holding the lock.
/// - Parameter body: The block to execute while holding the writer lock.
/// - Returns: The value returned by the block.
@inlinable
public func withWriterLock<T>(_ body: () throws -> T) rethrows -> T {
Expand All @@ -126,6 +172,18 @@ extension ReadWriteLock {
}
return try body()
}

// specialise Void return (for performance)
@inlinable
internal func withReaderLockVoid(_ body: () throws -> Void) rethrows {
try self.withReaderLock(body)
}

// specialise Void return (for performance)
@inlinable
internal func withWriterLockVoid(_ body: () throws -> Void) rethrows {
try self.withWriterLock(body)
}
}

/// A wrapper providing locked access to a value.
Expand Down
15 changes: 15 additions & 0 deletions Sources/Tracing/TracingTime.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import Android
import Musl
#elseif canImport(WASILibc)
import WASILibc
#elseif os(Windows)
import WinSDK
#else
#error("Unsupported runtime")
#endif
Expand Down Expand Up @@ -110,6 +112,18 @@ public struct DefaultTracerClock {

/// Returns the current instant in time.
public var now: Self.Instant {
#if os(Windows)
var fileTime = FILETIME()
GetSystemTimePreciseAsFileTime(&fileTime)

let fileTime64 = (UInt64(fileTime.dwHighDateTime) << 32) | UInt64(fileTime.dwLowDateTime)

let windowsToUnixEpochIn100ns: UInt64 = 116_444_736_000_000_000
let unixTime100ns = fileTime64 &- windowsToUnixEpochIn100ns
let nowNanos = unixTime100ns &* 100

return Instant(nanosecondsSinceEpoch: nowNanos)
#else // not Windows
var ts = timespec()
#if os(WASI)
CWASI_clock_gettime_realtime(&ts)
Expand All @@ -122,5 +136,6 @@ public struct DefaultTracerClock {
let nowNanos = UInt64(ts.tv_sec) &* 1_000_000_000 &+ UInt64(ts.tv_nsec)

return Instant(nanosecondsSinceEpoch: nowNanos)
#endif // Windows
}
}