Well, how much god damn optimization is a 100% conforming compiler
allowed to do on my std::atomics?
Can they be used as if they are "volatile", or not, damn seems not...?
If I make three atomic stores, I better get those three atomic stores in
my generated ASM or something, damn it!
But, I am not 100% sure this is guaranteed unless another thread is in there. I know every std::atomic impl I have seen is laced with volatile somewhere. But, is it required to be? Or, well, shit.
Well, damn. It seems that std::atomic might not be sufficient, and
volatile needs to be used.
Look at the following code:
Is volatile ct_atomic_word aword = { 0 }; even legal? _______________________________
#include <iostream>
#include <cstdint>
#include <atomic>
// humm... For certian use cases,
// volatile might be needed here, say
// for embeded work, controlling something,
// ect...
// or is std::atomic guaranteed to work
typedef std::uint32_t ct_word;
typedef std::atomic<ct_word> ct_atomic_word;
int main()
{
-a-a-a ct_atomic_word aword = { 0 };
-a-a-a // na... but, well, for tricky hardware work?
-a-a-a // oh yeah. sucks. need that volatile.
-a-a-a // is this even okay? Used with a std::atomic?
-a-a-a //volatile ct_atomic_word aword = { 0 };
-a-a-a {
-a-a-a-a-a-a-a aword.store(1, std::memory_order_relaxed);
-a-a-a-a-a-a-a aword.store(2, std::memory_order_relaxed);
-a-a-a-a-a-a-a aword.store(3, std::memory_order_relaxed);
-a-a-a }
-a-a-a std::cout << "aword = " << aword.load(std::memory_order_relaxed) << std::endl;
-a-a-a return 0;
}
_______________________________
On 11/05/2026 06:51, Chris M. Thomasson wrote:
Well, damn. It seems that std::atomic might not be sufficient, and
volatile needs to be used.
I very much hope to be corrected if someone thinks my reasoning below is >incorrect - I am not an expert here by any means.
On Mon, 11 May 2026 08:25:55 +0200
David Brown <david.brown@hesbynett.no> gabbled:
On 11/05/2026 06:51, Chris M. Thomasson wrote:
Well, damn. It seems that std::atomic might not be sufficient, and
volatile needs to be used.
I very much hope to be corrected if someone thinks my reasoning below
is incorrect - I am not an expert here by any means.
Whats the point of std::atomic anyway? Just how often does a thread
critical
section just require the test and set of a single value? And if you do
want to do that posix semaphores have been a thing since the early 90s
and SysV ones since at least the 80s if not earlier.
Well, how much god damn optimization is a 100% conforming compiler
allowed to do on my std::atomics?
Can they be used as if they are "volatile", or not, damn seems not...?
If I make three atomic stores, I better get those three atomic stores in
my generated ASM or something, damn it!
But, I am not 100% sure this is guaranteed unless another thread is in there. I know every std::atomic impl I have seen is laced with volatile somewhere. But, is it required to be? Or, well, shit.
Well, damn. It seems that std::atomic might not be sufficient, and
volatile needs to be used.
Look at the following code:
Is volatile ct_atomic_word aword = { 0 }; even legal? _______________________________
#include <iostream>
#include <cstdint>
#include <atomic>
// humm... For certian use cases,
// volatile might be needed here, say
// for embeded work, controlling something,
// ect...
// or is std::atomic guaranteed to work
typedef std::uint32_t ct_word;
typedef std::atomic<ct_word> ct_atomic_word;
int main()
{
-a-a-a ct_atomic_word aword = { 0 };
-a-a-a // na... but, well, for tricky hardware work?
-a-a-a // oh yeah. sucks. need that volatile.
-a-a-a // is this even okay? Used with a std::atomic?
-a-a-a //volatile ct_atomic_word aword = { 0 };
-a-a-a {
-a-a-a-a-a-a-a aword.store(1, std::memory_order_relaxed);
-a-a-a-a-a-a-a aword.store(2, std::memory_order_relaxed);
-a-a-a-a-a-a-a aword.store(3, std::memory_order_relaxed);
-a-a-a }
-a-a-a std::cout << "aword = " << aword.load(std::memory_order_relaxed) << std::endl;
-a-a-a return 0;
}
_______________________________
As I understand it, volatile and atomic serve different purposes
(although they could use the same mechanism).
Volatile must be used to indicate that the contents of a location may
change by other means than the program code, e.g., a I/O register of a device. The consequence is that reading the location may not be removed
by optimisation. It usually also has the effect that writing the
location is not postponed.
Atomic must be used to make sure that the access to a location that
needs multiple steps is not interrupted by other threads that access the same location. If the compiler is able to conclude that no such other
thread can do this (e.g., because there is only one thread in the
program) reading the location could be optimised away if the contents is already known, or writing could be postponed if another write happens somewhat later, because it has no observable effects
On 11/05/2026 11:46, Fred. Zwarts wrote:
As I understand it, volatile and atomic serve different purposes
(although they could use the same mechanism).
Yes.
Volatile must be used to indicate that the contents of a location may
change by other means than the program code, e.g., a I/O register of a
device. The consequence is that reading the location may not be removed
by optimisation. It usually also has the effect that writing the
location is not postponed.
That's mostly right. Volatile accesses are reads or writes through
volatile lvalues (such as either accessing an object declared
"volatile", or use a pointer-to-volatile). Something other than the
code may read or change the volatile object, or there can be other
effects triggered by the read or write. (For hardware registers, you
might, for example, have an event flag register that is cleared when it
is read.)
The other critical point about volatile accesses is that they must
follow program order - but only as viewed by the current thread.
Normally reads and writes to memory can be re-ordered as convenient for
the compiler, and combined or eliminated if doing so does not affect the meaning of the program. Volatile accesses cannot be re-ordered with
respect to other volatile accesses - but they /can/ be re-ordered with respect to non-volatile accesses. And other threads may see the
volatile accesses in a different order.
Atomic must be used to make sure that the access to a location that
needs multiple steps is not interrupted by other threads that access the
same location. If the compiler is able to conclude that no such other
thread can do this (e.g., because there is only one thread in the
program) reading the location could be optimised away if the contents is
already known, or writing could be postponed if another write happens
somewhat later, because it has no observable effects
The first major point of an atomic access is that any other thread will
see the world before the access, or the world after the access - it will never see the access half-done. So if you have atomic fetch-and-add of
one happening in two threads on the same address, each thread will see
the atomic variable either before the other thread's operation starts,
or after it has finished - so you are guaranteed that both addition operations occur.
The second major point of atomics is that they can have specified memory orderings that affect how other threads see potential re-ordering of
memory operations - including non-atomic operations.
Note that processors have a set of sizes for which their load and store operations are atomic anyway - typically on modern processors all 64-bit
or smaller loads and stores are atomic without any special treatment.
(On an 8-bit processor, on the other hand, a 32-bit load or store would
not be atomic without extra work.) So if you have a 32-bit atomic load
or store with memory order "relaxed" (i.e., no requirements for synchronisation with other threads), then I don't think there is any difference from just a normal 32-bit load or store.
On 11/05/2026 10:28, boltar@caprica.universe wrote:
On Mon, 11 May 2026 08:25:55 +0200
David Brown <david.brown@hesbynett.no> gabbled:
On 11/05/2026 06:51, Chris M. Thomasson wrote:
Well, damn. It seems that std::atomic might not be sufficient, and
volatile needs to be used.
I very much hope to be corrected if someone thinks my reasoning below
is incorrect - I am not an expert here by any means.
Whats the point of std::atomic anyway? Just how often does a thread
critical
section just require the test and set of a single value? And if you do
want to do that posix semaphores have been a thing since the early 90s
and SysV ones since at least the 80s if not earlier.
Atomics are for shared information /without/ needing semaphores,
critical sections or other more expensive operations. Read up about >non-blocking or lock-free algorithms.
On Mon, 11 May 2026 11:02:19 +0200
David Brown <david.brown@hesbynett.no> gabbled:
On 11/05/2026 10:28, boltar@caprica.universe wrote:
On Mon, 11 May 2026 08:25:55 +0200
David Brown <david.brown@hesbynett.no> gabbled:
On 11/05/2026 06:51, Chris M. Thomasson wrote:
Well, damn. It seems that std::atomic might not be sufficient, and
volatile needs to be used.
I very much hope to be corrected if someone thinks my reasoning
below is incorrect - I am not an expert here by any means.
Whats the point of std::atomic anyway? Just how often does a thread
critical
section just require the test and set of a single value? And if you do
want to do that posix semaphores have been a thing since the early 90s
and SysV ones since at least the 80s if not earlier.
Atomics are for shared information /without/ needing semaphores,
The underlying implementation will be the same.
critical sections or other more expensive operations.-a Read up about
non-blocking or lock-free algorithms.
An atomic/semaphore is just a lock on a single value by another name.
Also IIRC the "+=" operation isn't atomic with std::atomic which is a bit
of a joke.
An atomic/semaphore is just a lock on a single value by another name.It is a atomic. But the memory-ordering is fixed to memory_order_seq_cst.
Also IIRC the "+=" operation isn't atomic with std::atomic which is
a bit of a joke.
In particular, semaphores have blocking support - you can block until a semaphore is available.-a Atomics do not block, and are critical to non- blocking algorithms.
On 11/05/2026 10:28, boltar@caprica.universe wrote:
On Mon, 11 May 2026 08:25:55 +0200
David Brown <david.brown@hesbynett.no> gabbled:
On 11/05/2026 06:51, Chris M. Thomasson wrote:
Well, damn. It seems that std::atomic might not be sufficient, and
volatile needs to be used.
I very much hope to be corrected if someone thinks my reasoning below
is incorrect - I am not an expert here by any means.
Whats the point of std::atomic anyway? Just how often does a thread
critical
section just require the test and set of a single value? And if you do
want to do that posix semaphores have been a thing since the early 90s
and SysV ones since at least the 80s if not earlier.
Atomics are for shared information /without/ needing semaphores,
critical sections or other more expensive operations. Read up about >non-blocking or lock-free algorithms.
In article <10ts5ur$updt$2@dont-email.me>,
David Brown <david.brown@hesbynett.no> wrote:
On 11/05/2026 10:28, boltar@caprica.universe wrote:
On Mon, 11 May 2026 08:25:55 +0200
David Brown <david.brown@hesbynett.no> gabbled:
On 11/05/2026 06:51, Chris M. Thomasson wrote:
Well, damn. It seems that std::atomic might not be sufficient, and
volatile needs to be used.
I very much hope to be corrected if someone thinks my reasoning below
is incorrect - I am not an expert here by any means.
Whats the point of std::atomic anyway? Just how often does a thread
critical
section just require the test and set of a single value? And if you do
want to do that posix semaphores have been a thing since the early 90s
and SysV ones since at least the 80s if not earlier.
Atomics are for shared information /without/ needing semaphores,
critical sections or other more expensive operations. Read up about
non-blocking or lock-free algorithms.
And also for implementing such things.
Indeed.-a Both volatiles and atomics (or volatile atomics) are useful in
the implementation of semaphores and other kinds of synchronisation, locking, and concurrency types. ...
On 11/05/2026 12:59, boltar@caprica.universe wrote:
On Mon, 11 May 2026 11:02:19 +0200
The underlying implementation will be the same.
<https://cppreference.com/cpp/thread#Semaphores> ><https://cppreference.com/cpp/atomic/atomic>
I don't know how you can see them as being similar, unless by "the >underlying mechanism" you mean memory accesses along with
processor-specific features like bus lock prefixes, >load-locked/store-conditional sequences, CAS operations, memory barrier >instructions, etc.
In particular, semaphores have blocking support - you can block until a >semaphore is available. Atomics do not block, and are critical to >non-blocking algorithms.
critical sections or other more expensive operations.-a Read up about
non-blocking or lock-free algorithms.
An atomic/semaphore is just a lock on a single value by another name.
Neither of them are locks.
Also IIRC the "+=" operation isn't atomic with std::atomic which is a bit
of a joke.
The operator "+=" on std::atomic integer and pointer types is defined as >"fetch_add(arg) + arg" - the operation on the atomic variable is atomic.
On 11/05/2026 12:59, boltar@caprica.universe wrote:
Atomics are for shared information /without/ needing semaphores,
The underlying implementation will be the same.
Op 11.mei.2026 om 06:51 schreef Chris M. Thomasson:
As I understand it, volatile and atomic serve different purposes
(although they could use the same mechanism).
"Fred. Zwarts" <F.Zwarts@HetNet.nl> writes:
Op 11.mei.2026 om 06:51 schreef Chris M. Thomasson:
As I understand it, volatile and atomic serve different purposes
(although they could use the same mechanism).
volatile restricts the compiler from making certain
optimizations.
atomics are implemented by the hardware.
On Mon, 11 May 2026 11:02:19 +0200
David Brown <david.brown@hesbynett.no> gabbled:
On 11/05/2026 10:28, boltar@caprica.universe wrote:
On Mon, 11 May 2026 08:25:55 +0200
David Brown <david.brown@hesbynett.no> gabbled:
On 11/05/2026 06:51, Chris M. Thomasson wrote:
Well, damn. It seems that std::atomic might not be sufficient, and
volatile needs to be used.
I very much hope to be corrected if someone thinks my reasoning
below is incorrect - I am not an expert here by any means.
Whats the point of std::atomic anyway? Just how often does a thread
critical
section just require the test and set of a single value? And if you do
want to do that posix semaphores have been a thing since the early 90s
and SysV ones since at least the 80s if not earlier.
Atomics are for shared information /without/ needing semaphores,
The underlying implementation will be the same.
critical sections or other more expensive operations.-a Read up about
non-blocking or lock-free algorithms.
An atomic/semaphore is just a lock on a single value by another name.
Also IIRC the "+=" operation isn't atomic with std::atomic which is a bit
of a joke.
Am 11.05.2026 um 15:18 schrieb David Brown:
Indeed.-a Both volatiles and atomics (or volatile atomics) are useful
in the implementation of semaphores and other kinds of
synchronisation, locking, and concurrency types. ...
volatile doesn't make sense for that purpose.
Am 11.05.2026 um 13:20 schrieb David Brown:
In particular, semaphores have blocking support - you can block until
a semaphore is available.-a Atomics do not block, and are critical to
non- blocking algorithms.
You can have a futex'd ::wait() on an atomic.
David Brown <david.brown@hesbynett.no> writes:
On 11/05/2026 12:59, boltar@caprica.universe wrote:
Atomics are for shared information /without/ needing semaphores,
The underlying implementation will be the same.
That depends entirely on the instruction set of the target
processor. Most of the modern architectures include
instructions to atomically modify a location in memory
(e.g. Fetch-and-add). This is far more efficient
than attempting to acquire a spinlock or posix mutex
simply to update a single location in memory,
particularly when there are a significant number
of contenders (e.g. on a 128-core CPU).
This is essentially free in systems with
processor memory caching, since only one
processor (hardware thread) can own a cache line for write
access at any point in time. The processor
simply sends the atomic op directly to the cache
(or if caches are bypassed, the memory controller)
for execution.
Those operations have been extended such that
modern hardware supports that level of atomicity
across PCI express and other interconnect
technologies.
The advantage of the cache coherency algorithm
efficiency vs LL/SC or LDEX/STREX is very signficiant.
ARM, for example, has atomic:
- load and add (LDADD)
- load and exclusive or (LDEOR)
- load and clear bit (LDCLR)
- load and set bit (LDSET)
- load and return minimum value
- load and return maximum value
On Mon, 11 May 2026 13:20:17 +0200
David Brown <david.brown@hesbynett.no> gabbled:
On 11/05/2026 12:59, boltar@caprica.universe wrote:
On Mon, 11 May 2026 11:02:19 +0200
The underlying implementation will be the same.
<https://cppreference.com/cpp/thread#Semaphores>
<https://cppreference.com/cpp/atomic/atomic>
I don't know how you can see them as being similar, unless by "the
underlying mechanism" you mean memory accesses along with processor-
specific features like bus lock prefixes, load-locked/store-
conditional sequences, CAS operations, memory barrier instructions, etc.
Nice cut and paste. Yes, I'm talking about the CPU level.
In particular, semaphores have blocking support - you can block until
a semaphore is available.-a Atomics do not block, and are critical to
non-blocking algorithms.
And? Semaphores don't have to block.
critical sections or other more expensive operations.-a Read up about >>>> non-blocking or lock-free algorithms.
An atomic/semaphore is just a lock on a single value by another name.
Neither of them are locks.
They're locks in the sense that nothing else can touch the value while the current thread is updating it. So if another thread tries it'll be blocked. You can call it what you like but locking seems a good enough word to me.
Also IIRC the "+=" operation isn't atomic with std::atomic which is a
bit
of a joke.
The operator "+=" on std::atomic integer and pointer types is defined
as "fetch_add(arg) + arg" - the operation on the atomic variable is
atomic.
Its not atomic at the code level so its effectively useless.
On 11/05/2026 17:08, boltar@caprica.universe wrote:
On Mon, 11 May 2026 13:20:17 +0200
David Brown <david.brown@hesbynett.no> gabbled:
On 11/05/2026 12:59, boltar@caprica.universe wrote:
On Mon, 11 May 2026 11:02:19 +0200
The underlying implementation will be the same.
<https://cppreference.com/cpp/thread#Semaphores>
<https://cppreference.com/cpp/atomic/atomic>
I don't know how you can see them as being similar, unless by "the
underlying mechanism" you mean memory accesses along with processor-
specific features like bus lock prefixes, load-locked/store-
conditional sequences, CAS operations, memory barrier instructions, etc.
Nice cut and paste. Yes, I'm talking about the CPU level.
In particular, semaphores have blocking support - you can block until
a semaphore is available.-a Atomics do not block, and are critical to
non-blocking algorithms.
And? Semaphores don't have to block.
Of course you don't /have/ to block on a semaphore, but there's not much
use of them if you don't.-a You might as well just use an atomic counter, which is much cheaper.
You can't use an atomic object for blocking.-a C++ has a nice interface
that appears to do so, but it's actually using a futex or similar
mechanism for the blocking.
Remember, an atomic int takes 4 bytes of
space (on most platforms), just like an int, and a relaxed read or write
is just a normal load or store - something that can block needs queues
of blocked threads and OS calls.-a They are different worlds.
https://en.cppreference.com/cpp/atomic/atomic/is_lock_free
https://en.cppreference.com/cpp/atomic/atomic/is_always_lock_free
https://en.cppreference.com/cpp/atomic/atomic_is_lock_free
The _fun_ part:
Expands to an integer constant expression with value
0 for the built-in atomic types that are never lock-free,
1 for the built-in atomic types that are sometimes lock-free,
2 for the built-in atomic types that are always lock-free.
David Brown wrote this screed in ALL-CAPS:[...
On 11/05/2026 11:46, Fred. Zwarts wrote:
I thought this was a newsgroup about C++.
< ducks and runs :-) >
On 5/11/2026 12:48 PM, Chris M. Thomasson wrote:
[redacted]
https://en.cppreference.com/cpp/atomic/atomic/is_lock_freeWould have been nice to have:
https://en.cppreference.com/cpp/atomic/atomic/is_always_lock_free
https://en.cppreference.com/cpp/atomic/atomic_is_lock_free
The _fun_ part:
Expands to an integer constant expression with value
0 for the built-in atomic types that are never lock-free,
1 for the built-in atomic types that are sometimes lock-free,
2 for the built-in atomic types that are always lock-free.
-1 = sometimes
-a0 = never
-a1 = always
So that the spaceship operator could be used.
| Sysop: | Amessyroom |
|---|---|
| Location: | Fayetteville, NC |
| Users: | 65 |
| Nodes: | 6 (0 / 6) |
| Uptime: | 08:06:22 |
| Calls: | 862 |
| Files: | 1,311 |
| D/L today: |
1 files (1,366K bytes) |
| Messages: | 264,936 |