AtomicRepresentable
A type that supports atomic operations through a separate atomic storage representation.
Declaration
protocol AtomicRepresentableOverview
Types that conform to the AtomicRepresentable protocol can be used as the Value type parameter with the Atomic type. Conformances that utilize existing atomic storage representations as their own representation will get the primitive atomic operations available on Atomic for free. Such operations include load, store, exchange, compareExchange, and weakCompareExchange.
Conforming to the AtomicRepresentable protocol
Conforming your own custom types allow them to be used in the Atomic type and get access to all of the primitive atomic operations explained above. There are two main ways to conform your type to AtomicRepresentable:
Using a predefined
RawRepresentableconformanceManually conforming to
AtomicRepresentable
If you custom type already conforms to RawRepresentable, then adding the AtomicRepresentable conformance may be really simple! If the RawValue associated type of your type is already itself an AtomicRepresentable, then all you need to do is add the conformance and you’re done!
enum TrafficLight: UInt8 {
case red
case yellow
case green
}
extension TrafficLight: AtomicRepresentable {}And that’s it! Here, we’re utilizing Swift’s automatic RawRepresentable conformance synthesis for enums by declaring our “raw value” to be a UInt8. By adding the AtomicRepresentable conformance, we automatically figure out how to do the conformance from the RawRepresentable implementation and do all of the necessary work for you. However, it is still possible to customize this behavior using the manual method explained below.
Defining your own AtomicRepresentable conformance is pretty simple. All you have to do is decide what atomic storage representation fits best for your type, and create the bidirectional relationship between the two.
// A point in an x-y coordinate system.
struct GridPoint {
var x: Int
var y: Int
}
extension GridPoint: AtomicRepresentable {
typealias AtomicRepresentation = WordPair.AtomicRepresentation
static func encodeAtomicRepresentation(
_ value: consuming GridPoint
) -> AtomicRepresentation {
let wordPair = WordPair(
first: UInt(bitPattern: value.x),
second: UInt(bitPattern: value.y)
)
return WordPair.encodeAtomicRepresentation(wordPair)
}
static func decodeAtomicRepresentation(
_ representation: consuming AtomicRepresentation
) -> GridPoint {
let wordPair = WordPair.decodeAtomicRepresentation(representation)
return GridPoint(
x: Int(bitPattern: wordPair.first),
y: Int(bitPattern: wordPair.second)
)
}
}Here, we’re going to select WordPair’s atomic storage representation as our own. This is very important because we only get the atomic operations like load and store if our representation is one of the fundamental storage representations. Luckily for us, WordPair does use one of these types as its storage type.
In addition to selecting what storage representation our type will use, we define two static functions that go from both our custom type to its representation and the representation back to our own type. Because our representation is the same as WordPair.AtomicRepresentation, we will actually go through WordPair’s AtomicRepresentable conformance to help define our own.
This is all you need to do to conform your custom type to the AtomicRepresentable protocol. From here, you can use this type in all of the primitive atomic operations like shown below:
func atomicGridPoint(_ gridPoint: Atomic<GridPoint>) {
let newGridPoint = GridPoint(x: 123, y: -456)
let oldGridPoint1 = gridPoint.load(ordering: .relaxed)
gridPoint.store(newGridPoint, ordering: .releasing)
let oldGridPoint2 = gridPoint.exchange(
desired: oldGridPoint1,
ordering: .acquiringAndReleasing
)
let (exchanged1, oldGridPoint2) = gridPoint.compareExchange(
expected: oldGridPoint1,
desired: newGridPoint,
ordering: .sequentiallyConsistent
)
let (exchanged2, oldGridPoint3) = gridPoint.weakCompareExchange(
expected: newGridPoint,
desired: oldGridPoint2,
ordering: .relaxed
)
}List of Fundamental Atomic Representations
When defining your own AtomicRepresentable conformance, it is critical that your custom type should choose from the following list of types as its own AtomicRepresentation:
UInt8.AtomicRepresentationUInt16.AtomicRepresentationUInt32.AtomicRepresentationUInt64.AtomicRepresentationUInt.AtomicRepresentationInt8.AtomicRepresentationInt16.AtomicRepresentationInt32.AtomicRepresentationInt64.AtomicRepresentationInt.AtomicRepresentationWordPair.AtomicRepresentation