Is It Ok to Read a Shared Boolean Flag Without Locking It When Another Thread May Set It (At Most Once)

Is it ok to read a shared boolean flag without locking it when another thread may set it (at most once)?

It is never OK to read something possibly modified in a different thread without synchronization. What level of synchronization is needed depends on what you are actually reading. For primitive types, you should have a look at atomic reads, e.g. in the form of std::atomic<bool>.

The reason synchronization is always needed is that the processors will have the data possibly shared in a cache line. It has no reason to update this value to a value possibly changed in a different thread if there is no synchronization. Worse, yet, if there is no synchronization it may write the wrong value if something stored close to the value is changed and synchronized.

What are the options for safely modifying and reading a boolean across multiple threads and cpus?

What potential issues could actually arise from separate threads polling and modifying the value of _connected?

It's Undefined Behavior, no matter what.

What options are there to prevent these?

A common solution is to use std::atomic<bool> instead of bool.

There are fancier (and much more complex) ways to ensure synchronization between threads, but std::atomic is an excellent first choice, and not difficult to use correctly.

Perhaps making _connected a volatile bool might solve issues

It won't. volatile does not solve thread synchronization issues.

Is it safe to set the boolean value in thread from another one?

It's perfectly safe to do this. The reading thread will always read either true or false. There will be no tearing because a Boolean is just a single byte. In fact the same is true for an aligned 32 bit value in a 32 bit process, i.e. Integer.

This is what is known as a benign race. There is a race condition on the boolean variable since one thread reads whilst another thread writes, without synchronisation. But the logic of this program is not adversely affected by the race. In more complex scenarios, such a race could be harmful and then synchronisation would be needed.

What happens if two threads attempt to access the same variable without any locking mechanism?

If

  1. You only have one writer; AND

  2. You do not care about false negatives (ie. isFinished appears false to the main thread while it is true to the worker thread)

Then you could get away without having synchronization.

Dangers of simultaneous write and read of a boolean in a simple situation

Data races result in undefined behavior. As far as the standard is concerned, a conforming implementation is permitted to segfault.

In practice the main danger is that without synchronization, the compiler will observe enough of the code in the reader loop to judge that b "never changes", and optimize out all but the first read of the value. It can do this because if it observes that there is no synchronization in the loop, then it knows that any write to the value would be a data race. The optimizer is permitted to assume that your program does not provoke undefined behavior, so it is permitted to assume that there are no writes from other threads.

Marking b as volatile will prevent this particular optimization in practice, but even on volatile objects data races are undefined behavior. Calling into code that the optimizer "can't see" will also prevent the optimization in practice, since it doesn't know whether that code modifies b. Of course with link-time/whole-program optimization there is less that the optimizer can't see, than with compile-time-only optimization.

Anyway, preventing the optimization from being made in software doesn't prevent the equivalent thing happening in hardware on a system with non-coherent caches (at least, so I claim: other people argue that this is not correct, and that volatile accesses are required to read/write through caches. Some implementations do behave that way). If you're asking about what the standard says then it doesn't really matter whether or not the hardware shows you a stale cache indefinitely, since behavior remains undefined and so the implementation can break your code regardless of whether this particular optimization is the thing that breaks it.

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?

C# and thread-safety of a bool

No, not all of them are thread safe.

Case one isn't actually completely thread safe, or better saying - it isn't thread safe at all. Even if operations with boolean are atomic, variable value can be stored in a cache, and so, as in multicore CPU each core has it's own cache, value can be potentially corrupted.

Going even further, compiler and CPU can perform some internal optimizations, including instruction reordering, which can harmfully affect your program's logic.

You can add the volatile keyword, to notify the compiler that this field is used in a multi-threaded context. It will fix problems with cache and instruction reordering, but doesn't give you truly "thread safe" code (as write operations still will be not synchronized). Also volatile cannot be applied to local variable.

So when dealing with multi-threading you always have to use some technique of thread synchronization on valuable resources.

For more information - read this answer, which has some deeper explanation of different techniques. (example there is about int, but is doesn't really matter, it describes general approach.)

AtomicBoolean, set flag once, necessary? Might a static boolean be ok?

When most of the threads only reads the variable and only one of them and once will change the value you don't need to worry about the performance. AtomicBoolean is using a mechanism called CAS which now is supported by most modern processors and is a low level operation. As a result of using CAS there is almost no drawback when reading the value (which is something different from using a standard lock). With the scenario you described volatile static boolean will be enough - volatile prevents jvm from doing optimization when reading from variable, like reusing value held in register instead of checking the value in memory, so whenever a thread change the value of variable other threads will see the change. In your case both solutions will give similar if not the same performance results.
Volatile will be faster than AtomicBoolean in a scenario where lot of write operation have place, but honestly I can not imagine a scenario where you have lot of writing and no one is interested in reading.



Related Topics



Leave a reply



Submit