When Do I Really Need to Use Atomic<Bool> Instead of Bool

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

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.

Do I have to use atomic bool for exit bool variable?

Do I have to use atomic for “exit” bool variable?

Yes.

Either use atomic<bool>, or use manual synchronization through (for instance) an std::mutex. Your program currently contains a data race, with one thread potentially reading a variable while another thread is writing it. This is Undefined Behavior.

Per Paragraph 1.10/21 of the C++11 Standard:

The execution of a program contains a data race if it contains two conflicting actions in different threads,
at least one of which is not atomic, and neither happens before the other. Any such data race results in
undefined behavior.

The definition of "conflicting" is given in Paragraph 1.10/4:

Two expression evaluations conflict if one of them modifies a memory location (1.7) and the other one
accesses or modifies the same memory location.

Do I need std::atomic bool or is POD bool good enough?

You need to use std::atomic to avoid undesired optimizations (compiler reading the value once and either always looping or never looping) and to get correct behavior on systems without a strongly ordered memory model (x86 is strongly ordered, so once the write finishes, the next read will see it; on other systems, if the threads don't flush CPU cache to main RAM for other reasons, the write might not be seen for a long time, if ever).

You can improve the performance though. Default use of std::atomic uses a sequential consistency model that's overkill for a single flag value. You can speed it up by using load/store with an explicit (and less strict) memory ordering, so each load isn't required to use the most paranoid mode to maintaining consistency.

For example, you could do:

// global
std::atomic<bool> run = true;

// thread 1
while (run.load(std::memory_order_acquire)) { /* do stuff */ }

// thread 2
/* do stuff until it's time to shut down */
run.store(false, std::memory_order_release);

On an x86 machine, any ordering less strict than the (default, most strict) sequential consistency ordering typically ends up doing nothing but ensuring instructions are executed in a specific order; no bus locking or the like is required, because of the strongly ordered memory model. Thus, aside from guaranteeing the value is actually read from memory, not cached to a register and reused, using atomics this way on x86 is free, and on non-x86 machines, it makes your code correct (which it otherwise wouldn't be).

Why does a boolean need to be atomic?

What exactly does an AtomicBool protect us from? Why can we not use a regular bool from different threads (other than the fact that the compiler won't let us)?

Anything that might go wrong, whether you can think of it or not. I hate to follow this up with something I can think of, because it doesn't matter. The rules say it's not guaranteed to work and that should end it. Thinking you have to think of a way it can fail or it can't fail is just wrong.

But here's one way:

 // Release the lock
thread_lock = false

Say this particular CPU doesn't have a particularly good way to set a boolean to false without using a register but does have a good single operation that negates a boolean and tests if it's zero without using a register. On this CPU, in conditions of register pressure, this might get optimized to:

  1. Negate thread_lock and test if it's zero.
  2. If the copy of thread_lock was false, negate thread_lock again.

What happens if in-betweens steps 1 and 2 another thread observes thread_lock to be true even though it was false going into this operation and will be false when it's done?

When do I need to use AtomicBoolean in Java?

When multiple threads need to check and change the boolean. For example:

if (!initialized) {
initialize();
initialized = true;
}

This is not thread-safe. You can fix it by using AtomicBoolean:

if (atomicInitialized.compareAndSet(false, true)) {
initialize();
}

Can a bool read/write operation be not atomic on x86?

It all depends on what you actually mean by the word "atomic".

Do you mean "the final value will be updated in one go" (yes, on x86 that's definitely guaranteed for a byte value - and any correctly aligned value up to 64 bits at least), or "if I set this to true (or false), no other thread will read a different value after I've set it" (that's not quite such a certainty - you need a "lock" prefix to guarantee that).

difference between standard's atomic bool and atomic flag

std::atomic bool type not guranteed to be lock-free?

Correct. std::atomic may be implemented using locks.

then it's not atomic or what?

std::atomic is atomic whether it has been implemented using locks, or without. std::atomic_flag is guaranteed to be implemented without using locks.

So what's the difference b/w two

The primary difference besides the lock-free guarantee is:

std::atomic_flag does not provide load or store operations.


and when should I use which?

Usually, you will want to use std::atomic<bool> when you need an atomic boolean variable. std::atomic_flag is a low level structure that can be used to implement custom atomic structures.



Related Topics



Leave a reply



Submit