Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce a noncopyable Atomic<T> construct #94

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ _cSettings += [
]
_swiftSettings += [
.define("ATOMICS_NATIVE_BUILTINS"),
.enableExperimentalFeature("BuiltinModule")
.enableExperimentalFeature("BuiltinModule"),
.enableExperimentalFeature("RawLayout")
]

let package = Package(
Expand Down
265 changes: 265 additions & 0 deletions Sources/Atomics/Atomic.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Atomics open source project
//
// Copyright (c) 2023 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
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#if compiler(>=5.9) && $RawLayout
import Builtin

/// An atomic value.
@_rawLayout(like: Value.AtomicRepresentation)
@frozen
public struct Atomic<Value: AtomicValue>: ~Copyable
where Value.AtomicRepresentation.Value == Value
{
@usableFromInline
internal typealias _Storage = Value.AtomicRepresentation

@_transparent @_alwaysEmitIntoClient
internal var _ptr: UnsafeMutablePointer<_Storage> {
.init(Builtin.unprotectedAddressOfBorrow(self))
}

public init(_ initialValue: __owned Value) {
_ptr.initialize(to: _Storage(initialValue))
}

#if false // FIXME: This doesn't work correctly yet
public consuming func destroy() -> Value {
let value = _ptr.pointee.dispose()
_ptr.deinitialize(count: 1)
discard self // Doesn't yet work for raw layout types
return value
}
#endif

@inlinable
deinit {
_ = _ptr.pointee.dispose()
_ptr.deinitialize(count: 1)
}
}

extension Atomic {
/// Atomically loads and returns the current value, applying the specified
/// memory ordering.
///
/// - Parameter ordering: The memory ordering to apply on this operation.
/// - Returns: The current value.
@_semantics("atomics.requires_constant_orderings")
@_transparent @_alwaysEmitIntoClient
public func load(
ordering: AtomicLoadOrdering
) -> Value {
_Storage.atomicLoad(at: _ptr, ordering: ordering)
}

/// Atomically sets the current value to `desired`, applying the specified
/// memory ordering.
///
/// - Parameter desired: The desired new value.
/// - Parameter ordering: The memory ordering to apply on this operation.
@_semantics("atomics.requires_constant_orderings")
@_transparent @_alwaysEmitIntoClient
public func store(
_ desired: __owned Value,
ordering: AtomicStoreOrdering
) {
_Storage.atomicStore(desired, at: _ptr, ordering: ordering)
}

/// Atomically sets the current value to `desired` and returns the original
/// value, applying the specified memory ordering.
///
/// - Parameter desired: The desired new value.
/// - Parameter ordering: The memory ordering to apply on this operation.
/// - Returns: The original value.
@_semantics("atomics.requires_constant_orderings")
@_transparent @_alwaysEmitIntoClient
public func exchange(
_ desired: __owned Value,
ordering: AtomicUpdateOrdering
) -> Value {
_Storage.atomicExchange(desired, at: _ptr, ordering: ordering)
}

/// Perform an atomic compare and exchange operation on the current value,
/// applying the specified memory ordering.
///
/// This operation performs the following algorithm as a single atomic
/// transaction:
///
/// ```
/// atomic(self) { currentValue in
/// let original = currentValue
/// guard original == expected else { return (false, original) }
/// currentValue = desired
/// return (true, original)
/// }
/// ```
///
/// This method implements a "strong" compare and exchange operation
/// that does not permit spurious failures.
///
/// - Parameter expected: The expected current value.
/// - Parameter desired: The desired new value.
/// - Parameter ordering: The memory ordering to apply on this operation.
/// - Returns: A tuple `(exchanged, original)`, where `exchanged` is true if
/// the exchange was successful, and `original` is the original value.
@_semantics("atomics.requires_constant_orderings")
@_transparent @_alwaysEmitIntoClient
public func compareExchange(
expected: Value,
desired: __owned Value,
ordering: AtomicUpdateOrdering
) -> (exchanged: Bool, original: Value) {
_Storage.atomicCompareExchange(
expected: expected,
desired: desired,
at: _ptr,
ordering: ordering)
}

/// Perform an atomic compare and exchange operation on the current value,
/// applying the specified success/failure memory orderings.
///
/// This operation performs the following algorithm as a single atomic
/// transaction:
///
/// ```
/// atomic(self) { currentValue in
/// let original = currentValue
/// guard original == expected else { return (false, original) }
/// currentValue = desired
/// return (true, original)
/// }
/// ```
///
/// The `successOrdering` argument specifies the memory ordering to use when
/// the operation manages to update the current value, while `failureOrdering`
/// will be used when the operation leaves the value intact.
///
/// This method implements a "strong" compare and exchange operation
/// that does not permit spurious failures.
///
/// - Parameter expected: The expected current value.
/// - Parameter desired: The desired new value.
/// - Parameter successOrdering: The memory ordering to apply if this
/// operation performs the exchange.
/// - Parameter failureOrdering: The memory ordering to apply on this
/// operation does not perform the exchange.
/// - Returns: A tuple `(exchanged, original)`, where `exchanged` is true if
/// the exchange was successful, and `original` is the original value.
@_semantics("atomics.requires_constant_orderings")
@_transparent @_alwaysEmitIntoClient
public func compareExchange(
expected: Value,
desired: __owned Value,
successOrdering: AtomicUpdateOrdering,
failureOrdering: AtomicLoadOrdering
) -> (exchanged: Bool, original: Value) {
_Storage.atomicCompareExchange(
expected: expected,
desired: desired,
at: _ptr,
successOrdering: successOrdering,
failureOrdering: failureOrdering)
}

/// Perform an atomic weak compare and exchange operation on the current
/// value, applying the memory ordering. This compare-exchange variant is
/// allowed to spuriously fail; it is designed to be called in a loop until
/// it indicates a successful exchange has happened.
///
/// This operation performs the following algorithm as a single atomic
/// transaction:
///
/// ```
/// atomic(self) { currentValue in
/// let original = currentValue
/// guard original == expected else { return (false, original) }
/// currentValue = desired
/// return (true, original)
/// }
/// ```
///
/// (In this weak form, transient conditions may cause the `original ==
/// expected` check to sometimes return false when the two values are in fact
/// the same.)
///
/// - Parameter expected: The expected current value.
/// - Parameter desired: The desired new value.
/// - Parameter ordering: The memory ordering to apply on this operation.
/// - Returns: A tuple `(exchanged, original)`, where `exchanged` is true if
/// the exchange was successful, and `original` is the original value.
@_semantics("atomics.requires_constant_orderings")
@_transparent @_alwaysEmitIntoClient
public func weakCompareExchange(
expected: Value,
desired: __owned Value,
ordering: AtomicUpdateOrdering
) -> (exchanged: Bool, original: Value) {
_Storage.atomicWeakCompareExchange(
expected: expected,
desired: desired,
at: _ptr,
ordering: ordering)
}

/// Perform an atomic weak compare and exchange operation on the current
/// value, applying the specified success/failure memory orderings. This
/// compare-exchange variant is allowed to spuriously fail; it is designed to
/// be called in a loop until it indicates a successful exchange has happened.
///
/// This operation performs the following algorithm as a single atomic
/// transaction:
///
/// ```
/// atomic(self) { currentValue in
/// let original = currentValue
/// guard original == expected else { return (false, original) }
/// currentValue = desired
/// return (true, original)
/// }
/// ```
///
/// (In this weak form, transient conditions may cause the `original ==
/// expected` check to sometimes return false when the two values are in fact
/// the same.)
///
/// The `ordering` argument specifies the memory ordering to use when the
/// operation manages to update the current value, while `failureOrdering`
/// will be used when the operation leaves the value intact.
///
/// - Parameter expected: The expected current value.
/// - Parameter desired: The desired new value.
/// - Parameter successOrdering: The memory ordering to apply if this
/// operation performs the exchange.
/// - Parameter failureOrdering: The memory ordering to apply on this
/// operation does not perform the exchange.
/// - Returns: A tuple `(exchanged, original)`, where `exchanged` is true if
/// the exchange was successful, and `original` is the original value.
@_semantics("atomics.requires_constant_orderings")
@_transparent @_alwaysEmitIntoClient
public func weakCompareExchange(
expected: Value,
desired: __owned Value,
successOrdering: AtomicUpdateOrdering,
failureOrdering: AtomicLoadOrdering
) -> (exchanged: Bool, original: Value) {
_Storage.atomicWeakCompareExchange(
expected: expected,
desired: desired,
at: _ptr,
successOrdering: successOrdering,
failureOrdering: failureOrdering)
}
}
#endif
77 changes: 75 additions & 2 deletions Sources/Atomics/AtomicBool.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,17 @@
}%
${autogenerated_warning()}

#if !ATOMICS_NATIVE_BUILTINS
#if ATOMICS_NATIVE_BUILTINS
import Builtin

extension Bool {
@_alwaysEmitIntoClient
@inline(__always)
internal init(_ builtin: Builtin.Int1) {
self = unsafeBitCast(builtin, to: Bool.self)
}
}
#else
import _AtomicsShims
#endif

Expand Down Expand Up @@ -189,7 +199,10 @@ extension Bool.AtomicRepresentation {
% end
}

% for construct in ["UnsafeAtomic", "ManagedAtomic"]:
% for construct in ["Atomic", "UnsafeAtomic"]:
% if construct == "Atomic":
#if compiler(>=5.9) && $RawLayout
% end
extension ${construct} where Value == Bool {
% for (name, iname, op, label, doc) in boolOperations:
/// Perform an atomic ${doc} operation and return the original value, applying
Expand Down Expand Up @@ -234,4 +247,64 @@ extension ${construct} where Value == Bool {
}
% end
}
% if construct == "Atomic":
#endif
% end
% end

extension ManagedAtomic where Value == Bool {
% for (name, iname, op, label, doc) in boolOperations:
/// Perform an atomic ${doc} operation and return the original value, applying
/// the specified memory ordering.
///
/// - Parameter operand: A boolean value.
/// - Parameter ordering: The memory ordering to apply on this operation.
/// - Returns: The original value before the operation.
@_semantics("atomics.requires_constant_orderings")
@_transparent @_alwaysEmitIntoClient
public func loadThen${name}(
${label} operand: Value,
ordering: AtomicUpdateOrdering
) -> Value {
#if compiler(>=5.9) && $RawLayout
_storage.loadThen${name}(
${argLabel(label)}operand,
ordering: ordering)
#else
Value.AtomicRepresentation.atomicLoadThen${name}(
${argLabel(label)}operand,
at: _ptr,
ordering: ordering)
#endif
}
% end
}

extension ManagedAtomic where Value == Bool {
% for (name, iname, op, label, doc) in boolOperations:
/// Perform an atomic ${doc} operation and return the original value, applying
/// the specified memory ordering.
///
/// - Parameter operand: A boolean value.
/// - Parameter ordering: The memory ordering to apply on this operation.
/// - Returns: The original value before the operation.
@_semantics("atomics.requires_constant_orderings")
@_transparent @_alwaysEmitIntoClient
public func ${lowerFirst(name)}ThenLoad(
${label} operand: Value,
ordering: AtomicUpdateOrdering
) -> Value {
#if compiler(>=5.9) && $RawLayout
_storage.${lowerFirst(name)}ThenLoad(
${argLabel(label)}operand,
ordering: ordering)
#else
let original = Value.AtomicRepresentation.atomicLoadThen${name}(
${argLabel(label)}operand,
at: _ptr,
ordering: ordering)
return original ${op} operand
#endif
}
% end
}
Loading