What does __ATOMIC_RELAXED mean?
Asked Answered
B

2

10

In GCC atomic built-in I found that __atomic_exchange function does have a third parameter int memorder, which could take one of the values __ATOMIC_RELAXED, __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE, __ATOMIC_RELEASE, and __ATOMIC_ACQ_REL.

__ATOMIC_RELAXED: Implies no inter-thread ordering constraints.

I am not sure how can that happen, isn't this supposed to be atomic operation and therefore there will not be any inter-thread ordering ?

Biller answered 3/9, 2015 at 13:36 Comment(1)
Do you mean ordering instructions __atomic_exchange or ordering the load and store used by the function __atomic_exchange ?Biller
L
9

All these builtins correspond to new C11 standard atomicity features. Standard contains perfect explanation of memory_order_relaxed ordering model and some examples (refer to 7.17.3/14)

// Thread 1:
r1 = atomic_load_explicit(&y, memory_order_relaxed);
atomic_store_explicit(&x, r1, memory_order_relaxed);

// Thread 2:
r2 = atomic_load_explicit(&x, memory_order_relaxed);
atomic_store_explicit(&y, 42, memory_order_relaxed);

This code is allowed to produce r1 == 42 && r2 == 42. because store of y in thread 2 might reordered before load of r2.

Longley answered 4/9, 2015 at 13:19 Comment(0)
D
2

__ATOMIC_RELAXED means that the operation itself is atomic, without there being additional ordering constraints. In other words, the operation itself is atomic: another thread will only ever see the state before or after the operation, but never an intermediate state where the operation is partially executed. However the compiler, CPU, and memory system are free to reorder the operation with respect to other operations on other variables.

(shared = 2; tmp = shared; with a relaxed store and load can't run as tmp = shared; shared = 2;. RELAXED Operations on the same variable by the same thread are still ordered wrt. each other, but not wrt. anything else. See http://eel.is/c++draft/intro.races#19)


The gcc atomics are basically a copy of the C++11 spec, regardless of whether you use them in C or C++. Here is what the gcc atomics manual says (boldface emphasis on C++11 references is mine)

The following built-in functions approximately match the requirements for the C++11 memory model. They are all identified by being prefixed with ‘__atomic’ and most are overloaded so that they work with multiple types.

The description of each memory order is only meant to roughly illustrate the effects and is not a specification; see the C++11 memory model for precise semantics.

This explains the lack of detailed description of the memory orders. So, turning to the C++11 spec, we find the following detail for __ATOMIC_RELAXED:

Relaxed operation: there are no synchronization or ordering constraints imposed on other reads or writes, only this operation's atomicity is guaranteed (see Relaxed ordering below).

Dermatogen answered 10/11, 2023 at 21:56 Comment(5)
free to reorder the operation with respect to other operations. ... on other objects. For example, compilers and hardware need to make sure shared = 2; tmp = shared; loads 2 (or any later but not earlier value in the modification-order of shared if other threads are also modifying it). That requirement might seem obvious for basic sanity, but people have asked about so it's worth mentioning. eel.is/c++draft/intro.races#19Plebeian
@Peter Are you just saying that the compiler/HW can't reorder statements in a way that would break single-thread semantics? (That is true... but if we have to mention it, shouldn't we also mention that this applies to all statements in the program, not just the atomics primitives?)Dermatogen
It goes somewhat beyond not breaking single-threaded programs. For example as the standard points out, tmp1 = shared; tmp2 = shared; must load in that order, not the reverse. So if shared is monotonically increasing and doesn't wrap, it's guaranteed that tmp2 >= tmp1, even if those increments are being done by another thread or threads. (Even if it's another thread doing shared = local_counter++; in a loop with relaxed stores.) Every atomic object has its own modification-order which all threads can agree on, even in programs that use relaxed atomics.Plebeian
@Peter Oh OK now I get it. I think tmp1 = shared; tmp2 = shared; is a better example because it could be reordered without breaking single-threaded programs, and therefore the atomic is adding an ordering restriction that we wouldn't otherwise have. Can we just replace the example in the answer? (I still don't quite get the shared = 2; tmp = shared; example, as it doesn't seem to be a case where the atomic adds an ordering restriction)Dermatogen
Go ahead and edit, you're right that read-read coherence is a better and more illustrative example than write-read which seems obviously necessary even for single-threaded code to work. (And yet I'm pretty sure I remember at least one question about write-read, or maybe just something in comments, from someone taking an incomplete statement about relaxed at face value and not thinking through the consequences for single-threaded code.)Plebeian

© 2022 - 2024 — McMap. All rights reserved.