| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- //===----------------------------------------------------------------------===//
- //
- // This source file is part of the Swift Metrics API open source project
- //
- // Copyright (c) 2018-2026 Apple Inc. and the Swift Metrics API project authors
- // Licensed under Apache License v2.0
- //
- // See LICENSE.txt for license information
- // See CONTRIBUTORS.txt for the list of Swift Metrics API project authors
- //
- // SPDX-License-Identifier: Apache-2.0
- //
- //===----------------------------------------------------------------------===//
- //===----------------------------------------------------------------------===//
- //
- // This source file is part of the SwiftNIO open source project
- //
- // Copyright (c) 2017-2026 Apple Inc. and the SwiftNIO project authors
- // Licensed under Apache License v2.0
- //
- // See LICENSE.txt for license information
- // See CONTRIBUTORS.txt for the list of SwiftNIO project authors
- //
- // SPDX-License-Identifier: Apache-2.0
- //
- //===----------------------------------------------------------------------===//
- #if canImport(Darwin)
- import Darwin
- #elseif os(Windows)
- import ucrt
- import WinSDK
- #elseif canImport(Glibc)
- @preconcurrency import Glibc
- #elseif canImport(Android)
- @preconcurrency import Android
- #elseif canImport(Musl)
- @preconcurrency import Musl
- #elseif canImport(Bionic)
- @preconcurrency import Bionic
- #elseif canImport(WASILibc)
- @preconcurrency import WASILibc
- #if canImport(wasi_pthread)
- import wasi_pthread
- #endif
- #else
- #error("The concurrency lock module was unable to identify your C library.")
- #endif
- /// A threading lock based on `libpthread` instead of `libdispatch`.
- ///
- /// This object provides a lock on top of a single `pthread_mutex_t`. This kind
- /// of lock is safe to use with `libpthread`-based threading models, such as the
- /// one used by NIO. On Windows, the lock is based on the substantially similar
- /// `SRWLOCK` type.
- package final class Lock {
- #if os(Windows)
- fileprivate let mutex: UnsafeMutablePointer<SRWLOCK> =
- UnsafeMutablePointer.allocate(capacity: 1)
- #elseif os(FreeBSD) || os(OpenBSD)
- fileprivate let mutex: UnsafeMutablePointer<pthread_mutex_t?> =
- UnsafeMutablePointer.allocate(capacity: 1)
- #elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
- fileprivate let mutex: UnsafeMutablePointer<pthread_mutex_t> =
- UnsafeMutablePointer.allocate(capacity: 1)
- #endif
- /// Create a new lock.
- package init() {
- #if os(Windows)
- InitializeSRWLock(self.mutex)
- #elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
- #if os(FreeBSD) || os(OpenBSD)
- var attr = pthread_mutexattr_t(bitPattern: 0)
- #else
- var attr = pthread_mutexattr_t()
- #endif
- var err = pthread_mutexattr_init(&attr)
- precondition(err == 0, "\(#function) failed in pthread_mutexattr_init with error \(err)")
- debugOnly {
- #if os(FreeBSD) || os(OpenBSD)
- pthread_mutexattr_settype(&attr, .init(PTHREAD_MUTEX_ERRORCHECK.rawValue))
- #else
- pthread_mutexattr_settype(&attr, .init(PTHREAD_MUTEX_ERRORCHECK))
- #endif
- }
- err = pthread_mutex_init(self.mutex, &attr)
- precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
- // `pthread_mutexattr_t` only lives during init; destroy here instead of deinit.
- let attrDestroyErr = pthread_mutexattr_destroy(&attr)
- precondition(
- attrDestroyErr == 0,
- "\(#function) failed in pthread_mutexattr_destroy with error \(attrDestroyErr)"
- )
- #endif
- }
- deinit {
- #if os(Windows)
- mutex.deallocate()
- #elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
- let err = pthread_mutex_destroy(self.mutex)
- precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
- mutex.deallocate()
- #endif
- }
- /// Acquire the lock.
- ///
- /// Whenever possible, consider using `withLock` instead of this method and
- /// `unlock`, to simplify lock handling.
- package func lock() {
- #if os(Windows)
- AcquireSRWLockExclusive(self.mutex)
- #elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
- let err = pthread_mutex_lock(self.mutex)
- precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
- #endif
- }
- /// Release the lock.
- ///
- /// Whenever possible, consider using `withLock` instead of this method and
- /// `lock`, to simplify lock handling.
- package func unlock() {
- #if os(Windows)
- ReleaseSRWLockExclusive(self.mutex)
- #elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
- let err = pthread_mutex_unlock(self.mutex)
- precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)")
- #endif
- }
- /// Acquire the 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.
- ///
- /// - Parameter body: The block to execute while holding the lock.
- /// - Returns: The value returned by the block.
- @inlinable
- package func withLock<T>(_ body: () throws -> T) rethrows -> T {
- self.lock()
- defer {
- self.unlock()
- }
- return try body()
- }
- // specialise Void return (for performance)
- @inlinable
- package func withLockVoid(_ body: () throws -> Void) rethrows {
- try self.withLock(body)
- }
- }
- /// A utility function that runs the body code only in debug builds, without
- /// emitting compiler warnings.
- ///
- /// This is currently the only way to do this in Swift: see
- /// https://forums.swift.org/t/support-debug-only-code/11037 for a discussion.
- @inlinable
- internal func debugOnly(_ body: () -> Void) {
- assert(
- {
- body()
- return true
- }()
- )
- }
- extension Lock: @unchecked Sendable {}
- /// A reader/writer threading lock based on `libpthread` instead of `libdispatch`.
- ///
- /// 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. On Windows, the lock is based on the substantially similar
- /// `SRWLOCK` type.
- internal 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
- #elseif os(FreeBSD) || os(OpenBSD)
- fileprivate let rwlock: UnsafeMutablePointer<pthread_rwlock_t?> =
- UnsafeMutablePointer.allocate(capacity: 1)
- #else
- fileprivate let rwlock: UnsafeMutablePointer<pthread_rwlock_t> =
- UnsafeMutablePointer.allocate(capacity: 1)
- #endif
- /// Create a new lock.
- public init() {
- #if os(Windows)
- InitializeSRWLock(self.rwlock)
- #elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
- let err = pthread_rwlock_init(self.rwlock, nil)
- precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)")
- #endif
- }
- deinit {
- #if os(Windows)
- self.rwlock.deallocate()
- #elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
- let err = pthread_rwlock_destroy(self.rwlock)
- precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)")
- self.rwlock.deallocate()
- #endif
- }
- /// Acquire a reader lock.
- ///
- /// Whenever possible, consider using `withReaderLock` instead of this
- /// method and `unlock`, to simplify lock handling.
- fileprivate func lockRead() {
- #if os(Windows)
- AcquireSRWLockShared(self.rwlock)
- self.shared = true
- #elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
- let err = pthread_rwlock_rdlock(self.rwlock)
- precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)")
- #endif
- }
- /// Acquire a writer lock.
- ///
- /// Whenever possible, consider using `withWriterLock` instead of this
- /// method and `unlock`, to simplify lock handling.
- fileprivate func lockWrite() {
- #if os(Windows)
- AcquireSRWLockExclusive(self.rwlock)
- self.shared = false
- #elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
- let err = pthread_rwlock_wrlock(self.rwlock)
- precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)")
- #endif
- }
- /// Release the lock.
- ///
- /// Whenever possible, consider using `withReaderLock` and `withWriterLock`
- /// instead of this method and `lockRead` and `lockWrite`, to simplify lock
- /// handling.
- fileprivate func unlock() {
- #if os(Windows)
- if self.shared {
- ReleaseSRWLockShared(self.rwlock)
- } else {
- ReleaseSRWLockExclusive(self.rwlock)
- }
- #elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
- let err = pthread_rwlock_unlock(self.rwlock)
- 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 `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 reader lock.
- /// - Returns: The value returned by the block.
- @inlinable
- internal func withReaderLock<T>(_ body: () throws -> T) rethrows -> T {
- self.lockRead()
- defer {
- self.unlock()
- }
- return try body()
- }
- /// Acquire the writer lock for the duration of the given block.
- ///
- /// 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 writer lock.
- /// - Returns: The value returned by the block.
- @inlinable
- internal func withWriterLock<T>(_ body: () throws -> T) rethrows -> T {
- self.lockWrite()
- defer {
- self.unlock()
- }
- 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)
- }
- }
|