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

Add a weakCompareExchange variant that only takes a single ordering #76

Merged
merged 2 commits into from
Mar 25, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
9 changes: 5 additions & 4 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,13 @@ let package = Package(
dependencies: ["_AtomicsShims"],
exclude: [
"CMakeLists.txt",
"Builtins.swift.gyb",
"HighLevelTypes.swift.gyb",
"PointerConformances.swift.gyb",
"IntegerConformances.swift.gyb",
"AtomicBool.swift.gyb",
"AtomicLazyReference.swift.gyb",
"HighLevelTypes.swift.gyb",
"IntegerConformances.swift.gyb",
"PointerConformances.swift.gyb",
"Primitives.native.swift.gyb",
"Primitives.shims.swift.gyb",
],
cSettings: _cSettings,
swiftSettings: _swiftSettings
Expand Down
18 changes: 18 additions & 0 deletions Sources/Atomics/AtomicMemoryOrderings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,24 @@ extension AtomicUpdateOrdering: CustomStringConvertible {
}
}

extension AtomicLoadOrdering {
@_semantics("constant_evaluable")
@_semantics("atomics.requires_constant_orderings")
@_transparent @_alwaysEmitIntoClient
static func _failureOrdering(
for ordering: AtomicUpdateOrdering
) -> AtomicLoadOrdering {
switch ordering {
case .relaxed: return .relaxed
case .acquiring: return .acquiring
case .releasing: return .relaxed
case .acquiringAndReleasing: return .acquiring
case .sequentiallyConsistent: return .sequentiallyConsistent
default: fatalError("Unsupported ordering")
}
}
}

/// Establishes a memory ordering without associating it with a
/// particular atomic operation.
///
Expand Down
2 changes: 2 additions & 0 deletions Sources/Atomics/AtomicStrongReference.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

#if ATOMICS_NATIVE_BUILTINS
import Swift
#else
import _AtomicsShims
#endif

/// A class type that supports atomic strong references.
Expand Down
71 changes: 71 additions & 0 deletions Sources/Atomics/AtomicValue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,43 @@ public protocol AtomicStorage {
failureOrdering: AtomicLoadOrdering
) -> (exchanged: Bool, original: Value)

/// Perform an atomic weak compare and exchange operation on the value
/// referenced by `pointer`, applying the specified 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.)
///
/// - Parameter expected: The expected current value.
/// - Parameter desired: The desired new value.
/// - Parameter pointer: A memory location previously initialized with a value
/// returned by `prepareAtomicRepresentation(for:)`.
/// - 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")
static func atomicWeakCompareExchange(
expected: Value,
desired: __owned Value,
at pointer: UnsafeMutablePointer<Self>,
ordering: AtomicUpdateOrdering
) -> (exchanged: Bool, original: Value)

/// Perform an atomic weak compare and exchange operation on the value
/// referenced by `pointer`, applying the specified success/failure memory
/// orderings. This compare-exchange variant is allowed to spuriously fail; it
Expand Down Expand Up @@ -221,3 +258,37 @@ public protocol AtomicStorage {
failureOrdering: AtomicLoadOrdering
) -> (exchanged: Bool, original: Value)
}

extension AtomicStorage {
@_semantics("atomics.requires_constant_orderings")
@_transparent @_alwaysEmitIntoClient
public static func atomicCompareExchange(
expected: Value,
desired: __owned Value,
at pointer: UnsafeMutablePointer<Self>,
ordering: AtomicUpdateOrdering
) -> (exchanged: Bool, original: Value) {
atomicCompareExchange(
expected: expected,
desired: desired,
at: pointer,
successOrdering: ordering,
failureOrdering: ._failureOrdering(for: ordering))
}

@_semantics("atomics.requires_constant_orderings")
@_transparent @_alwaysEmitIntoClient
public static func atomicWeakCompareExchange(
expected: Value,
desired: __owned Value,
at pointer: UnsafeMutablePointer<Self>,
ordering: AtomicUpdateOrdering
) -> (exchanged: Bool, original: Value) {
atomicWeakCompareExchange(
expected: expected,
desired: desired,
at: pointer,
successOrdering: ordering,
failureOrdering: ._failureOrdering(for: ordering))
}
}
40 changes: 40 additions & 0 deletions Sources/Atomics/HighLevelTypes.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,46 @@ extension ${type} {
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
Expand Down
80 changes: 80 additions & 0 deletions Sources/Atomics/autogenerated/HighLevelTypes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,46 @@ extension UnsafeAtomic {
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
Expand Down Expand Up @@ -654,6 +694,46 @@ extension ManagedAtomic {
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
Expand Down
14 changes: 8 additions & 6 deletions Tests/AtomicsTests/Basics/BasicTests.gyb-template
Original file line number Diff line number Diff line change
Expand Up @@ -125,36 +125,37 @@ class BasicAtomic${label}Tests: XCTestCase {

% end

% for (order, _, _, _, _) in updateOrderings:
func test_compareExchange_${order}() {
% for operation in ["compareExchange", "weakCompareExchange"]:
% for (order, _, _, _, _) in updateOrderings:
func test_${operation}_${order}() {
let v: UnsafeAtomic<${type}> = .create(${a})
defer { v.destroy() }

var (exchanged, original): (Bool, ${type}) = v.compareExchange(
var (exchanged, original): (Bool, ${type}) = v.${operation}(
expected: ${a},
desired: ${b},
ordering: .${order})
XCTAssertTrue(exchanged)
XCTAssertEqual(original, ${a})
XCTAssertEqual(v.load(ordering: .relaxed), ${b})

(exchanged, original) = v.compareExchange(
(exchanged, original) = v.${operation}(
expected: ${a},
desired: ${b},
ordering: .${order})
XCTAssertFalse(exchanged)
XCTAssertEqual(original, ${b})
XCTAssertEqual(v.load(ordering: .relaxed), ${b})

(exchanged, original) = v.compareExchange(
(exchanged, original) = v.${operation}(
expected: ${b},
desired: ${a},
ordering: .${order})
XCTAssertTrue(exchanged)
XCTAssertEqual(original, ${b})
XCTAssertEqual(v.load(ordering: .relaxed), ${a})

(exchanged, original) = v.compareExchange(
(exchanged, original) = v.${operation}(
expected: ${b},
desired: ${a},
ordering: .${order})
Expand All @@ -163,6 +164,7 @@ class BasicAtomic${label}Tests: XCTestCase {
XCTAssertEqual(v.load(ordering: .relaxed), ${a})
}

% end
% end

% for operation in ["compareExchange", "weakCompareExchange"]:
Expand Down
Loading