ARM64: LDXR/STXR vs LDAXR/STLXR

arm, arm64, atomic

Solution

`OSAtomicAdd32Barrier()` exists for people that are using `OSAtomicAdd()` for something beyond just atomic increment. Specifically, they are implementing their own multi-processing synchronization primitives based on `OSAtomicAdd()`. For example, creating their own mutex library. `OSAtomicAdd32Barrier()` uses heavy barrier instructions to enforce memory ordering on both side of the atomic operation. This is not desirable in normal usage.

To summarize:

1) If you just want to increment an integer in a thread-safe way, use `OSAtomicAdd32()`

2) If you are stuck with a bunch of old code that foolishly assumes `OSAtomicAdd32()` can be used as an interprocessor memory ordering and speculation barrier, replace it with `OSAtomicAdd32Barrier()`

Problem

On iOS, there are two similar functions, `OSAtomicAdd32` and `OSAtomicAdd32Barrier`. I'm wondering when you would need the `Barrier` variant. Disassembled, they are: ``` _OSAtomicAdd32: ldxr w8, [x1] add w8, w8, w0 stxr w9, w8, [x1] cbnz w9, _OSAtomicAdd32 mov x0, x8 ret lr _OSAtomicAdd32Barrier: ldaxr w8, [x1] add w8, w8, w0 stlxr w9, w8, [x1] cbnz w9, _OSAtomicAdd32Barrier mov x0, x8 ret lr ``` In which scenarios would you need the Load-Acquire / Store-Release semantics of the latter? Can `LDXR`/`STXR` instructions be reordered? If they can, is it possible for an atomic update to be "lost" in the absence of a barrier? From what I've read, it doesn't seem like that can happen, and if true, then why would you need the `Barrier` variant? Perhaps only if you also happened to need a `DMB` for other purposes? Thanks!

Original source