//===----------------------------------------------------------------------===// // // This source file is part of the Swift Metrics API open source project // // Copyright (c) 2018-2019 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-2018 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(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 #else #error("Unsupported runtime") #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. internal final class Lock { #if os(Windows) fileprivate let mutex: UnsafeMutablePointer = UnsafeMutablePointer.allocate(capacity: 1) #else fileprivate let mutex: UnsafeMutablePointer = UnsafeMutablePointer.allocate(capacity: 1) #endif /// Create a new lock. public init() { #if os(Windows) InitializeSRWLock(self.mutex) #else var attr = pthread_mutexattr_t() pthread_mutexattr_init(&attr) pthread_mutexattr_settype(&attr, .init(PTHREAD_MUTEX_ERRORCHECK)) let err = pthread_mutex_init(self.mutex, &attr) precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)") #endif } deinit { #if os(Windows) // SRWLOCK does not need to be free'd #else let err = pthread_mutex_destroy(self.mutex) precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)") #endif self.mutex.deallocate() } /// Acquire the lock. /// /// Whenever possible, consider using `withLock` instead of this method and /// `unlock`, to simplify lock handling. public func lock() { #if os(Windows) AcquireSRWLockExclusive(self.mutex) #else 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. public func unlock() { #if os(Windows) ReleaseSRWLockExclusive(self.mutex) #else let err = pthread_mutex_unlock(self.mutex) precondition(err == 0, "\(#function) failed in pthread_mutex with error \(err)") #endif } } extension Lock { /// 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 func withLock(_ body: () throws -> T) rethrows -> T { self.lock() defer { self.unlock() } return try body() } // specialise Void return (for performance) @inlinable func withLockVoid(_ body: () throws -> Void) rethrows { try self.withLock(body) } } 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 { #if canImport(WASILibc) // WASILibc is single threaded, provides no locks #elseif os(Windows) fileprivate let rwlock: UnsafeMutablePointer = UnsafeMutablePointer.allocate(capacity: 1) fileprivate var shared: Bool = true #else fileprivate let rwlock: UnsafeMutablePointer = 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, "\(#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 #else let err = pthread_rwlock_destroy(self.rwlock) precondition(err == 0, "\(#function) failed in pthread_rwlock with error \(err)") #endif self.rwlock.deallocate() } /// Acquire a reader lock. /// /// 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, "\(#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. 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, "\(#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. 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, "\(#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 func withReaderLock(_ 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 func withWriterLock(_ body: () throws -> T) rethrows -> T { self.lockWrite() defer { self.unlock() } return try body() } // specialise Void return (for performance) @inlinable func withReaderLockVoid(_ body: () throws -> Void) rethrows { try self.withReaderLock(body) } // specialise Void return (for performance) @inlinable func withWriterLockVoid(_ body: () throws -> Void) rethrows { try self.withWriterLock(body) } } extension ReadWriteLock: @unchecked Sendable {}