4

I'm working on a multi threaded program that provides access to one side of an interprocess communication system. Having never used volatile, I'm trying to figure out its proper usage.
I understand that (the relevant part) volatile tells the compiler that the variable it's applied to might be written to outside of the sequence of instructions of this thread so it should reread the memory every time it's used.

I have looked at some tutorials on volatile, but most either have the simplest examples (such as global shared variable) or just copy each other. Then from time to time I see someone positing that volatile doesn't do what you think it does. Also, some people say that if you're not writing device drivers or some such you shouldn't use volatile ( Is 'volatile' needed in this multi-threaded C++ code?). At this point I have my brain in a knot and I don't even know if the things that I worry about are real issues. So I'm wondering if there are any example code of volatile usage in OOP that I could look at.

But in particular, my main concern is about the external usage of getter function. In my code everything that I use is properly protected, however, I do provide some getter functions for private member variables that I don't protect (similar to this question Overhead of pthread mutexes?, especially the last paragraph of the first answer, or unprotected access to member in property get) because I don't want to tie my writing thread down, especially if multiple other threads are waiting in loops using the get function.

I'm worried that if I don't set the variable accessed with the get function as volatile then if some external program is waiting on it in a loop then the compiler for that external program might not reread the variable so the outside program will stay in an infinite loop. Is this a valid concern?

A very similar question is here, but I'm not sure what the answer was for c++.

Community
  • 1
  • 1
Matt
  • 1,327
  • 1
  • 12
  • 21
  • 2
    FYI, `volatile` is not really relevant to multithreading. See http://herbsutter.com/2009/01/12/effective-concurrency-volatile-vs-volatile/ and http://software.intel.com/en-us/blogs/2007/11/30/volatile-almost-useless-for-multi-threaded-programming/. – John Calsbeek Jun 04 '11 at 23:22

2 Answers2

3

In C++, volatile is not used for multithreading (at least as intended in the likes of Java 5, which provides a memory barrier or the like). In C++0x, there's atomic variables, and that comes closest to Java 5-style volatile.

C. K. Young
  • 219,335
  • 46
  • 382
  • 435
  • volatile is implemented with memory barrier semantics in MS Visual C++ starting in version 2005 http://msdn.microsoft.com/en-us/library/12a04hfd%28VS.80%29.aspx See also The Old New Thing: http://blogs.msdn.com/b/oldnewthing/archive/2011/04/19/10155452.aspx – sean e Jun 05 '11 at 01:59
  • 1
    @sean: Nice to know. Though, that's not very portable, especially given that the OP hasn't specified using MSVC. – C. K. Young Jun 05 '11 at 02:26
  • @seane That does you no good unless it's *guaranteed* to be implemented that way. Designing code based on how one compiler you looked at happened to implement something is foolish. – David Schwartz Aug 19 '17 at 03:00
3

The "some people" you refer to are entirely correct. The use of volatile does guarantee that the memory location is read or written each time you do that in the code. It does not guarantee that the value is current or seen by other threads that might run on a separate core or separate CPU. For that you need a memory barrier.

When used in a device driver, to read or write external hardware, this isn't a problem as the hardware will have been mapped to non-cached memory.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
  • So for the example given of an unprotected get method on multiple CPUs, would the second CPU eventually update to the new value or will it store the value somewhere and not re-read it. If not, how would you solve my unprotected get function issue? Keep in mind that using critical sections would needlessly slow down my code as mentioned. Is there some pattern to follow? And at this point I cannot rewrite too much of my code. – Matt Jun 08 '11 at 18:07
  • 1
    @Matt - It depends on your system, both hardware and software. Like other comments here mention, late versions of MSVC support some uses of volatile variables by promising not to move other accesses across a volatile access. Other compilers do not promise that. Some hardware will sync the caches of separate CPUs. Others will not unless explicitly asked to, as it is expensive. In that case each CPU can have it's own cached copy of the variable for a very long time. Using a lock makes **sure** that it works. Otherwise it is a system specific and non-portable solution. – Bo Persson Jun 08 '11 at 18:23
  • I'll convert most of them to critical sections and make a note of those that still rely on volatile. Thanks. – Matt Jun 08 '11 at 21:13
  • @BoPersson: It may also be worth noting that some implementations treat an acess to `volatile` as a global memory barrier; while it would be better to have explicit memory-barrier directives, `volatile` is the closest thing C has to a syntax that would even *suggest* a memory barrier on implementations that can't provide full support for C11 atomics. In many cases, using a volatile "buffer is ready" flag with a non-qualified buffer can be a good approach if a barrier can be guaranteed between accesses to the flag and accesses to the buffer. Such an approach may be much more efficient... – supercat Feb 26 '17 at 17:54
  • ...than requiring that the buffer be `volatile`, and all accesses to it must be treated as such. – supercat Feb 26 '17 at 17:55