Created
February 18, 2026 05:51
-
-
Save timboudreau/f576f1332bb899b0aaf8d188e93ffd96 to your computer and use it in GitHub Desktop.
An API for atomic integers in Swift that is not complete pants
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| We generate two implementations for each integer type (optionally including UInt128, which won't compile | |
| on MacOS < 15, so it's not there by default), including size_t and ptrdiff_t (aka Rust's isize): | |
| * SwiftAtomic$TYPE - A class type which owns the allocation and implements SwiftAtomicInteger | |
| * Atomic$TYPEPointer - A struct which consumes an UnsafeMutablePointer<$TYPE> and does *not* involve a | |
| reference-counted class type (but the code that instantiates it is entirely responsible for ensuring | |
| the pointee is not dropped, and for explicitly dropping it in deinit) | |
| For the second, it would be nicer to implement IntegerAtomic *directly* on UnsafeMutablePointer, but Swift | |
| doesn't allow more than one such implementation, so we need separate wrapper types - but being a struct, | |
| it should consume no more memory than the underlying pointer. | |
| */ | |
| /// Base protocol for integers which can atomically be read, written and perform compare-and-swap operations. | |
| public protocol IntegerAtomic { | |
| associatedtype IntegerType : BinaryInteger | |
| /// Reads and writes the value with acquire/release semantics. | |
| var value : Self.IntegerType { get set } | |
| /// Reads and writes the value with sequentially-consistent semantics. | |
| var value_strong : Self.IntegerType { get set } | |
| /// Set the value. | |
| func set_atomic(_ value : Self.IntegerType) | |
| /// Set the value using sequentially-consistent semantics. | |
| func set_strong_atomic(_ value: Self.IntegerType) | |
| /// Increment the value. | |
| func increment() | |
| /// Decrement the value. | |
| func decrement() | |
| /// Exchange the value, returning the previous one. | |
| func exchange(_ value : Self.IntegerType) -> Self.IntegerType | |
| /// Exchange the value, returning the previous one, using sequentially-consistent semantics. | |
| func exchange_strong(_ value : Self.IntegerType) -> Self.IntegerType | |
| /// Perform a CAS (compare-and-set) operation. | |
| func compare_exchange(_ value : Self.IntegerType, expect : Self.IntegerType) -> CompareExchangeResult<Self.IntegerType> | |
| /// Perform a CAS (compare-and-set) operation with sequentially-consistent semantics. | |
| func compare_exchange_strong(_ value : Self.IntegerType, expect : Self.IntegerType) -> CompareExchangeResult<Self.IntegerType> | |
| /// Set the value to 0, returning true if that caused the value to change. | |
| func clear() -> Bool | |
| /// Logically `or` the existing value with the passed value, returning the prior value. | |
| func or(_ value : Self.IntegerType) -> Self.IntegerType | |
| /// Logically `and` the existing value with the passed value, returning the prior value. | |
| func and(_ value : Self.IntegerType) -> Self.IntegerType | |
| /// Logically `not-and` - that is, `and` the passed value and the bitwise inverse of the stored value, returning the prior value. | |
| func not_and(_ value : Self.IntegerType) -> Self.IntegerType | |
| /// Logically `xor` the existing value with a new one, returning the prior value. | |
| func xor(_ value : Self.IntegerType) -> Self.IntegerType | |
| } | |
| /// Protocol for straightforward atomic integers in Swift, where an allocation is needed. | |
| public protocol SwiftAtomicInteger : Sendable, CustomStringConvertible, IntegerAtomic { | |
| /// Create a new instance initialized to zero. | |
| init() | |
| /// Create a new instance initialized with a value. | |
| init(_ value : Self.IntegerType) | |
| /// Create a new instance initialized from a pointer. | |
| init(pointer : UnsafeMutablePointer<Self.IntegerType>) | |
| } | |
| public extension IntegerAtomic { | |
| /// Logically `and`s the value with the bitwise inverse of the passed value, returning the prior value. | |
| func and_not(_ value : Self.IntegerType) -> Self.IntegerType { | |
| self.and(~value) | |
| } | |
| } | |
| /// Wraps an `OptionSet` type which is backed by a `BinaryInteger` type in atomic storage. | |
| public struct AtomicOptionSet<T : OptionSet, I: SwiftAtomicInteger> : Sendable where T.RawValue == I.IntegerType, T.Element == T { | |
| private let _value : I | |
| /// Get the raw integer value | |
| public var rawValue : I.IntegerType { | |
| _value.value | |
| } | |
| /// Get the option set value. | |
| public var value : T { | |
| get { | |
| .init(rawValue: _value.value) | |
| } nonmutating set(v) { | |
| _value.set_atomic(v.rawValue) | |
| } | |
| } | |
| /// Get the option set value using sequentially-consistent semantics. | |
| public var value_strong : T { | |
| get { | |
| .init(rawValue: _value.value_strong) | |
| } nonmutating set(v) { | |
| _value.set_strong_atomic(v.rawValue) | |
| } | |
| } | |
| /// Initialize with a raw value. | |
| public init(_ value : I.IntegerType) { | |
| self._value = .init(value) | |
| } | |
| /// Initialize with an `OptionSet` value (the declaration will need to be annotated with the type of `I`). | |
| public init(_ value : I) { | |
| self._value = value | |
| } | |
| /// Atomically exchange the current value with the new value, returning the old value. | |
| public func exchange(_ value : T) -> T { | |
| T.init(rawValue: self._value.exchange(value.rawValue)) | |
| } | |
| /// Test if the passed value is a member of this set. | |
| public func contains(_ value : T) -> Bool { | |
| self.value.contains(value) | |
| } | |
| /// Insert a value, atomically, into this set, returning true if the value changed. | |
| public func insert(_ value : T) -> Bool { | |
| let raw = value.rawValue | |
| let old = self._value.or(raw) | |
| return !T.init(rawValue: old).contains(value) | |
| } | |
| /// Remove a value, atomically, into this set, returning true if the value changed. | |
| public func remove(_ value : T) -> Bool { | |
| let raw = value.rawValue | |
| let old = self._value.and(~raw) | |
| // return T.init(rawValue: old).contains(value) | |
| return old & raw != 0 | |
| } | |
| } | |
| extension AtomicOptionSet : CustomStringConvertible where T: CustomStringConvertible { | |
| public var description : String { | |
| value.description | |
| } | |
| } | |
| /// Result of a compare-and-set operation on an atomic integer. | |
| public enum CompareExchangeResult<T: Sendable & Copyable & Equatable & Hashable> : Equatable, Hashable, Sendable { | |
| /// The operation succeeded | |
| case Success(T) | |
| /// The operation failed | |
| case Failure(T) | |
| /// The value | |
| public var value : T { | |
| switch self { | |
| case .Success(let t): t | |
| case .Failure(let t): t | |
| } | |
| } | |
| /// Whether or not the operation succeeded | |
| public var succeeded : Bool { | |
| switch self { | |
| case .Success: true | |
| case .Failure: false | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment