Skip to content

Commit f104af1

Browse files
committed
ManagedAtomic, ManagedAtomicLazyReference: Only forward to noncopyable atomics when they exist
1 parent efe6d54 commit f104af1

File tree

2 files changed

+128
-0
lines changed

2 files changed

+128
-0
lines changed

Sources/Atomics/AtomicLazyReference.swift

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
#if compiler(>=5.9) && $RawLayout
1314
/// A lazily initializable atomic strong reference.
1415
///
1516
/// These values can be set (initialized) exactly once, but read many
@@ -85,6 +86,7 @@ extension AtomicLazyReference {
8586
return value?.takeUnretainedValue()
8687
}
8788
}
89+
#endif
8890

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

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

271+
deinit {}
272+
#else
273+
@usableFromInline
274+
internal typealias _Rep = Optional<Unmanaged<Instance>>.AtomicRepresentation
275+
276+
/// The atomic representation of the value stored inside.
277+
///
278+
/// Warning: This ivar must only ever be accessed via `_ptr` after
279+
/// its initialization.
280+
@usableFromInline
281+
internal let _storage: _Rep
282+
283+
/// Initializes a new managed atomic lazy reference with a nil value.
284+
@inlinable
285+
public init() {
286+
_storage = _Rep(nil)
287+
}
288+
268289
deinit {
290+
if let unmanaged = _ptr.pointee.dispose() {
291+
unmanaged.release()
292+
}
293+
}
294+
295+
@_alwaysEmitIntoClient @inline(__always)
296+
internal var _ptr: UnsafeMutablePointer<_Rep> {
297+
_getUnsafePointerToStoredProperties(self).assumingMemoryBound(to: _Rep.self)
269298
}
299+
#endif
270300
}
271301

302+
272303
extension ManagedAtomicLazyReference: @unchecked Sendable
273304
where Instance: Sendable {}
274305

@@ -298,14 +329,36 @@ extension ManagedAtomicLazyReference {
298329
///
299330
/// This operation uses acquiring-and-releasing memory ordering.
300331
public func storeIfNilThenLoad(_ desired: __owned Instance) -> Instance {
332+
#if compiler(>=5.9) && $RawLayout
301333
_storage.storeIfNilThenLoad(desired)
334+
#else
335+
let desiredUnmanaged = Unmanaged.passRetained(desired)
336+
let (exchanged, current) = _Rep.atomicCompareExchange(
337+
expected: nil,
338+
desired: desiredUnmanaged,
339+
at: _ptr,
340+
ordering: .acquiringAndReleasing)
341+
if !exchanged {
342+
// The reference has already been initialized. Balance the retain that
343+
// we performed on `desired`.
344+
desiredUnmanaged.release()
345+
return current!.takeUnretainedValue()
346+
}
347+
return desiredUnmanaged.takeUnretainedValue()
348+
#endif
302349
}
303350

304351
/// Atomically loads and returns the current value of this reference.
305352
///
306353
/// The load operation is performed with the memory ordering
307354
/// `AtomicLoadOrdering.acquiring`.
308355
public func load() -> Instance? {
356+
#if compiler(>=5.9) && $RawLayout
309357
_storage.load()
358+
#else
359+
let value = _Rep.atomicLoad(at: _ptr, ordering: .acquiring)
360+
return value?.takeUnretainedValue()
361+
#endif
310362
}
311363
}
364+

Sources/Atomics/ManagedAtomic.swift

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ where Value.AtomicRepresentation.Value == Value {
1919
// (We'd need one set of implementations for the type equality condition, and
2020
// another for `Value: AtomicReference`.)
2121

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

@@ -28,6 +29,34 @@ where Value.AtomicRepresentation.Value == Value {
2829
public init(_ value: Value) {
2930
_storage = Atomic(value)
3031
}
32+
#else
33+
@usableFromInline
34+
internal typealias _Storage = Value.AtomicRepresentation
35+
36+
/// The atomic representation of the value stored inside.
37+
///
38+
/// Warning: This ivar must only ever be accessed via `_ptr` after
39+
/// its initialization.
40+
@usableFromInline
41+
internal var _storage: _Storage
42+
43+
/// Initialize a new managed atomic instance holding the specified initial
44+
/// value.
45+
@inline(__always) @_alwaysEmitIntoClient
46+
public init(_ value: Value) {
47+
_storage = _Storage(value)
48+
}
49+
50+
deinit {
51+
_ = _ptr.pointee.dispose()
52+
}
53+
54+
@_alwaysEmitIntoClient @inline(__always)
55+
internal var _ptr: UnsafeMutablePointer<_Storage> {
56+
_getUnsafePointerToStoredProperties(self)
57+
.assumingMemoryBound(to: _Storage.self)
58+
}
59+
#endif
3160
}
3261

3362
extension ManagedAtomic: @unchecked Sendable where Value: Sendable {}
@@ -43,7 +72,11 @@ extension ManagedAtomic {
4372
public func load(
4473
ordering: AtomicLoadOrdering
4574
) -> Value {
75+
#if compiler(>=5.9) && $RawLayout
4676
_storage.load(ordering: ordering)
77+
#else
78+
_Storage.atomicLoad(at: _ptr, ordering: ordering)
79+
#endif
4780
}
4881

4982
/// Atomically sets the current value to `desired`, applying the specified
@@ -57,7 +90,11 @@ extension ManagedAtomic {
5790
_ desired: __owned Value,
5891
ordering: AtomicStoreOrdering
5992
) {
93+
#if compiler(>=5.9) && $RawLayout
6094
_storage.store(desired, ordering: ordering)
95+
#else
96+
_Storage.atomicStore(desired, at: _ptr, ordering: ordering)
97+
#endif
6198
}
6299

63100
/// Atomically sets the current value to `desired` and returns the original
@@ -72,7 +109,11 @@ extension ManagedAtomic {
72109
_ desired: __owned Value,
73110
ordering: AtomicUpdateOrdering
74111
) -> Value {
112+
#if compiler(>=5.9) && $RawLayout
75113
_storage.exchange(desired, ordering: ordering)
114+
#else
115+
_Storage.atomicExchange(desired, at: _ptr, ordering: ordering)
116+
#endif
76117
}
77118

78119
/// Perform an atomic compare and exchange operation on the current value,
@@ -105,8 +146,16 @@ extension ManagedAtomic {
105146
desired: __owned Value,
106147
ordering: AtomicUpdateOrdering
107148
) -> (exchanged: Bool, original: Value) {
149+
#if compiler(>=5.9) && $RawLayout
108150
_storage.compareExchange(
109151
expected: expected, desired: desired, ordering: ordering)
152+
#else
153+
_Storage.atomicCompareExchange(
154+
expected: expected,
155+
desired: desired,
156+
at: _ptr,
157+
ordering: ordering)
158+
#endif
110159
}
111160

112161
/// Perform an atomic compare and exchange operation on the current value,
@@ -147,11 +196,20 @@ extension ManagedAtomic {
147196
successOrdering: AtomicUpdateOrdering,
148197
failureOrdering: AtomicLoadOrdering
149198
) -> (exchanged: Bool, original: Value) {
199+
#if compiler(>=5.9) && $RawLayout
150200
_storage.compareExchange(
151201
expected: expected,
152202
desired: desired,
153203
successOrdering: successOrdering,
154204
failureOrdering: failureOrdering)
205+
#else
206+
_Storage.atomicCompareExchange(
207+
expected: expected,
208+
desired: desired,
209+
at: _ptr,
210+
successOrdering: successOrdering,
211+
failureOrdering: failureOrdering)
212+
#endif
155213
}
156214

157215
/// Perform an atomic weak compare and exchange operation on the current
@@ -187,10 +245,18 @@ extension ManagedAtomic {
187245
desired: __owned Value,
188246
ordering: AtomicUpdateOrdering
189247
) -> (exchanged: Bool, original: Value) {
248+
#if compiler(>=5.9) && $RawLayout
190249
_storage.weakCompareExchange(
191250
expected: expected,
192251
desired: desired,
193252
ordering: ordering)
253+
#else
254+
_Storage.atomicWeakCompareExchange(
255+
expected: expected,
256+
desired: desired,
257+
at: _ptr,
258+
ordering: ordering)
259+
#endif
194260
}
195261

196262
/// Perform an atomic weak compare and exchange operation on the current
@@ -234,10 +300,19 @@ extension ManagedAtomic {
234300
successOrdering: AtomicUpdateOrdering,
235301
failureOrdering: AtomicLoadOrdering
236302
) -> (exchanged: Bool, original: Value) {
303+
#if compiler(>=5.9) && $RawLayout
237304
_storage.weakCompareExchange(
238305
expected: expected,
239306
desired: desired,
240307
successOrdering: successOrdering,
241308
failureOrdering: failureOrdering)
309+
#else
310+
_Storage.atomicWeakCompareExchange(
311+
expected: expected,
312+
desired: desired,
313+
at: _ptr,
314+
successOrdering: successOrdering,
315+
failureOrdering: failureOrdering)
316+
#endif
242317
}
243318
}

0 commit comments

Comments
 (0)