4

While doing concurrent programming I need to tell the compiler/optimizer that it may not cache the value of a variable, that is, it may change at any time. I am currently using the volatile keyword, but I am wondering if this is actually correct?

The standard states that volatile accesses may not be reordered, like IO calls, but I actually don't care about the ordering at all, I care about the contents. Is there anything in the standard that would clarify that a volatile must be loaded every time it is accessed?

More so, in this case I don't even care if it is reordered. I use fences/atomic operations to guarantee any ordering which I need.

Also, in C++0x, will using atomic<T> automatically give this same loading guarantee (if I call load)? Or will I nonetheless have to mark the variable as volatile?

IMPORTANT I'm not interested in locking a section of code. I already use fences to ensure ordering. I'm talking specifically about the access to a single fundamental like int (assume atomic on the platform I'm on). That is, I need to specifically tell the GCC optimizer that variable a should not be cached in any way, so that if used in a loop the appropriate load instruction must be called every single time.


If volatile is not correct, what is the correct way of doing this? I am using GCC and not using C++0x at the moment.

ANSWER: Basically pre C++0x there is nothing to force a reload, and reload may not even be enough on certain architectures. volatile strongly implies that the variable should be reloaded, and will work on many architectures, and while not the correct answer, is the only available option at the moment.


There are already many questions about volatile, but I have not seen one that addresses specifically what I am asking: the proper way to mark a variable for concurrent access.

edA-qa mort-ora-y
  • 30,295
  • 39
  • 137
  • 267

4 Answers4

5

Congratulations on figuring so many details out for yourself. Yes, volatile is not particularly useful for multithreaded programming, and constructs provided by your platform-specific multithreading library (e.g. pthreads) should always be preferred.

Specifically, you should use a read-write lock: an object which can be unlocked for one writer at a time to the exclusion of readers and other writers, or unlocked by multiple readers to the exclusion of any writer. This will be included in any threading API.

C++0x atomic<T> does solve the problem, you should never need volatile unless you are writing a device driver. However atomic is at a lower level and you'll probably be better off with the read-write lock abstraction.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • Is there any way prior to C++0x to properly do what I want? – edA-qa mort-ora-y Apr 07 '11 at 10:49
  • How will pthreads help? I have a specific variable I need the optimizer to assume can change at any moment. – edA-qa mort-ora-y Apr 07 '11 at 10:51
  • 1
    Not portably. Windows (and I think Linux) has some system specific atomic access functions; when I had the problem under Solaris (on a Sparc), I needed some inline assembler. – James Kanze Apr 07 '11 at 10:51
  • "you should never need `volatile` unless you are writing a device driver" - is it not still necessary for asynchronous signal handling? – Tony Delroy Apr 07 '11 at 10:52
  • @edA-qa: You lock the variable to ensure that it *cannot* change. If it can change at any moment, then you can't guarantee its state is valid at all, without an atomic variables API. Atomic variables are sometimes accomplished by pthreads locks, which sends you back to square 1. – Potatoswatter Apr 07 '11 at 10:53
  • @Tony: okay, I forgot that one — but that use case is extremely similar to a driver, as the signal handler looks a lot like an interrupt subroutine. – Potatoswatter Apr 07 '11 at 10:54
  • 1
    @Potatoswatter, no, I'm not interested in locks. The variable is a fundamental that is atomic on my target platforms. I have fences in place to ensure memory ordering, I need only to tell the optimizer to forcibly reload the value each time it accesses it (otherwise it assumes it doesn't change and behaves incorrectly). – edA-qa mort-ora-y Apr 07 '11 at 10:56
  • @edA-qa mort-ora-y: atomic operations themselves will sync the variable between threads before and after doing the updates (at least on AMD/Intel hardware), so no need for you to have fences/memory-barriers/whatever. `volatile` sounds useful to me though, it's just not needed when you're using posix mutexes. – Tony Delroy Apr 07 '11 at 10:56
  • @Tony, okay, `atomic` and C++0x atomics may well do this, but how can I do this without using C++0x. I use GCC and there is no `__sync_load` function. – edA-qa mort-ora-y Apr 07 '11 at 10:58
  • @edA-qa: If you're sure everything else is in order, then yes, `volatile` does require that the CPU load the variable from memory and not registers. If you already rely that it's atomic on your platform, then you've consciously limited portability and worrying about the Standard way of doing it is pointless. – Potatoswatter Apr 07 '11 at 10:59
  • @Potatoswatter: "Atomic variables are sometimes accomplished by pthreads locks, which sends you back to square 1" - it's the other way around, pthread locks (i.e. mutexes) are typically implemented using an atomic compare-and-swap, some level of spinning, then hand-off to the kernel for rescheduling if it spins out.... – Tony Delroy Apr 07 '11 at 11:01
  • @Potatoswatter, does the standard specifcally state that volatile must be reloaded? About compatibility, C++0x does specifically say some variables are atomic, so while I may be limiting portability, I'm certainly not intending on breaking the standard. – edA-qa mort-ora-y Apr 07 '11 at 11:02
  • @edA-qa: There's no `__unsync_twist` function either ;-(. As Potatoswatter says, atomic operations are outside the pre-C++0x spec, the only C++-specific part of the question is that `volatile` promises to read/write to memory each time and not use registers, but that memory can of course be core-specific cache, so a synchronisation still requires an atomic operation or a memory barrier pre read. I.e. volatile certainly does **not** force an inter-core cache sync. – Tony Delroy Apr 07 '11 at 11:04
  • @edA-qa: The relevant section of the standard (§1.9) is notoriously vague, and was completely rewritten for C++0x. However, the spirit is captured well by informative note §7.1.5.1/8: "[Note: volatile is a hint to the implementation to avoid aggressive optimization involving the object because the value of the object might be changed by means undetectable by an implementation. See 1.9 for detailed semantics. In general, the semantics of volatile are intended to be the same in C++ as they are in C. ]" – Potatoswatter Apr 07 '11 at 11:08
  • I think we're in agreement, pre C++0x `atomic` I have nothing standard to help me. I just label it `volatile` and hope for the best. – edA-qa mort-ora-y Apr 07 '11 at 11:11
1

No, volatile has nothing to do with threads.

EDIT

volatile is useless for multithreaded programming. It does not provide any synchronization, it does not create memory fences, nor does it ensure the order of execution of operations. It does not make operations atomic. It does not make your code magically thread safe. volatile may be the single-most misunderstood facility in all of C++. See this, this and this for more information about volatile

On the other hand, volatile does have some use that may not be so obvious. It can be used much in the same way one would use const to help the compiler show you where you might be making a mistake in accessing some shared resource in a non-protected way. This use is discussed by Alexandrescu in this article.

volatile is specifically used for is interfacing with memory-mapped hardware, signal handlers and the setjmp machine code instruction.

What to use for concurrent access?

You actually need some mechanism that was developed specifically for that purpose. For example mutexes, and semaphores.

Community
  • 1
  • 1
BЈовић
  • 62,405
  • 41
  • 173
  • 273
0

As far as I understand, prior to atomic<T>, there is no standard way 'to mark a variable for concurrent access' in C++. Instead, you must use platform-specific functions to achieve the results you want, like the InterlockedXXX functions on Windows for example.

Kylotan
  • 18,290
  • 7
  • 46
  • 74
0

No, volatile isn't intended for concurrent use and is not suitable for making it concurrency-safe at all. Volatile will prevent the compiler from caching the variable, but this is not enough to make it thread-safe. Most platforms come with concurrency extensions, but C++03 does not have anything about it in it's Standard.

If you are using atomic operations, you don't need volatile. As far as I know, for both MSVC and GCC, atomic operations compile to specific instructions and they will not cache atomically accessed variables. If you look at the source code for MSVC's std::shared_ptr, they do not mark the reference counts as volatile, but instead just use atomic operations.

Puppy
  • 144,682
  • 38
  • 256
  • 465