From a1a778c9f91e90c458d6b0344f00324c4a13436e Mon Sep 17 00:00:00 2001 From: Luke Howard Date: Tue, 18 Jul 2023 21:14:02 +0200 Subject: [PATCH] Linux build (#37) Import Locking.swift from upstream AsyncAlgorithms to enable non-Darwin builds --- Sources/Supporting/Locking.swift | 154 ++++++++++++++++++ Sources/Supporting/ManagedCriticalState.swift | 45 ----- 2 files changed, 154 insertions(+), 45 deletions(-) create mode 100644 Sources/Supporting/Locking.swift delete mode 100644 Sources/Supporting/ManagedCriticalState.swift diff --git a/Sources/Supporting/Locking.swift b/Sources/Supporting/Locking.swift new file mode 100644 index 0000000..80fb4dd --- /dev/null +++ b/Sources/Supporting/Locking.swift @@ -0,0 +1,154 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Async Algorithms open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// +//===----------------------------------------------------------------------===// + +#if canImport(Darwin) +import Darwin +#elseif canImport(Glibc) +import Glibc +#elseif canImport(WinSDK) +import WinSDK +#endif + +internal struct Lock { +#if canImport(Darwin) + typealias Primitive = os_unfair_lock +#elseif canImport(Glibc) + typealias Primitive = pthread_mutex_t +#elseif canImport(WinSDK) + typealias Primitive = SRWLOCK +#else + typealias Primitive = Int +#endif + + typealias PlatformLock = UnsafeMutablePointer + let platformLock: PlatformLock + + private init(_ platformLock: PlatformLock) { + self.platformLock = platformLock + } + + fileprivate static func initialize(_ platformLock: PlatformLock) { +#if canImport(Darwin) + platformLock.initialize(to: os_unfair_lock()) +#elseif canImport(Glibc) + let result = pthread_mutex_init(platformLock, nil) + precondition(result == 0, "pthread_mutex_init failed") +#elseif canImport(WinSDK) + InitializeSRWLock(platformLock) +#endif + } + + fileprivate static func deinitialize(_ platformLock: PlatformLock) { +#if canImport(Glibc) + let result = pthread_mutex_destroy(platformLock) + precondition(result == 0, "pthread_mutex_destroy failed") +#endif + platformLock.deinitialize(count: 1) + } + + fileprivate static func lock(_ platformLock: PlatformLock) { +#if canImport(Darwin) + os_unfair_lock_lock(platformLock) +#elseif canImport(Glibc) + pthread_mutex_lock(platformLock) +#elseif canImport(WinSDK) + AcquireSRWLockExclusive(platformLock) +#endif + } + + fileprivate static func unlock(_ platformLock: PlatformLock) { +#if canImport(Darwin) + os_unfair_lock_unlock(platformLock) +#elseif canImport(Glibc) + let result = pthread_mutex_unlock(platformLock) + precondition(result == 0, "pthread_mutex_unlock failed") +#elseif canImport(WinSDK) + ReleaseSRWLockExclusive(platformLock) +#endif + } + + static func allocate() -> Lock { + let platformLock = PlatformLock.allocate(capacity: 1) + initialize(platformLock) + return Lock(platformLock) + } + + func deinitialize() { + Lock.deinitialize(platformLock) + } + + func lock() { + Lock.lock(platformLock) + } + + func unlock() { + Lock.unlock(platformLock) + } + + /// 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. + func withLock(_ body: () throws -> T) rethrows -> T { + self.lock() + defer { + self.unlock() + } + return try body() + } + + // specialise Void return (for performance) + func withLockVoid(_ body: () throws -> Void) rethrows -> Void { + try self.withLock(body) + } +} + +struct ManagedCriticalState { + private final class LockedBuffer: ManagedBuffer { + deinit { + withUnsafeMutablePointerToElements { Lock.deinitialize($0) } + } + } + + private let buffer: ManagedBuffer + + init(_ initial: State) { + buffer = LockedBuffer.create(minimumCapacity: 1) { buffer in + buffer.withUnsafeMutablePointerToElements { Lock.initialize($0) } + return initial + } + } + + @discardableResult + func withCriticalRegion(_ critical: (inout State) throws -> R) rethrows -> R { + try buffer.withUnsafeMutablePointers { header, lock in + Lock.lock(lock) + defer { Lock.unlock(lock) } + return try critical(&header.pointee) + } + } + + func apply(criticalState newState: State) { + self.withCriticalRegion { actual in + actual = newState + } + } + + var criticalState: State { + self.withCriticalRegion { $0 } + } +} + +extension ManagedCriticalState: @unchecked Sendable where State: Sendable { } diff --git a/Sources/Supporting/ManagedCriticalState.swift b/Sources/Supporting/ManagedCriticalState.swift deleted file mode 100644 index 102b7d0..0000000 --- a/Sources/Supporting/ManagedCriticalState.swift +++ /dev/null @@ -1,45 +0,0 @@ -import Darwin - -final class LockedBuffer: ManagedBuffer { - deinit { - _ = self.withUnsafeMutablePointerToElements { lock in - lock.deinitialize(count: 1) - } - } -} - -struct ManagedCriticalState { - let buffer: ManagedBuffer - - init(_ initial: State) { - buffer = LockedBuffer.create(minimumCapacity: 1) { buffer in - buffer.withUnsafeMutablePointerToElements { lock in - lock.initialize(to: os_unfair_lock()) - } - return initial - } - } - - @discardableResult - func withCriticalRegion( - _ critical: (inout State) throws -> R - ) rethrows -> R { - try buffer.withUnsafeMutablePointers { header, lock in - os_unfair_lock_lock(lock) - defer { os_unfair_lock_unlock(lock) } - return try critical(&header.pointee) - } - } - - func apply(criticalState newState: State) { - self.withCriticalRegion { actual in - actual = newState - } - } - - var criticalState: State { - self.withCriticalRegion { $0 } - } -} - -extension ManagedCriticalState: @unchecked Sendable where State: Sendable { }