Hi All,

I'm trying to use bools to sync between two threads. I cant' use C++11 and I thought mutexes would be slightly heavy for this.
So, I do something like this:

// ==================================
// Thread A
.. do some stuff ..

// wait for signal.
while (!signalFromThreadB) {
    pthread_yield();
}
.. do some more stuff.
// ==================================

// ==================================
// Thread B
.. do some stuff ..
signalFromThreadB = true
.. continue.
// ==================================

There is no other shared data between these two threads. Now questions:
I have declared signalFromThreadB as volatile bool

  1. Is the bool assignment guarenteed to be atomic?
  2. Is this fine for doing a sync or would I need to use mutexes to be safe?

Can anyone please explain?
Thanks!

Recommended Answers

All 7 Replies

Volatile allows a global or shared variable to be read and written attomically. Example: int newvalue = vvar++; which would set newvalue to the volatile variable (vvar) and then increment vvar. This may be a viable approach if the threads share no other data that needs to be protected from simultaneous access. Without more of your code to review, this is as much as I can say.

Volatile allows a global or shared variable to be read and written attomically.

That's not true (maybe you are confused with Java's volatiles? For Java 5 and up, "volatile" is equivalent to C++11's std::atomic). It's a common misconception (that I must admit, I used to have too). Volatile variables only mean that the compiler cannot optimize away the read / write accesses to the variable (e.g., by re-using the last cached value either in L1 cache or in registers). They used to be considered as poor-men's atomic variables because of two assumptions: (1) the program runs on a single core machine and (2) read / write operations on primitive values (bool, int, etc.) are just a single instruction. Under those conditions, a volatile primitive variable is essentially like an atomic variable because there is no need for any additional precautions except for making sure that the read/write operations are indivisible (a single instruction is indivisible) and not optimized away.

Nowadays, assumption (1) is definitely out because you can rarely find a single-core computer anywhere these days. And assumption (2) is complicated by the existence of multi-level caches and instruction pipelines in modern CPUs. So, volatile variables have sort of lost their appeal as poor-men's atomics (but not completely), because they simply don't work anymore. For instance, I onced used a volatile variable in place of an atomic in a real application, and saw sporatic failures (roughly, 1 in a million duty cycles (writes) of the variable) that completely confounded me until I learned about my mistaken assumptions about volatile variables.

Just as a basic example, if two threads are concurrently incrementing the value of a shared volatile variable, like a += 5; where a is a volatile int, then the final value of the variable is undefined, unlike when using atomics, in which case it is guaranteed to end up having the twice-incremented value at the end.

Is the bool assignment guarenteed to be atomic?

No, but it doesn't really matter.

First of all, bool is a very small type, obviously, so, it would be hard to find any platform on which writing a value to a bool variable would take more than a single instruction (anything smaller than a single word (32bit, usually, even on 64bit platforms) should get written in a single instruction). So, in that "old fashion" sense, the bool assignment is atomic, but not in the modern sense.

The modern sense of atomic implies that after the moment a new value is written to the variable, there is no possibility that any other thread will see its old value when reading it. And that's simply not true here because (1) the other thread could be running simultaneously (on another core of the CPU, as opposed to purely on a task-scheduling basis, as in the old single-core days), and (2) a duplicate image of the memory containing the bool value might exist on an another per-core cache (L1) or registers. Therefore, it is possible for another thread to read (or use) the old value of the bool after you have written the new value to it.

However, is this really a problem for you? I would say, probably not, given the code that you have posted. For this type of simple "rendezvous" predicate between two threads, the worst that will happen if your reading thread reads an old value of the bool after you have set it to true is that it will "yield" one more time. When threads are swapped in and out of execution, the OS kernel will cycle the cache memory, meaning that the new value of the bool should be visible when the thread resumes the next time around (and making the bool "volatile" guarantees that when the thread resumes, it will read the value again, not just assume that it didn't change). So, the worst that can happen is that you miss one task scheduling cycle, which is probably no big deal, and if it was a big deal, then you shouldn't be using threads at all (you should be using an asynchronous micro-kernel OS like QNX or GNU Hurd).

Is this fine for doing a sync or would I need to use mutexes to be safe?

This is fine for doing a sync. You definitely don't need a mutex for this. For this type of stuff, you either use a volatile variable (as you did, and knowing the limitations that I just explained), or you use an atomic variable. Using the atomic variable would only really be required if you have multiple writing threads, or if there is some things that could break if a reading thread saw an old value after it had been updated (e.g., like corrupting a calculation that comes right after, or something like that). For example, you need an atomic variable if you implement a spin-lock, which is very similar to your "rendezvous" (your thing is like a "spin-rendezvous") but with the key difference that immediately after seeing the "unlocked" value on the shared variable, the variable must be locked again, atomically, so that no other threads that are waiting might lock it concurrently (as could be the case with a volatile variable).

Warning There is one thing that you have to worry about here. Your bool variable could cause a kind of false sharing problem. First, since bool is a small type (usually 1 byte), if it is arranged alongside other small global variables, they could all be packed together on the same word. This means that when writing a new value to the bool, the system also has to do a kind of read-write of the other surrounding bytes (on 32bit alignment boundary around the bool you are actually writing to). This means that a concurrent access to those other global variables might be affected by your write operations on that volatile bool variable. So, you should make sure that any such volatile bool variable that you use between threads is not being lumped in with surrounding small variables (one way to avoid this is to use int instead, just to make sure you fill the whole alignment boundary with that shared variable). The second false sharing problem is the performance issue. The bool variable and all the memory around it (within the size of cache lines on your system (usually between 64bytes and 256bytes)) becomes a kind of "thread-sensitive" region of memory because multiple threads are using it, which means that if there are frequent reads and writes from the surrounding variables, even if they are not shared between threads, then there will be a performance overhead due to the work that the CPU has to do to ensure coherence of the data over that entire cache line. So, it is preferrable if you don't have any fast changing data around that region (e.g., just non-volatile / constant variables).

commented: I was thinking pre-C++11. :-) +0
while (!signalFromThreadB) {
    pthread_yield();
}

Isn't that a flat-out polling loop that's going to burn 100% of the spare CPU time? Safe, maybe, but not good practice?

Isn't that a flat-out polling loop that's going to burn 100% of the spare CPU time?

It's not burning 100% CPU time because it yields its time at each iteration.

Safe, maybe, but not good practice?

It is good practice in some cases. Without more context, we can't judge it. Mainly, such spin-lock is appropriate when contingency is very low, that is, the vast majority of the time, when the loop is reached, the condition is already met, and therefore, there is no actual loop happening, it just passes the condition and moves on. In those cases, if you were to use a synchronization primitive like a mutex (or a condition-variable, or a future-promise mechanism), you would end up doing a kernel-space switch, which has a run-time cost, whether the condition is already met or not.

Thanks a lot for the wonderful explanation Mike!

For this type of stuff, you either use a volatile variable (as you did, and knowing the limitations that I just explained), or you use an atomic variable

My use case is in such a way that one thread waits till the other thread does some initialization. Till then, the waiting thread yields. So, volatile bool seems viable.

Also, I can't use C++11. I'm curious, is there an alternative? Apart from using a mutex?

If you can't use C++11, you can just use Boost.Thread, which is nearly identical to standard threads of C++11.

Apart from using a mutex?

Using a mutex directly is kind of awkward in this case (you would have to lock it during initialization and release it when done, and wait for that release in the second thread). Another typical, but also awkward, solution is to use a conditional variable, which also involves a mutex, and I find it to be awkward because you have to deal with spurious wake-ups and stuff like that.

The solution that I would recommend is a future<void>. Using Boost.Thread, you would do this:

#include <boost/thread/future.hpp>

boost::promise<void> signalFromThreadB;

// ==================================
// Thread A

boost::future<void> eventFromB = signalFromThreadB.get_future();

.. do some stuff ..

// wait for signal.
eventFromB.get();

.. do some more stuff.

// ==================================


// ==================================
// Thread B

.. do some stuff ..

if( initialization went OK )
  signalFromThreadB.set_value();
else
  signalFromThreadB.set_exception(some_exception());

.. continue.

// ==================================

And as you can notice, the future-promise mechanism is really nice because it's simple, works, and you can even communication an exception across the threads (set the exception on thread B, and it will get thrown in thread A, which is nice).

You can also use something other than void if you want to safely communicate a piece of data across the threads, along with the notification.

In your case, this might be overkill, but at the same time, using something like future-promise gives you peace of mind, and frankly, I would gladly take that trade-off any day.

And if you can't use Boost, then you should either revise your project's guidelines, or you should write your own version of the future-promise mechanism using platform-specific functions under-the-hood (or just copy-paste the Boost.thread implementation.. it's BSD-licensed for a reason, after all).

P.S.:

rubberman: I was thinking pre-C++11. :-)

C++11 did not change anything in my first (long) explanation post. All that I said in there is just as valid in pre-C++11 as it is now in C++11.

Thanks Mike!

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.