4

I've seen this sentence:

the general rule is, if you have variables of primitive type that must be shared among multiple threads, declare those variables volatile

from this article, and this sentence:

In general, any data that may be undated asynchronously should be declared to be volatile.

from this page, now considering this introduced rule I'd like to know could you bring an example of a case where despite existence of asynchronous access to a data declaring that data volatile has no use in practice or there's no such exceptional case and the rule is strict.

John Dibling
  • 99,718
  • 31
  • 186
  • 324
Pooria
  • 2,017
  • 1
  • 19
  • 37
  • Did some formatting for you, rollback if you dont like it – John Dibling Nov 12 '10 at 20:25
  • @Pooria: Not exactly a dupe, but this question is well-answered in your own question from a few days ago: http://stackoverflow.com/questions/4136900/what-rules-does-compiler-have-to-follow-when-dealing-with-volatile-memory-locatio – John Dibling Nov 12 '10 at 20:30
  • @John: That's _his_ question, I voted to close this one. @Pooria: If the first article says volatile is any good for multi-threading, it's bullshit. That has been explained to you in the question John pointed out. – sbi Nov 12 '10 at 20:35
  • @sbi: explained in fact, at least in part, by me – John Dibling Nov 12 '10 at 20:40
  • After looking at the article in question, I take that back. The explanation won't fit in a comment, so I'll write an answer instead. – sbi Nov 12 '10 at 20:41
  • 1
    @sbi: see also what I have to say about Alexandrescu's use of `volatile`. In summary: it has nothing to do with async access or memory fences, and it has *everything* to do with letting the compiler catch places where you might be messing up. Just like when you try to assign a value to a `const` variable. – John Dibling Nov 12 '10 at 20:45
  • @John: Where? Had I known you have written such an answer, it would have saved me the work to write one myself. – sbi Nov 12 '10 at 20:48
  • In the other post. Your wording was much better than mine though. Please leave your answer intact. – John Dibling Nov 12 '10 at 20:48
  • @John: I've just stolen a few phrases from your very good comment above, though, and put them into my answer. I hope that's Ok for you. __Edit:__ Nevermind. I've made my answer CW. I felt guilty, otheriwse. `:)` – sbi Nov 12 '10 at 20:53
  • 1
    I've also added elaboration to my response in http://stackoverflow.com/questions/4136900/what-rules-does-compiler-have-to-follow-when-dealing-with-volatile-memory-locatio – John Dibling Nov 12 '10 at 21:10
  • @John Dibling,Sbi_this question is not the same to (http://stackoverflow.com/questions/4136900/what-rules-does-compiler-have-to-follow-when-dealing-with-volatile-memory-locatio) and please summarize stuff not to make too many posts. – Pooria Nov 12 '10 at 21:54
  • 1
    @Pooria: Here's your summary: Pooria: "Is it true that `volatile` must be used in multithreading?" Others: "`volatile` has nothing to do with multithreading" Pooria: "I'm not asking about multithreading" – John Dibling Nov 12 '10 at 22:26
  • 1
    @John Dibling_I'm asking about multithreading but not synchronizing accesses to memory as you think. – Pooria Nov 13 '10 at 05:21
  • 2
    It's my understanding that declaring something as volatile only tells the compiler not to cache it's value as it may be changed elsewhere. It doesn't tell the compiler not to reorder code, and that is the underlying problem here. So declaring as volatile *may* be necessary but it is not sufficient alone to achieve thread safety. That in practise it will probably work due to the way compilers get implemented - but that probably isn't a good enough guarentee really! Is that correct? – jcoder Nov 13 '10 at 11:29

8 Answers8

12

I remember when that article was published and I remember the endless discussions that then followed on comp.lang.c++.moderated.

IIRC, Andrei hijacks the volatile keyword to use it to discriminate between different function overloads. (See this article by Scott Meyers for another such an idea.) What he does is brilliant, in that it allows the compiler to catch you if you mess up protected and unprotected access to objects (very much like the compiler catches you should you try to modify a constant). But besides the fact that it helps you, it has nothing to do with actually protecting concurrent access to objects.

The problem is only that 90% of the people have one glance at the article and all they see is volatile and "threads" in the same article. Depending on their knowledge, they then either draw the wrong conclusion that volatile is good for threads (you seem to have done so) or they yell at him for leading others to draw the wrong conclusions.
Very few people seem to be able to actually read the article thoroughly and understand what he really does.

sbi
  • 219,715
  • 46
  • 258
  • 445
  • 1
    This is absolutely, 100% correct. Alexandrescu is brilliant in his use of `volatile`, and I have used it in this way ever since I read the article. It has helped me a great deal. But honestly, I do think that this one article is so misunderstood, so casually read, that it has done far more * damage* to the state of the art than helped it. – John Dibling Nov 12 '10 at 20:47
  • Well, well, wouldn't you know. Someone disagreed with this, but was too shy to explain what's wrong with it. Tsk, tsk. – sbi Nov 12 '10 at 21:01
  • @sbi_"volatile has nothing to do with actually protecting concurrent access to objects", If you think I'm confusing goal of volatile usage with synchronization object usage, you're absolutely extremely wrong. – Pooria Nov 12 '10 at 21:04
  • 1
    @Pooria: If what you're asking here isn't what has been answered in comments to your other question, I still have no idea what you're asking here. You then might want to clarify your question. – sbi Nov 12 '10 at 21:07
  • 3
    @Pooria: We are not here to fight with you. We are here to *help* you. If sbi and I are answering questions that you aren't asking, then *please restate the question* – John Dibling Nov 12 '10 at 21:12
  • @sbi_I told what you think is my intention is not even in that previous question of mine, just don't know why you'd have such misconception of the reason behind my question but too many other people have not and I'd appreciate if when you think you got an answer to a question you think you understand try not to make the thread yours. – Pooria Nov 12 '10 at 21:19
  • 1
    @Pooria: Won't speak for sbi, but here's my confusion. In this thread you say "if you have variables of primitive type that must be shared among multiple threads, declare those variables volatile" Then we respond "volatile has nothing to do with multithreading." Then you say "I wasnt asking about multithreading." If you're not asking about multithreading, then what are you asking about? – John Dibling Nov 12 '10 at 21:23
  • @John Dibling_what do you say about relation between volatile and caching? – Pooria Nov 12 '10 at 21:40
  • @Pooria: The Standard says that at sequence points `volatile` objects are "stable." Meaning all previous side-effects are complete, and no subsequent side-effects have occured. – John Dibling Nov 12 '10 at 22:17
  • 1
    [Someone asked the master](http://stackoverflow.com/questions/2479067/why-is-the-volatile-qualifier-used-through-out-stdatomic/2479474#2479474) – Johannes Schaub - litb Nov 12 '10 at 23:44
  • @John Dibling_So there you go that's one achievement. – Pooria Nov 13 '10 at 05:11
  • @John Dibling - I can see why Pooria doesn't find this response helpful. The original question is not asking whether volatile will protect concurrent access to objects, instead it asks whether volatile is necessary when using multi-threading. sbi's answer responds that "[volatile] has nothing to do with actually protecting concurrent access to objects" which doesn't answer the question. – danio Sep 12 '11 at 14:20
  • @danio: As you can tell by reading the comments in this thread, there was quite a bit of confusion about what was actually being asked. – John Dibling Sep 13 '11 at 16:31
11

I can't speak for the actual asynchronous access scenario, since I'm not too good at multithreading, but what the volatile modifier does is tell the compiler:

"Listen, this may change at any time, so don't cache it or put it in a register or do anything crazy like that, okay?"

It does not protect against asynchronous writes, it simply disables optimizations that are invalid if the variable can be changed by external forces.

Edit: As a potential example, one that doesn't involve multithreading (but, does involve exceptionally convoluted code ;), here's a case where volatile is important:

volatile bool keepRunning = true;
void Stuff() {
    int notAPointer = 0;

    notAPointer = (int)(&keepRunning); //Don't do this! Especially on 64-bit processors!

    while(keepRunning) {
        *(bool*)(notAPointer) = false;
    }

    printf("The loop terminated!");
}

Without that volatile modifier, the compiler might go "Hey, keepRunning is never modified, so I don't even need to generate code that checks it!", when in reality we're simply modifying it in secret.

(In reality, this would probably still work on a non-optimized build. And, it might also still work if the compiler is smart and notices the pointer being taken. But, the principle is the same)

Mike Caron
  • 14,351
  • 4
  • 49
  • 77
  • 1
    +1 @Mike - basically what it is doing (from my understanding) is forcing the compiler to generate code such that the value is "looked up" every time it is accessed, so no loop invariant hoisting or anything like that. – pstrjds Nov 12 '10 at 20:30
  • I added an example demonstrating this. – Mike Caron Nov 12 '10 at 20:44
  • 1
    @Pooria: I should hope not :P No one should ever write anything like what I just did :) – Mike Caron Nov 12 '10 at 20:56
  • Now hold on. If the keepRunning is not modified, it is an endless loop, so what is the point of volatile? – BЈовић Nov 12 '10 at 20:59
  • 1
    @VJo keepRunning is modified in the dereferenced notAPointer line in the while loop. The point of volatile is that the compiler in theory could look at the supplied code and determine that keepRunning is not modified and thus not check it for each loop iteration (or check before entering the loop and do nothing thereafter), volatile should be forcing the check of keepRunning for each iteration. – pstrjds Nov 12 '10 at 21:00
  • @VJo: I'm deliberately circumventing the compiler's heuristics. `keepRunning` IS modified, by dereferencing an address that I've stashed away in a regular integer. The compiler, however, can't know that, so it THINKS `keepRunning` isn't modified. `volatile` tells the compiler "I know you know it's never modified, but please trust me on this.". – Mike Caron Nov 12 '10 at 21:06
  • And, actually, it doesn't even work. I couldn't get MSVC to both optimize away the variable check AND indirectly assign to keepRunning, no matter what I did. But, the theory is sound. – Mike Caron Nov 12 '10 at 21:45
  • The rules for MSVC are at http://msdn.microsoft.com/en-us/library/12a04hfd(VS.80).aspx IIRC they are somewhat stricter than what C++ gives you. I don't think you will get MSVC in optimising mode to bother writing to a non-volatile alias, since it can reorder such a write to after the loop terminates, and if you make the alias volatile then the loop will always terminate after one iteration as the write is ordered before the next read. – Pete Kirkham Nov 12 '10 at 22:26
3

Read this. Volatile has nothing to do with multi-threading.

BЈовић
  • 62,405
  • 41
  • 173
  • 273
  • And more: Section 5 of the linked article basically says as much as "Volatile has nothing to do with multi-threaded programming." The article was co-written by the same person who, 3.5 years earlier, wrote "if you have variables of primitive type that must be shared among multiple threads, declare those variables volatile." – John Dibling Nov 12 '10 at 20:36
  • I can only speculate as to Alexandrescu's apparent change of heart, but it would seem that he has changed him mind. It's a good thing too: while I won't go so far as to say it was *bad*, the original advice definitely was misunderstood and gave many programmers a false sense of security when using `volatile`. – John Dibling Nov 12 '10 at 20:38
  • 2
    @John: Andrei took a lot of heat on c.l.c++.m back when when this first article appeared, most of it due to misunderstanding. I suppose that made him say "Volatile has nothing to do with multi-threaded programming" later just to be on the safe side. His original article is still right, but it would be just as right if he had hijacked `volatile` to make the compile tell him when he messed up, say, system-encoded and UTF-8 strings. In so far, his use of `volatile` has nothing to do with threading even in the first article. – sbi Nov 12 '10 at 21:05
2

To follow up on Mike's answer, it's useful in cases like this (global variables used to avoid complexity for this example):

static volatile bool thread_running = true;

static void thread_body() {
    while (thread_running) {
        // ...
    }
}

static void abort_thread() {
    thread_running = false;
}

Depending on how complex thread_body is, the compiler may elect to cache the value of thread_running in a register when the thread begins running, which means it will never notice if the value changes to false. volatile forces the compiler to emit code that will check the actual thread_running variable on every loop.

cdhowie
  • 158,093
  • 24
  • 286
  • 300
  • 1
    @VJo: What do you mean? It doesn't directly have anything do with multithreading, sure, but it **does** have to do with asynchronous access. – Mike Caron Nov 12 '10 at 20:34
  • 1
    @VJo: Did you read the paper? It discusses the insufficience of `volatile` in a multi-threaded environment. However, asnychronous != multi-threaded. `volatile` doesn't have to do with multithreading, it DOES have to do with asynchonous access (which includes things like memory-mapped I/O). – Mike Caron Nov 12 '10 at 20:44
2

I would propose a considerably more strict but very useful rule: if you do not understand exactly what volatile does, do not use it. Instead, use lock. If you do not understand exactly what lock does and how to use it, do not use multithreading.

jason
  • 236,483
  • 35
  • 423
  • 525
  • 1
    Unfortunately locks without `volatile` may cause the compiler to do optimizations that cause your program to miss updates to a properly locked variable. – Mark B Nov 12 '10 at 20:31
2

I would say that in theory those rules are absolutely right, and volatile is needed every time when a variable is accessed by 2 threads. (Even when using mutexes, cause they don't prevent compiler optimizations.) But in practice compilers are good enough at recognizing situations where a variable might be modified outside a particular function so that its value shouldn't be cached in registers. In most cases volatile is not necessary.

I once did some testing in MSVC by inspecting the assembler output for different situations, and all it took to prevent a variable from being cached was to have another function writing to the same variable or taking its address. Global variables were never optimized unless "whole program optimization" was turned on (so the compiler can be sure the variable is not modified in other translation units).

Timo
  • 5,125
  • 3
  • 23
  • 29
  • @Timo_So you tell just declare a variable of any primitive type, don't even declare that volatile then pass it's address to another thread via thread creation API, that variable won't be cached? – Pooria Nov 12 '10 at 21:49
0

Likewise, the C++ standard does not specify how volatile should work, you have to look at what a particular compiler does for a particular platform. Also volatile is a bit subtle and what it does, depends on the compiler and the memory model of the target platform. Locks are way more intuitive to use.

Chris O
  • 5,017
  • 3
  • 35
  • 42
0

Before you take the article you linked to first too seriously, you might want to read another. In a later posting on Usenet, Andrei later admitted that parts of the original article were just plain wrong, and pointed to this one as giving a more realistic view of things (though note that the link he gives to it there seems to have gone stale).

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111