Is volatile bool for thread control considered wrong?

murrekatt picture murrekatt · Aug 9, 2011 · Viewed 17k times · Source

As a result of my answer to this question, I started reading about the keyword volatile and what the consensus is regarding it. I see there is a lot of information about it, some old which seems wrong now and a lot new which says it has almost no place in multi-threaded programming. Hence, I'd like to clarify a specific usage (couldn't find an exact answer here on SO).

I also want to point out I do understand the requirements for writing multi-threaded code in general and why volatile is not solving things. Still, I see code using volatile for thread control in code bases I work in. Further, this is the only case I use the volatile keyword as all other shared resources are properly synchronized.

Say we have a class like:

class SomeWorker
{
public:
    SomeWorker() : isRunning_(false) {}
    void start() { isRunning_ = true; /* spawns thread and calls run */ }
    void stop() { isRunning_ = false; }

private:
    void run()
    {
        while (isRunning_)
        {
            // do something
        }
    }
    volatile bool isRunning_;
};

For simplicity some things are left out, but the essential thing is that an object is created which does something in a newly spawned thread checking a (volatile) boolean to know if it should stop. This boolean value is set from another thread whenever it wants the worker to stop.

My understanding has been that the reason to use volatile in this specific case is simply to avoid any optimization which would cache it in a register for the loop. Hence, resulting in an infinite loop. There is no need to properly synchronize things, because the worker thread will eventually get the new value?

I'd like to understand if this is considered completely wrong and if the right approach is to use a synchronized variable? Is there a difference between compiler/architecture/cores? Maybe it's just a sloppy approach worth avoiding?

I'd be happy if someone would clarify this. Thanks!

EDIT

I'd be interested to see (in code) how you choose to solve this.

Answer

Kerrek SB picture Kerrek SB · Aug 9, 2011

You don't need a synchronized variable, but rather an atomic variable. Luckily, you can just use std::atomic<bool>.

The key issue is that if more than one thread accesses the same memory simultaneously, then unless the access is atomic, your entire program ceases to be in a well-defined state. Perhaps you're lucky with a bool, which is possibly getting updated atomically in any case, but the only way to be offensively certain that you're doing it right is to use atomic variables.

"Seeing codebases you work in" is probably not a very good measure when it comes to learning concurrent programming. Concurrent programming is fiendishly difficult and very few people understand it fully, and I'm willing to bet that the vast majority of homebrew code (i.e. not using dedicated concurrent libraries throughout) is incorrect in some way. The problem is that those errors may be extremely hard to observe or reproduce, so you might never know.

Edit: You aren't saying in your question how the bool is getting updated, so I am assuming the worst. If you wrap your entire update operation in a global lock, for instance, then of course there's no concurrent memory access.