Std::atomic lock-freedom

In another thread t0m was pointing to std::atomic_flag as a guaranteed lock-free atomic variable.

However, from what I’ve read, I suspect that, in practice, std::atomic<int> and std::atomic<float> store and load might be compiled as lock-free for all “ordinary” PC/Mac computers.

In other words has anybody some statistics on the result of is_lock_free() on std::atomic<int> and std::atomic<float> variables?

I’m fully aware of the risk of using methods that only work “in practice” but when dealing with code written by others, sometimes it is hard to justify a big investment to fix something that is already working.

I found this interesting thread on stackoverflow:

Anybody has a good read on this topic with more context?

I don’t know any good article about this. But here is what I know:

AFAIK all primitive 32bit data types like float and int are lock-free on x64 systems. But there is no guarantee that an atomic struct that contains two floats or more data is lock-free. In this case, you can use is_lock_free() to test this.

Don’t access atomic types in sample-based DSP algorithms directly. Store them into a local variable first to make it possible for the compiler to optimize the code.

You most likely don’t run into problems when you are not using atomics for shared primitive data types like floats or ints at a first sight.
Nonetheless, you should use them to avoid subtle hard to reproduce and time-consuming bugs:

1 Like

Thanks for the link to the other thread kunz

I understand that there is absolutely no guarantee for struct’s but I’ve fixed the format of my initial post to focus my question on the int and float types.

For non atomic int’s and float’s I read about the possibility of one thread running in a CPU core storing the value in the cache, and another thread running in another CPU core reading from its own cache without knowing about the other CPU’s cache. Multi-core CPU’s and software are becoming more ubiquitous nowadays so I’m afraid that it can be dangerous to rely on what we have seen in practice to work so far. Particularly, those are edge cases that can be very hard to notice in real life, specially when dealing with music but can get your software to play the wrong note in a bad day (although we could argue that there is not such a thing like a “wrong” note if it is played with attitude :wink: )

1 Like

Recently i read a paper about the OOTA/RFUB problem for memory_order_relaxed atomic variables. Nothing related directly to your question, but a clear warning about how concurrency can be a nightmare if you don’t stick to good practices and if you go in the UB world.

YouTube / < A Relaxed Guide to memory_order_relaxed - Paul E. McKenney & Hans Boehm - CppCon 2020 - YouTube >
< CppCon 2016: Hans Boehm “Using weakly ordered C++ atomics correctly" - YouTube >

1 Like

Very interesting too. It might not be related to how I formulated my question but you have guessed it correctly: I’m currently trying to understand well the behavior of std::atomic and I was also struggling to infer good practices regarding the use of memory_order_relaxed, memory_order_consume, memory_order_acquire, memory_order_release, memory_order_acq_rel, memory_order_seq_cst.

There’s good resources for beginners about atomics on the web:

1 Like

Actually, in this kind of topics, beginner texts can be more confusing than helpful though. The author introduces his own atomic library instead of discussing std::atomic (to be fair it was published in 2013 when C++11 was recent).

1 Like

Mintomic is a very small part in all the blog posts (anyway it is not used anymore).
The C++11 atomics are largely covered.
It is for me the best resource for beginners (easier at start that the Anthony Williams book for instance).
My opinion is probably biased since i learned all (at first) with that blog! :heart_eyes:

From oldest to newest:

< Locks Aren't Slow; Lock Contention Is >
< Always Use a Lightweight Mutex >
< Roll Your Own Lightweight Mutex >
< Implementing a Recursive Mutex >
< Memory Reordering Caught in the Act >
< An Introduction to Lock-Free Programming >
< Memory Ordering at Compile Time >
< Memory Barriers Are Like Source Control Operations >
< Acquire and Release Semantics >
< Weak vs. Strong Memory Models >
< This Is Why They Call It a Weakly-Ordered CPU >
< Introducing Mintomic: A Small, Portable Lock-Free API >
< Atomic vs. Non-Atomic Operations >
< The Happens-Before Relation >
< The Synchronizes-With Relation >
< Acquire and Release Fences >
< Double-Checked Locking is Fixed In C++11 >
< Acquire and Release Fences Don't Work the Way You'd Expect >
< The Purpose of memory_order_consume in C++11 >
< Fixing GCC's Implementation of memory_order_consume >
< Semaphores are Surprisingly Versatile >
< You Can Do Any Kind of Atomic Read-Modify-Write Operation >
< Can Reordering of Release/Acquire Operations Introduce Deadlock? >

1 Like

Ups, I think that a quick search about “atomic” wasn’t the best approach to find the best posts :wink: Thanks!

1 Like