Skip to content

Commit c63d43f

Browse files
committed
AtomicLazyReference improvements
- De-gyb sources. - Introduce noncopyable variant, `AtomicLazyReference`. - Reformulate `ManagedAtomicLazyReference` in terms of `AtomicLazyReference`.
1 parent 2518e3e commit c63d43f

File tree

3 files changed

+125
-281
lines changed

3 files changed

+125
-281
lines changed

Sources/Atomics/autogenerated/AtomicLazyReference.swift renamed to Sources/Atomics/AtomicLazyReference.swift

Lines changed: 108 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,81 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
// #############################################################################
14-
// # #
15-
// # DO NOT EDIT THIS FILE; IT IS AUTOGENERATED. #
16-
// # #
17-
// #############################################################################
13+
/// A lazily initializable atomic strong reference.
14+
///
15+
/// These values can be set (initialized) exactly once, but read many
16+
/// times.
17+
@frozen
18+
public struct AtomicLazyReference<Instance: AnyObject>: ~Copyable {
19+
/// The value logically stored in an atomic lazy reference value.
20+
public typealias Value = Instance?
21+
22+
@usableFromInline
23+
internal let _storage: Atomic<Unmanaged<Instance>?>
24+
25+
/// Initializes a new managed atomic lazy reference with a nil value.
26+
@inlinable
27+
public init() {
28+
_storage = Atomic(nil)
29+
}
30+
31+
@inlinable
32+
deinit {
33+
if let unmanaged = _storage.load(ordering: .acquiring) {
34+
unmanaged.release()
35+
}
36+
}
37+
}
38+
39+
extension AtomicLazyReference {
40+
/// Atomically initializes this reference if its current value is nil, then
41+
/// returns the initialized value. If this reference is already initialized,
42+
/// then `storeIfNilThenLoad(_:)` discards its supplied argument and returns
43+
/// the current value without updating it.
44+
///
45+
/// The following example demonstrates how this can be used to implement a
46+
/// thread-safe lazily initialized reference:
47+
///
48+
/// ```
49+
/// class Image {
50+
/// var _histogram: UnsafeAtomicLazyReference<Histogram> = .init()
51+
///
52+
/// // This is safe to call concurrently from multiple threads.
53+
/// var atomicLazyHistogram: Histogram {
54+
/// if let histogram = _histogram.load() { return histogram }
55+
/// // Note that code here may run concurrently on
56+
/// // multiple threads, but only one of them will get to
57+
/// // succeed setting the reference.
58+
/// let histogram = ...
59+
/// return _histogram.storeIfNilThenLoad(histogram)
60+
/// }
61+
/// ```
62+
///
63+
/// This operation uses acquiring-and-releasing memory ordering.
64+
public func storeIfNilThenLoad(_ desired: __owned Instance) -> Instance {
65+
let desiredUnmanaged = Unmanaged.passRetained(desired)
66+
let (exchanged, current) = _storage.compareExchange(
67+
expected: nil,
68+
desired: desiredUnmanaged,
69+
ordering: .acquiringAndReleasing)
70+
if !exchanged {
71+
// The reference has already been initialized. Balance the retain that
72+
// we performed on `desired`.
73+
desiredUnmanaged.release()
74+
return current!.takeUnretainedValue()
75+
}
76+
return desiredUnmanaged.takeUnretainedValue()
77+
}
1878

79+
/// Atomically loads and returns the current value of this reference.
80+
///
81+
/// The load operation is performed with the memory ordering
82+
/// `AtomicLoadOrdering.acquiring`.
83+
public func load() -> Instance? {
84+
let value = _storage.load(ordering: .acquiring)
85+
return value?.takeUnretainedValue()
86+
}
87+
}
1988

2089
/// An unsafe reference type holding a lazily initializable atomic
2190
/// strong reference, requiring manual memory management of the
@@ -125,47 +194,6 @@ extension UnsafeAtomicLazyReference {
125194
}
126195
}
127196

128-
/// A reference type holding a lazily initializable atomic
129-
/// strong reference, with automatic memory management.
130-
///
131-
/// These values can be set (initialized) exactly once, but read many
132-
/// times.
133-
@_fixed_layout
134-
public class ManagedAtomicLazyReference<Instance: AnyObject> {
135-
/// The value logically stored in an atomic lazy reference value.
136-
public typealias Value = Instance?
137-
138-
@usableFromInline
139-
internal typealias _Rep = Optional<Unmanaged<Instance>>.AtomicRepresentation
140-
141-
/// The atomic representation of the value stored inside.
142-
///
143-
/// Warning: This ivar must only ever be accessed via `_ptr` after
144-
/// its initialization.
145-
@usableFromInline
146-
internal let _storage: _Rep
147-
148-
/// Initializes a new managed atomic lazy reference with a nil value.
149-
@inlinable
150-
public init() {
151-
_storage = _Rep(nil)
152-
}
153-
154-
deinit {
155-
if let unmanaged = _ptr.pointee.dispose() {
156-
unmanaged.release()
157-
}
158-
}
159-
160-
@_alwaysEmitIntoClient @inline(__always)
161-
internal var _ptr: UnsafeMutablePointer<_Rep> {
162-
_getUnsafePointerToStoredProperties(self).assumingMemoryBound(to: _Rep.self)
163-
}
164-
}
165-
166-
extension ManagedAtomicLazyReference: @unchecked Sendable
167-
where Instance: Sendable {}
168-
169197
extension UnsafeAtomicLazyReference {
170198
/// Atomically initializes this reference if its current value is nil, then
171199
/// returns the initialized value. If this reference is already initialized,
@@ -194,10 +222,10 @@ extension UnsafeAtomicLazyReference {
194222
public func storeIfNilThenLoad(_ desired: __owned Instance) -> Instance {
195223
let desiredUnmanaged = Unmanaged.passRetained(desired)
196224
let (exchanged, current) = _Rep.atomicCompareExchange(
197-
expected: nil,
198-
desired: desiredUnmanaged,
199-
at: _ptr,
200-
ordering: .acquiringAndReleasing)
225+
expected: nil,
226+
desired: desiredUnmanaged,
227+
at: _ptr,
228+
ordering: .acquiringAndReleasing)
201229
if !exchanged {
202230
// The reference has already been initialized. Balance the retain that
203231
// we performed on `desired`.
@@ -216,6 +244,34 @@ extension UnsafeAtomicLazyReference {
216244
return value?.takeUnretainedValue()
217245
}
218246
}
247+
248+
/// A reference type holding a lazily initializable atomic
249+
/// strong reference, with automatic memory management.
250+
///
251+
/// These values can be set (initialized) exactly once, but read many
252+
/// times.
253+
@_fixed_layout
254+
public class ManagedAtomicLazyReference<Instance: AnyObject> {
255+
/// The value logically stored in an atomic lazy reference value.
256+
public typealias Value = Instance?
257+
258+
/// The actual lazily initialized reference value.
259+
@usableFromInline
260+
internal let _storage: AtomicLazyReference<Instance>
261+
262+
/// Initializes a new managed atomic lazy reference with a nil value.
263+
@inlinable
264+
public init() {
265+
_storage = AtomicLazyReference()
266+
}
267+
268+
deinit {
269+
}
270+
}
271+
272+
extension ManagedAtomicLazyReference: @unchecked Sendable
273+
where Instance: Sendable {}
274+
219275
extension ManagedAtomicLazyReference {
220276
/// Atomically initializes this reference if its current value is nil, then
221277
/// returns the initialized value. If this reference is already initialized,
@@ -242,27 +298,14 @@ extension ManagedAtomicLazyReference {
242298
///
243299
/// This operation uses acquiring-and-releasing memory ordering.
244300
public func storeIfNilThenLoad(_ desired: __owned Instance) -> Instance {
245-
let desiredUnmanaged = Unmanaged.passRetained(desired)
246-
let (exchanged, current) = _Rep.atomicCompareExchange(
247-
expected: nil,
248-
desired: desiredUnmanaged,
249-
at: _ptr,
250-
ordering: .acquiringAndReleasing)
251-
if !exchanged {
252-
// The reference has already been initialized. Balance the retain that
253-
// we performed on `desired`.
254-
desiredUnmanaged.release()
255-
return current!.takeUnretainedValue()
256-
}
257-
return desiredUnmanaged.takeUnretainedValue()
301+
_storage.storeIfNilThenLoad(desired)
258302
}
259303

260304
/// Atomically loads and returns the current value of this reference.
261305
///
262306
/// The load operation is performed with the memory ordering
263307
/// `AtomicLoadOrdering.acquiring`.
264308
public func load() -> Instance? {
265-
let value = _Rep.atomicLoad(at: _ptr, ordering: .acquiring)
266-
return value?.takeUnretainedValue()
309+
_storage.load()
267310
}
268311
}

0 commit comments

Comments
 (0)