282 lines
9.1 KiB
Swift
282 lines
9.1 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// 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<SRWLOCK> =
|
|
UnsafeMutablePointer.allocate(capacity: 1)
|
|
#else
|
|
fileprivate let mutex: UnsafeMutablePointer<pthread_mutex_t> =
|
|
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<T>(_ 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<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, "\(#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<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
|
|
func withWriterLock<T>(_ 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 {}
|