From d3a4e103b62eca8b9231cf720e3c226ac360a160 Mon Sep 17 00:00:00 2001 From: Luke Howard Date: Sun, 4 Oct 2026 09:27:03 +1100 Subject: [PATCH] Back ManagedCriticalState with Synchronization.Mutex Replace the os_unfair_lock ManagedBuffer with a Mutex held in a final class box, so the wrapper keeps its reference semantics and copyable holders while carrying no platform-specific locking code; it was the only platform-specific code in Sources. Mutex needs macOS 15 / iOS 18 / tvOS 18 / watchOS 11 / visionOS 2, so the manifest moves to that floor (tools 5.9 for the visionOS entry; the language mode is unchanged). Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01T8cahEd4GfsHQ89sNrmP4p --- Package.swift | 11 ++--- Sources/Supporting/ManagedCriticalState.swift | 41 +++++++------------ 2 files changed, 21 insertions(+), 31 deletions(-) diff --git a/Package.swift b/Package.swift index 5252a8b..306bac9 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.8 +// swift-tools-version:5.9 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -6,10 +6,11 @@ import PackageDescription let package = Package( name: "AsyncExtensions", platforms: [ - .iOS(.v13), - .macOS(.v10_15), - .tvOS(.v13), - .watchOS(.v6) + .iOS("18.0"), + .macOS("15.0"), + .tvOS("18.0"), + .watchOS("11.0"), + .visionOS("2.0") ], products: [ .library( diff --git a/Sources/Supporting/ManagedCriticalState.swift b/Sources/Supporting/ManagedCriticalState.swift index 102b7d0..7dcb7a9 100644 --- a/Sources/Supporting/ManagedCriticalState.swift +++ b/Sources/Supporting/ManagedCriticalState.swift @@ -1,44 +1,33 @@ -import Darwin +import Synchronization -final class LockedBuffer: ManagedBuffer { - deinit { - _ = self.withUnsafeMutablePointerToElements { lock in - lock.deinitialize(count: 1) +/// State guarded by a `Mutex`, with reference semantics so that iterators and state machines +/// can hold it by value and copy it. +struct ManagedCriticalState { + private final class Storage: @unchecked Sendable { + let mutex: Mutex + + init(_ initial: State) { + mutex = Mutex(initial) } } -} -struct ManagedCriticalState { - let buffer: ManagedBuffer + private let storage: Storage init(_ initial: State) { - buffer = LockedBuffer.create(minimumCapacity: 1) { buffer in - buffer.withUnsafeMutablePointerToElements { lock in - lock.initialize(to: os_unfair_lock()) - } - return initial - } + storage = Storage(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 withCriticalRegion(_ critical: (inout State) throws -> R) rethrows -> R { + try storage.mutex.withLock { state in try critical(&state) } } func apply(criticalState newState: State) { - self.withCriticalRegion { actual in - actual = newState - } + withCriticalRegion { $0 = newState } } var criticalState: State { - self.withCriticalRegion { $0 } + withCriticalRegion { $0 } } }