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 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
Prev Previous commit
ManagedAtomic, ManagedAtomicLazyReference: Only forward to noncopyabl…
…e atomics when they exist
  • Loading branch information
lorentey committed Aug 19, 2023
commit 24506678e022d40a3bf98bcbd9b3d8f89a8d9b49
15 changes: 15 additions & 0 deletions Sources/Atomics/AtomicBool.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -266,9 +266,16 @@ extension ManagedAtomic where Value == Bool {
${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
}
Expand All @@ -287,9 +294,17 @@ extension ManagedAtomic where Value == Bool {
${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
}
53 changes: 53 additions & 0 deletions Sources/Atomics/AtomicLazyReference.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//

#if compiler(>=5.9) && $RawLayout
/// A lazily initializable atomic strong reference.
///
/// These values can be set (initialized) exactly once, but read many
Expand Down Expand Up @@ -85,6 +86,7 @@ extension AtomicLazyReference {
return value?.takeUnretainedValue()
}
}
#endif

/// An unsafe reference type holding a lazily initializable atomic
/// strong reference, requiring manual memory management of the
Expand Down Expand Up @@ -255,6 +257,7 @@ public class ManagedAtomicLazyReference<Instance: AnyObject> {
/// The value logically stored in an atomic lazy reference value.
public typealias Value = Instance?

#if compiler(>=5.9) && $RawLayout
/// The actual lazily initialized reference value.
@usableFromInline
internal let _storage: AtomicLazyReference<Instance>
Expand All @@ -265,10 +268,38 @@ public class ManagedAtomicLazyReference<Instance: AnyObject> {
_storage = AtomicLazyReference()
}

deinit {}
#else
@usableFromInline
internal typealias _Rep = Optional<Unmanaged<Instance>>.AtomicRepresentation

/// The atomic representation of the value stored inside.
///
/// Warning: This ivar must only ever be accessed via `_ptr` after
/// its initialization.
@usableFromInline
internal let _storage: _Rep

/// Initializes a new managed atomic lazy reference with a nil value.
@inlinable
public init() {
_storage = _Rep(nil)
}

deinit {
if let unmanaged = _ptr.pointee.dispose() {
unmanaged.release()
}
}

@_alwaysEmitIntoClient @inline(__always)
internal var _ptr: UnsafeMutablePointer<_Rep> {
_getUnsafePointerToStoredProperties(self).assumingMemoryBound(to: _Rep.self)
}
#endif
}


extension ManagedAtomicLazyReference: @unchecked Sendable
where Instance: Sendable {}

Expand Down Expand Up @@ -298,14 +329,36 @@ extension ManagedAtomicLazyReference {
///
/// This operation uses acquiring-and-releasing memory ordering.
public func storeIfNilThenLoad(_ desired: __owned Instance) -> Instance {
#if compiler(>=5.9) && $RawLayout
_storage.storeIfNilThenLoad(desired)
#else
let desiredUnmanaged = Unmanaged.passRetained(desired)
let (exchanged, current) = _Rep.atomicCompareExchange(
expected: nil,
desired: desiredUnmanaged,
at: _ptr,
ordering: .acquiringAndReleasing)
if !exchanged {
// The reference has already been initialized. Balance the retain that
// we performed on `desired`.
desiredUnmanaged.release()
return current!.takeUnretainedValue()
}
return desiredUnmanaged.takeUnretainedValue()
#endif
}

/// Atomically loads and returns the current value of this reference.
///
/// The load operation is performed with the memory ordering
/// `AtomicLoadOrdering.acquiring`.
public func load() -> Instance? {
#if compiler(>=5.9) && $RawLayout
_storage.load()
#else
let value = _Rep.atomicLoad(at: _ptr, ordering: .acquiring)
return value?.takeUnretainedValue()
#endif
}
}

29 changes: 29 additions & 0 deletions Sources/Atomics/IntegerOperations.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,16 @@ extension ManagedAtomic where Value: AtomicInteger {
${label} operand: Value${" = 1" if "crement" in name else ""},
ordering: AtomicUpdateOrdering
) -> Value {
#if compiler(>=5.9) && $RawLayout
_storage.loadThen${name}(
${argLabel(label)}operand,
ordering: ordering)
#else
_Storage.atomicLoadThen${name}(
${argLabel(label)}operand,
at: _ptr,
ordering: ordering)
#endif
}

% end
Expand All @@ -248,9 +255,17 @@ extension ManagedAtomic where Value: AtomicInteger {
${label} operand: Value${" = 1" if "crement" in name else ""},
ordering: AtomicUpdateOrdering
) -> Value {
#if compiler(>=5.9) && $RawLayout
_storage.${lowerFirst(name)}ThenLoad(
${argLabel(label)}operand,
ordering: ordering)
#else
let original = _Storage.atomicLoadThen${name}(
${argLabel(label)}operand,
at: _ptr,
ordering: ordering)
return original ${op} operand
#endif
}

% end
Expand All @@ -269,9 +284,16 @@ extension ManagedAtomic where Value: AtomicInteger {
by operand: Value = 1,
ordering: AtomicUpdateOrdering
) {
#if compiler(>=5.9) && $RawLayout
_ = _storage.loadThenWrappingIncrement(
by: operand,
ordering: ordering)
#else
_ = _Storage.atomicLoadThenWrappingIncrement(
by: operand,
at: _ptr,
ordering: ordering)
#endif
}

/// Perform an atomic wrapping decrement operation applying the
Expand All @@ -288,8 +310,15 @@ extension ManagedAtomic where Value: AtomicInteger {
by operand: Value = 1,
ordering: AtomicUpdateOrdering
) {
#if compiler(>=5.9) && $RawLayout
_ = _storage.loadThenWrappingDecrement(
by: operand,
ordering: ordering)
#else
_ = _Storage.atomicLoadThenWrappingDecrement(
by: operand,
at: _ptr,
ordering: ordering)
#endif
}
}
75 changes: 75 additions & 0 deletions Sources/Atomics/ManagedAtomic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ where Value.AtomicRepresentation.Value == Value {
// (We'd need one set of implementations for the type equality condition, and
// another for `Value: AtomicReference`.)

#if compiler(>=5.9) && $RawLayout
@usableFromInline
internal let _storage: Atomic<Value>

Expand All @@ -28,6 +29,34 @@ where Value.AtomicRepresentation.Value == Value {
public init(_ value: Value) {
_storage = Atomic(value)
}
#else
@usableFromInline
internal typealias _Storage = Value.AtomicRepresentation

/// The atomic representation of the value stored inside.
///
/// Warning: This ivar must only ever be accessed via `_ptr` after
/// its initialization.
@usableFromInline
internal var _storage: _Storage

/// Initialize a new managed atomic instance holding the specified initial
/// value.
@inline(__always) @_alwaysEmitIntoClient
public init(_ value: Value) {
_storage = _Storage(value)
}

deinit {
_ = _ptr.pointee.dispose()
}

@_alwaysEmitIntoClient @inline(__always)
internal var _ptr: UnsafeMutablePointer<_Storage> {
_getUnsafePointerToStoredProperties(self)
.assumingMemoryBound(to: _Storage.self)
}
#endif
}

extension ManagedAtomic: @unchecked Sendable where Value: Sendable {}
Expand All @@ -43,7 +72,11 @@ extension ManagedAtomic {
public func load(
ordering: AtomicLoadOrdering
) -> Value {
#if compiler(>=5.9) && $RawLayout
_storage.load(ordering: ordering)
#else
_Storage.atomicLoad(at: _ptr, ordering: ordering)
#endif
}

/// Atomically sets the current value to `desired`, applying the specified
Expand All @@ -57,7 +90,11 @@ extension ManagedAtomic {
_ desired: __owned Value,
ordering: AtomicStoreOrdering
) {
#if compiler(>=5.9) && $RawLayout
_storage.store(desired, ordering: ordering)
#else
_Storage.atomicStore(desired, at: _ptr, ordering: ordering)
#endif
}

/// Atomically sets the current value to `desired` and returns the original
Expand All @@ -72,7 +109,11 @@ extension ManagedAtomic {
_ desired: __owned Value,
ordering: AtomicUpdateOrdering
) -> Value {
#if compiler(>=5.9) && $RawLayout
_storage.exchange(desired, ordering: ordering)
#else
_Storage.atomicExchange(desired, at: _ptr, ordering: ordering)
#endif
}

/// Perform an atomic compare and exchange operation on the current value,
Expand Down Expand Up @@ -105,8 +146,16 @@ extension ManagedAtomic {
desired: __owned Value,
ordering: AtomicUpdateOrdering
) -> (exchanged: Bool, original: Value) {
#if compiler(>=5.9) && $RawLayout
_storage.compareExchange(
expected: expected, desired: desired, ordering: ordering)
#else
_Storage.atomicCompareExchange(
expected: expected,
desired: desired,
at: _ptr,
ordering: ordering)
#endif
}

/// Perform an atomic compare and exchange operation on the current value,
Expand Down Expand Up @@ -147,11 +196,20 @@ extension ManagedAtomic {
successOrdering: AtomicUpdateOrdering,
failureOrdering: AtomicLoadOrdering
) -> (exchanged: Bool, original: Value) {
#if compiler(>=5.9) && $RawLayout
_storage.compareExchange(
expected: expected,
desired: desired,
successOrdering: successOrdering,
failureOrdering: failureOrdering)
#else
_Storage.atomicCompareExchange(
expected: expected,
desired: desired,
at: _ptr,
successOrdering: successOrdering,
failureOrdering: failureOrdering)
#endif
}

/// Perform an atomic weak compare and exchange operation on the current
Expand Down Expand Up @@ -187,10 +245,18 @@ extension ManagedAtomic {
desired: __owned Value,
ordering: AtomicUpdateOrdering
) -> (exchanged: Bool, original: Value) {
#if compiler(>=5.9) && $RawLayout
_storage.weakCompareExchange(
expected: expected,
desired: desired,
ordering: ordering)
#else
_Storage.atomicWeakCompareExchange(
expected: expected,
desired: desired,
at: _ptr,
ordering: ordering)
#endif
}

/// Perform an atomic weak compare and exchange operation on the current
Expand Down Expand Up @@ -234,10 +300,19 @@ extension ManagedAtomic {
successOrdering: AtomicUpdateOrdering,
failureOrdering: AtomicLoadOrdering
) -> (exchanged: Bool, original: Value) {
#if compiler(>=5.9) && $RawLayout
_storage.weakCompareExchange(
expected: expected,
desired: desired,
successOrdering: successOrdering,
failureOrdering: failureOrdering)
#else
_Storage.atomicWeakCompareExchange(
expected: expected,
desired: desired,
at: _ptr,
successOrdering: successOrdering,
failureOrdering: failureOrdering)
#endif
}
}
Loading