10

I have recently come across a curious use of the volatile keyword in C++ multithreaded code. To abstract the programming pattern, let's assume there is a control object which is accessed by one producer and several consumer threads:

class control_t {
    pthread_mutex_t control_lock;
    pthread_cond_t wake_cond;
    bool there_is_work_todo;

    control_t volatile* vthis() { return this; }
}

The consumer thread does the following (c is a non-volatile pointer to the control object):

...
pthread_mutex_lock(c->control_lock)
while (!c->vthis()->there_is_work_todo) {
    pthread_cond_wait(&c->wake_cond, &c->control_lock);
}
...

The idea here is that the consumer threads will wait until there is some work to be done, which the producer signalizes via the wake_cond variable.

What I don't understand here is why the control object is accessed through a volatile pointer to "this", which is returned by the method vthis(). Why is that?

Caetano Sauer
  • 263
  • 1
  • 10
  • Is the object pointer `c` declared volatile here ? – brokenfoot Apr 22 '14 at 15:12
  • 1
    As mentioned in [this MSDN page](http://msdn.microsoft.com/en-us/library/ytk2ae82.aspx), for a `volatile` `this` pointer: "Member data is loaded from memory each time it is accessed; disables certain optimizations" – Lilshieste Apr 22 '14 at 15:15
  • @alk, I don't think so. The compiler is free to perform the (legal) optimizations it wishes, even if an instance member is passed to `pthread_mutex_lock()`. – Frédéric Hamidi Apr 22 '14 at 15:15
  • @brokenfoot the pointer c is not volatile (I edited the question) – Caetano Sauer Apr 22 '14 at 15:34
  • 4
    As far as I remember PThreads' locking implies a memory barrier, so that using `volatile` is redundant: http://stackoverflow.com/a/8120128/694576 – alk Apr 22 '14 at 15:37
  • 2
    @alk: I agree, `volatile` should be useless for multi-threading, except maybe as a phantom type. It's a mechanism for hardware registers first and foremost. – Matthieu M. Apr 22 '14 at 15:41
  • @MatthieuM. You seem to suggest that volatile is not needed at all in this code. Could you please write an answer explaining that? Thank you – Caetano Sauer Apr 22 '14 at 15:47
  • 2
    Please see the answers to question to the answer I linked in my 2nd comment above. Also this question and its answers: http://stackoverflow.com/questions/2484980/why-is-volatile-not-considered-useful-in-multithreaded-c-or-c-programming turn to this issue. – alk Apr 22 '14 at 15:50
  • 1
    IMO, this is a bad design if you are not locking your shared variables and instead relying on `volatile`. One will end up with unexpected values! – brokenfoot Apr 22 '14 at 15:51

2 Answers2

5

The use of volatile in multi-threaded code is generally suspect. volatile was designed to avoid optimizing out reads and writes to memory, which is useful when such reads and writes occur on special addresses that map to hardware registers. See, for example, how volatile is useless to prevent data-races, and how it can be (ab)used as a phantom type...

Since the author used proper synchronization (mutexes and condition variables), the use of of volatile is extremely suspect. Such uses generally stem from a misunderstanding, most notably spread by languages such as Java who reused the same keyword with different semantics.

In C and C++, multi-threaded code should rely on memory barriers, such as those introduced by the mutexes and atomic operations, which guarantee that the values are correctly synchronized across the different CPU cores and caches.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • 1
    Just to add some reference for reading about volatile: http://www.drdobbs.com/parallel/volatile-vs-volatile/212701484 provides a nice discussion of the topic. – Jens Apr 22 '14 at 16:29
  • @Jens: Thanks, it seems indeed to be an interesting article about the differences of `volatile` in various languages. – Matthieu M. Apr 22 '14 at 16:32
1

The use of volatile in this code yields either undefined behavior or is redundant:

  1. If the object pointed to by c is not volatile, then accesses (including reads) with the addition of volatile is statically deemed as causing a side effect (to accomodate cases where the compiler cannot statically find out whether an access really uses a volatile object), but is not required to be carried out at all costs, since the side effect on the nonvolatile object does not constitute observable behavior.

  2. If the object that you call vthis on is volatile, then the code has undefined behavior because it accesses a volatile object through an lvalue of nonvolatile type before that call in the previous lines.

The code probably relies on an implementation not optimizing away the volatile access because of compatibility with existing code.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212