Skip to content

Commit e05cf1f

Browse files
committed
SE-0258: property wrappers, revise the Atomic example.
Use 'class' rather than 'struct' because there is no way to make a struct property atomic from within the wrapper. TSAN will correctly report an error if you use a struct Atomic wrapper. Some developers have been baffled by the TSAN failure, which actually correctly identifies the bug. Fixes rdar://79173755 (TSAN violation on standard Atomic property wrappers)
1 parent be68670 commit e05cf1f

File tree

1 file changed

+18
-6
lines changed

1 file changed

+18
-6
lines changed

proposals/0258-property-wrappers.md

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -451,12 +451,12 @@ This implementation would address the problem detailed in
451451

452452
### `Atomic`
453453

454-
Support for atomic operations (load, store, increment/decrement, compare-and-exchange) is a commonly-requested Swift feature. While the implementation details for such a feature would involve compiler and standard library magic, the interface itself can be nicely expressed as a property wrapper type:
454+
Support for atomic operations (load, store, increment/decrement, compare-and-exchange) is a commonly-requested Swift feature. While the implementation details for such a feature would involve compiler and standard library magic, the interface itself can be expressed as a property wrapper reference type:
455455

456456

457457
```swift
458458
@propertyWrapper
459-
struct Atomic<Value> {
459+
class Atomic<Value> {
460460
private var _value: Value
461461

462462
init(wrappedValue: Value) {
@@ -469,13 +469,13 @@ struct Atomic<Value> {
469469
}
470470

471471
func load(order: MemoryOrder = .relaxed) { ... }
472-
mutating func store(newValue: Value, order: MemoryOrder = .relaxed) { ... }
473-
mutating func increment() { ... }
474-
mutating func decrement() { ... }
472+
func store(newValue: Value, order: MemoryOrder = .relaxed) { ... }
473+
func increment() { ... }
474+
func decrement() { ... }
475475
}
476476

477477
extension Atomic where Value: Equatable {
478-
mutating func compareAndExchange(oldValue: Value, newValue: Value, order: MemoryOrder = .relaxed) -> Bool {
478+
func compareAndExchange(oldValue: Value, newValue: Value, order: MemoryOrder = .relaxed) -> Bool {
479479
...
480480
}
481481
}
@@ -485,6 +485,18 @@ enum MemoryOrder {
485485
};
486486
```
487487

488+
The Atomic property wrapper is class rather than a struct because the
489+
memory guarded by synchronization primitives must be independent from
490+
the wrapper. The wrapper's value will be loaded before the call to
491+
`wrappedValue.getter` and written back after the call to
492+
`wrappedValue.setter`. Therefore, synchronization within the wrapper
493+
cannot provide atomic access to its own value. However, when the
494+
wrapped value is a separate stored property within the wrapper object,
495+
then it is accessed independently, only after calling the
496+
wrappedValue's getter and setter. Note that a class type property
497+
wrapper gives the wrapped value reference semantics. All copies of the
498+
parent object will share the same atomic value.
499+
488500
Here are some simple uses of `Atomic`. With atomic types, it's fairly common
489501
to weave lower-level atomic operations (`increment`, `load`, `compareAndExchange`) where we need specific semantics (such as memory ordering) with simple queries, so both the property and the synthesized storage property are used often:
490502

0 commit comments

Comments
 (0)