3

I'm creating this multithreaded C++ program and upon compiling in Release mode, I'm finding bugs of the sort (object still null) ie, it looks like missing volatile markers.

But the problem is, since there is a 2nd worker thread touching all kinds of objects, it means that virtually everything is volatile in the program.

I'm wondering if there is a way to turn off optimizations in the Apple LLVM compiler that create the bugs the volatile keyword was specifically designed to fix. These bugs don't show up in debug mode (because optimizations are off). Putting volatile everywhere basically means peppering every class with volatile after every member function, and adding volatile before every shared variable declaration.

I think I'd rather lose that volatile optimization than risk a spurious bug showing up because I forgot to mark something volatile.

Kara
  • 6,115
  • 16
  • 50
  • 57
bobobobo
  • 64,917
  • 62
  • 258
  • 363
  • 10
    If a bug shows up in optimized mode, but is not showing up in debug mode, it still means that the program is incorrect. If anything, I'd be regretting that the error is not showing up in debug mode, because there's a race condition somewhere in your code that you cannot catch. – Sergey Kalinichenko Aug 16 '13 at 15:09
  • 3
    Don't use volatile for multithreading (better use std library, std::atomic for example): http://software.intel.com/en-us/blogs/2007/11/30/volatile-almost-useless-for-multi-threaded-programming – user1837009 Aug 16 '13 at 15:11
  • Well it's pretty clear what is happening. I have a `vector` array that shows up empty, even though it clearly was initialized. – bobobobo Aug 16 '13 at 15:11
  • 1
    Can you initialise your vector before starting your worker threads? – dunc123 Aug 16 '13 at 15:16
  • 2
    @bobobobo: You may blame Java for reusing the keyword `volatile` to denote variables that are concurrently accessed/modified by different threads, because in C and C++ `volatile` has *nothing* to do with threads: it's about marking special hardware registers so that reads and writes to them are not optimized away. This used, for example, to write kernel/driver code (even single-threaded). For multi-threaded code, what you need is *memory synchronization* operations. – Matthieu M. Aug 16 '13 at 15:39
  • @MatthieuM. So, if I have a class level variable, say `Object *o`, and it is _initialized on another thread_, assuming I've locked correctly, `o` will _always_ be correctly initialized on the main thread, even if `o` isn't marked `volatile`? – bobobobo Aug 16 '13 at 17:48
  • 3
    @bobobobo: If you've locked correctly, then yes. A correct locking implementation will synchronise the threads when the lock is acquired and/or released, so once the main thread has the lock it will see anything that another thread modified while it had the lock. `volatile` has nothing to do with that. – Mike Seymour Aug 16 '13 at 18:05
  • @MikeSeymour *Thank you* for being the voice of reason. [Questions like this](http://stackoverflow.com/questions/18789894/read-same-structure-on-all-threads/18790175#comment27707243_18790175) involving using `volatile` have been the bane of existence lately. – WhozCraig Sep 13 '13 at 21:48

1 Answers1

7

In C++, volatile has nothing to do with thread safety. You cannot rely on it to avoid data races. Its purpose is to force synchronised accesses to a variable (from a single thread, or threads that use some other mechanism to synchronise with each other) to happen exactly in the order specified. This is often necessary when interacting with hardware, to prevent accesses that appear to do nothing, but actually affect the state of the hardware, from being optimised away. It gives no guarantees about the effect of unsynchronised accesses.

To avoid data races, you must use either atomic operations or explicit locks to synchronise access to shared objects. C++11 provides these in the standard library; if you're stuck in the past, then you'll have to rely on whatever libraries (such as pthreads) or language extensions (such as atomic intrinsics) are available on your platform.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • I know. I'm just asking about the possibility that a class-level variable, being initialized on a worker thread, would ever appear as _still uninitialized_ on the main thread (assuming I've locked access correctly). I thought `volatile` meant _don't cache this value, because it might have changed on another thread since you last read it_. – bobobobo Aug 16 '13 at 17:49
  • @bobobobo: If you see that then access was not locked correctly. `volatile` means that all accesses to the variable will happen in the order specified within a thread; it doesn't give any guarantees that a modification will propagate to another thread, or that it will appear atomic if it does. `volatile` simply cannot be used for thread safety, since that is not what it was designed for. – Mike Seymour Aug 16 '13 at 18:00
  • Well you'll be pleased to know the bug was the result of a very rare `nan` condition. I was blaming threads because I'd always been suspicious of them. Ah was wrong. – bobobobo Aug 16 '13 at 21:50