When do I really need to use atomic<bool> instead of bool?

C++C++11AtomicStdatomic

C++ Problem Overview


Isn't atomic<bool> redundant because bool is atomic by nature? I don't think it's possible to have a partially modified bool value. When do I really need to use atomic<bool> instead of bool?

C++ Solutions


Solution 1 - C++

No type in C++ is "atomic by nature" unless it is an std::atomic*-something. That's because the standard says so.

In practice, the actual hardware instructions that are emitted to manipulate an std::atomic<bool> may (or may not) be the same as those for an ordinary bool, but being atomic is a larger concept with wider ramifications (e.g. restrictions on compiler re-ordering). Furthermore, some operations (like negation) are overloaded on the atomic operation to create a distinctly different instruction on the hardware than the native, non-atomic read-modify-write sequence of a non-atomic variable.

Solution 2 - C++

Remember about memory barriers. Although it may be impossible to change bool partially, it is possible that multiprocessor system has this variable in multiple copies and one thread can see old value even after another thread has changed it to new. Atomic introduces memory barrier, so it becomes impossible.

Solution 3 - C++

C++'s atomic types deal with three potential problems. First, a read or write can be torn by a task switch if the operation requires more than one bus operation (and that can happen to a bool, depending on how it's implemented). Second, a read or write may affect only the cache associated with the processor that's doing the operation, and other processors may have a different value in their cache. Third, the compiler can rearrange the order of operations if they don't affect the result (the constraints are a bit more complicated, but that's sufficient for now).

You can deal with each of these three problems on your own by making assumptions about how the types you are using are implemented, by explicitly flushing caches, and by using compiler-specific options to prevent reordering (and, no, volatile doesn't do this unless your compiler documentation says it does).

But why go through all that? atomic takes care of it for you, and probably does a better job than you can do on your own.

Solution 4 - C++

Consider a compare and exchange operation:

bool a = ...;
bool b = ...;

if (a)
    swap(a,b);

After we read a, we get true, another thread could come along and set a false, we then swap (a,b), so after exit b is false, even though the swap was made.

Using std::atomic::compare_exchange we can do the entire if/swap logic atomically such that the other thread could not set a to false in between the if and the swap (without locking). In such a circumstance if the swap was made than b must be false on exit.

This is just one example of an atomic operation that applies to a two value type such as bool.

Solution 5 - C++

Atomic operations are about more than just torn values, so while I agree with you and other posters that I am not aware of an environment where torn bool is a possibility, there is more at stake.

Herb Sutter gave a great talk about this which you can view online. Be warned, it is a long and involved talk. Herb Sutter, Atomic Weapons. The issue boils down to avoiding data races because it allows you to have the illusion of sequential consistency.

Solution 6 - C++

Atomicity of certain types depends exclusively on the underlying hardware. Each processor architecture has different guarantees about atomicity of certain operations. For example:

> The Intel486 processor (and newer processors since) guarantees that the following basic memory operations will always be carried out atomically: > > - Reading or writing a byte > - Reading or writing a word aligned on a 16-bit boundary > - Reading or writing a doubleword aligned on a 32-bit boundary

Other architectures have different specifications on which operations are atomic.

C++ is a high-level programming language that strives to abstract you from the underlying hardware. For this reason standard simply cannot permit one to rely on such low-level assumptions because otherwise your application wouldn't be portable. Accordingly, all the primitive types in C++ are provided with atomic counterparts by C++11 compliant standard library out-of-the-box.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser955249View Question on Stackoverflow
Solution 1 - C++Kerrek SBView Answer on Stackoverflow
Solution 2 - C++DimsView Answer on Stackoverflow
Solution 3 - C++Pete BeckerView Answer on Stackoverflow
Solution 4 - C++Andrew TomazosView Answer on Stackoverflow
Solution 5 - C++huskerchadView Answer on Stackoverflow
Solution 6 - C++Alexander ShukaevView Answer on Stackoverflow