17

I have a multi-threaded C++ app which does 3D rendering with the OpenSceneGraph library. I'm planning to kick off OSG's render loop as a separate thread using boost::threads, passing a data structure containing shared state in to the thread. I'm trying to avoid anything too heavyweight (like mutexes) for synchronization, as the render loop needs to be pretty tight, and OSG itself tries to avoid having to ever lock. Most of the shared state is set before the thread is started, and never changed. I do have some data that does need to be changed, which I am planning to double-buffer. However, I have a simple boolean for signaling the thread to suspend rendering, and later resume rendering, and another to kill it. In both cases the app thread sets the bool, and the render thread only reads it. Do I need to synchronize access to these bools? As far as I can tell, the worse thing that could happen is the the render loop continues on for an extra frame before suspending or quitting.

Brian Stewart
  • 8,647
  • 11
  • 49
  • 65

5 Answers5

16

In C++11 and later, which has standards-defined concurrency, use std::atomic<bool> for this purpose. From http://en.cppreference.com/w/cpp/atomic/atomic:

If one thread writes to an atomic object while another thread reads from it, the behavior is well-defined (see memory model for details on data races).


The following old answer may have been true at some time in the past with some compilers and some operating environments, but it should not be relied upon today:

You're right, in this case you won't need to synchronise the bools. You should declare them volatile though, to ensure that the compiler actually reads them from memory each time, instead of caching the previous read in a thread (that's a simplified explanation, but it should do for this purpose).

The following question has more information about this: C++ Thread, shared data

Greg Hewgill
  • 828,234
  • 170
  • 1,097
  • 1,237
  • 2
    That's not what 'volatile' actually does by the spec, though. MSVC is a bit more forgiving of lazy programmers, but GCC certainly will happily optimize, reschedule, and otherwise mangle your 'volatile' memory accesses. – ephemient Oct 21 '08 at 18:33
  • IIRC, per the spec, 'volatile' means the compiler must assume the variable can change at any time outside of the application's control. So if the compiler is correct, 'volatile' should give the desired effect. – Nick Oct 21 '08 at 18:43
  • No, 'volatile' only has meaning on memory mapped to device I/O registers. Anything else is unspecified, and is not obeyed by GCC with higher optimizations. – ephemient Oct 21 '08 at 20:08
  • My understanding is that conforming C++ implementations must re-evaluate volatile variables after any sequence point, which imposes very specific restrictions on the compiler. That said, GCC may certainly not be a conforming compiler for C++, and/or my understanding could be wrong. – Nick Oct 21 '08 at 20:35
  • 5
    That's true but not useful. The access to the volatile object must have been evaluated, but C++ has nothing to say about whether a write from another thread is visible to your thread, because C++ has nothing to say about threads. You must consult your compiler/thread documentation, or use C++0x. – Steve Jessop Oct 22 '08 at 01:05
  • 1
    So for example the C++ spec does *not* require that on systems with a non-coherent cache, volatile accesses cause cache synchronisation, only that the access (to cache) isn't moved across a sequence point. Compilers might be friendly, and do cache sync on volatile access. Or might not. – Steve Jessop Oct 22 '08 at 01:12
  • 6
    volatile does not prevent read / write re-ordering. VC++ 2005/8 seems to do this correctly, by adding additional meaning to the keyword volatile in the same way Java 5 does. GCC on the other hand will definitely re-order things. – computinglife Oct 22 '08 at 18:08
  • As of today, 2017 (9 years later).. This answer is **completely wrong**. More precisely, its been wrong since 2011. – WhiZTiM Dec 18 '17 at 19:05
  • @WhiZTiM: Thanks for pointing this out, I've updated the answer. – Greg Hewgill Dec 18 '17 at 19:22
7

Why not simply use an interlocked variable?

shoosh
  • 70,450
  • 50
  • 199
  • 310
4

As for C++11 and later it is finally threads-aware and clearly states that modifying a bool (or other non-atomic variable) in one thread and accessing it at the same time in another one is undefined behavior. In you case using std::atomic<bool> should be enough to make your program correct, saving you from using locks.
Do not use volatile. It has nothing to do with threads. For more discussion look at Can I read a bool variable in a thread without mutex?

Tadeusz Kopec
  • 11,984
  • 6
  • 51
  • 79
3

I don't think you need a fully fledged mutex here -- though the render thread will need to busy wait in the 'suspended' state if you aren't using a synchronization object that supports a wait primitive.

You should look into using the various interlocked exchange primitives though (InterlockedExchange under Windows). Not because read/writes from the bool are non-atomic, but to ensure that there are no weird behaviours the compiler reordering memory accesses on a single thread.

Rob Walker
  • 43,959
  • 15
  • 93
  • 134
1

This thread has a little more info and discussion on thread-safety, especially for simple data types:

How can I create a thread-safe singleton pattern in Windows?

Community
  • 1
  • 1
Mark Ingram
  • 65,792
  • 48
  • 164
  • 225