Skip to content

Commit

Permalink
SE-0258: property wrappers, revise the Atomic example.
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
atrick committed Jun 11, 2021
1 parent be68670 commit e05cf1f
Showing 1 changed file with 18 additions and 6 deletions.
24 changes: 18 additions & 6 deletions proposals/0258-property-wrappers.md
Expand Up @@ -451,12 +451,12 @@ This implementation would address the problem detailed in

### `Atomic`

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:
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:


```swift
@propertyWrapper
struct Atomic<Value> {
class Atomic<Value> {
private var _value: Value

init(wrappedValue: Value) {
Expand All @@ -469,13 +469,13 @@ struct Atomic<Value> {
}

func load(order: MemoryOrder = .relaxed) { ... }
mutating func store(newValue: Value, order: MemoryOrder = .relaxed) { ... }
mutating func increment() { ... }
mutating func decrement() { ... }
func store(newValue: Value, order: MemoryOrder = .relaxed) { ... }
func increment() { ... }
func decrement() { ... }
}

extension Atomic where Value: Equatable {
mutating func compareAndExchange(oldValue: Value, newValue: Value, order: MemoryOrder = .relaxed) -> Bool {
func compareAndExchange(oldValue: Value, newValue: Value, order: MemoryOrder = .relaxed) -> Bool {
...
}
}
Expand All @@ -485,6 +485,18 @@ enum MemoryOrder {
};
```

The Atomic property wrapper is class rather than a struct because the
memory guarded by synchronization primitives must be independent from
the wrapper. The wrapper's value will be loaded before the call to
`wrappedValue.getter` and written back after the call to
`wrappedValue.setter`. Therefore, synchronization within the wrapper
cannot provide atomic access to its own value. However, when the
wrapped value is a separate stored property within the wrapper object,
then it is accessed independently, only after calling the
wrappedValue's getter and setter. Note that a class type property
wrapper gives the wrapped value reference semantics. All copies of the
parent object will share the same atomic value.

Here are some simple uses of `Atomic`. With atomic types, it's fairly common
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:

Expand Down

0 comments on commit e05cf1f

Please sign in to comment.