Jump to content
  • Advertisement
Sign in to follow this  
Molle85

[C++] Volatile variables

This topic is 3710 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, I stumbled upon one of LusikkaMage's tutorials on YouTube (
) and just a few seconds in she declared the variable: volatile long speedCounter = 0; Myself have never used or seen for that matter the volatile keyword... I looked it up and it seemed to me that volatile was making the variable accessible to the entire system. Isn't this "dangerous" declaring a variable on the stack as volatile ?

Share this post


Link to post
Share on other sites
Advertisement
Quote:
Original post by Molle85
I looked it up and it seemed to me that volatile was making the variable accessible to the entire system.

Isn't this "dangerous" declaring a variable on the stack as volatile ?
The volatile keyword doesn't make the variable accessible from the entire system, it tells the compiler that the variable can change at any time (E.g. it's used by more than one thread and the other thread could get context-switched in and change it at any time, or it's mapped to a hardware register that's updated by interrupts or something else). That means that whenever you use the variable in your code, the compiler will read from the address containing it - usually the compiler would read it once and then keep it handy which can be slightly more performant.

There's no problems with making a volatile variable on the stack, in fact there's no problem with making all of your variables volatile (Although that'll destroy the compilers optimisation).

Share this post


Link to post
Share on other sites
Okey, so volatile is mostly used when dealing with shared variables between threads.

I presume Allegro is threaded since LusikkaMage got it from a tutorial on Loomsoft.

Thnx.

Share this post


Link to post
Share on other sites
Quote:
Original post by Molle85
Okey, so volatile is mostly used when dealing with shared variables between threads.



Not usually. It is on Windows using Visual C++ 2003 or newer. But with older versions of Visual C++ or on other compilers / platforms, volatile may not necessarily be sufficient to synchronize access to variables shared across threads. Volatile was originally invented to mark variables whose values could be changed unexpectedly by external devices, not to synchronize access to thread-shared variables.

IMO the C++ standard says nothing about volatile being suitable for this task (probably because the standard doesn't really say much or anything about threads in the first place), which is why what exactly volatile really does is pretty much implementation-specific ... so if you care about portable code, don't rely on volatile to act like a mutex.

Share this post


Link to post
Share on other sites
Usually I use 'volatile' when I want to run the app in optimized mode, but still be able to debug-watch the values of a handful of variables. I put 'volatile' on those variables, and it typically guarantees that the compiler won't optimize the variable into registers - this means the debugger will be able to watch their value.

I know... I'm completely abusing the keyword, but it works great! :)

Share this post


Link to post
Share on other sites
Quote:
Original post by Red Ant
Quote:
Original post by Molle85
Okey, so volatile is mostly used when dealing with shared variables between threads.



Not usually. It is on Windows using Visual C++ 2003 or newer. But with older versions of Visual C++ or on other compilers / platforms, volatile may not necessarily be sufficient to synchronize access to variables shared across threads. Volatile was originally invented to mark variables whose values could be changed unexpectedly by external devices, not to synchronize access to thread-shared variables.

Huh? No compiler I'm aware of guarantees that volatile is *sufficient* to synchronize access, before or after VC2003.

But the OP is correct, it is mostly used when dealing with shared variables. It's generally not sufficient to mark the variable as volatile, but it's one of the tools you tend to use in that situation.

Share this post


Link to post
Share on other sites
As I've recently found out in another discussion about volatile (also on this forum), in Visual C++ 2003 or newer, making an integral variable volatile means you can safely access it from several threads without having to worry that its value might get corrupted. Check this Visual C++ 2005 description of the volatile keyword: http://msdn.microsoft.com/en-us/library/12a04hfd(VS.80).aspx

Quote:

[...]This allows volatile objects to be used for memory locks and releases in multithreaded applications.


Then navigate to the 2003 version and notice how it doesn't say anything like that. So either this was changed from 2003 to 2005, or the older description simply omitted that detail.

(So .... sorry, I got the version wrong ... should be Visual C++ 2005 or newer then, not 2003).

However, except for the most trivial cases, I'd recommend using proper synchronization constructs instead (CRITICAL_SECTION, boost::mutex or whatever), in which case volatile isn't necessary any more anyway.

Share this post


Link to post
Share on other sites
Quote:
Original post by Spoonbender
Quote:
Original post by Red Ant
Quote:
Original post by Molle85
Okey, so volatile is mostly used when dealing with shared variables between threads.
Not usually. It is on Windows using Visual C++ 2003 or newer. But with older versions of Visual C++ or on other compilers / platforms, volatile may not necessarily be sufficient to synchronize access to variables shared across threads. Volatile was originally invented to mark variables whose values could be changed unexpectedly by external devices, not to synchronize access to thread-shared variables.

Huh? No compiler I'm aware of guarantees that volatile is *sufficient* to synchronize access, before or after VC2003.

But the OP is correct, it is mostly used when dealing with shared variables. It's generally not sufficient to mark the variable as volatile, but it's one of the tools you tend to use in that situation.

They are a bad tool for that.

Consider some details about volatile variables:
* Volatile variables *always* suppress optimization.
* Volatile variables *always* incur a penalty when accessed.
* Volatile variables *always* incur a penalty when modified. That penalty is at least as large as other memory coherency locks on multi-core machines. Note that it is *memory* coherency and not *cache* coherency, since they shouldn't even be cached.
* Volatile variables are not inherently atomic, and have no concurrency guarantees.

Next, consider semaphores:
* Semaphores do not suppress optimization.
* Semaphores (system specific) generally incur no penalty to read.
* Semaphores (system specific) generally incur no more penalty to write than cache coherency. (not regular *memory* coherency)
* Semaphores are inherently atomic, and offer some (system specific) concurrency guarantees.

Unfortunately, only a few systems allow you to use semaphores for arbitrary data. In that case, you could consider a mutex:
* Mutex performance is exactly the same as semaphore, that is:
* * Mutex do not suppress optimization.
* * Mutex (system specific) generally incur no penalty to query.
* * Mutex (system specific) generally incur no more penalty to lock than cache coherency.
* * All access inside a mutex is considered atomic within that block.
As an additional benefit:
* Mutex provides locked access to a range of code rather than a specific value.

Share this post


Link to post
Share on other sites
volatile and mutex/semahpores are two completely different things.

please read!
http://msdn.microsoft.com/en-us/library/12a04hfd.aspx
http://en.wikipedia.org/wiki/Mutex
http://en.wikipedia.org/wiki/Semaphore_(programming)


volatile variables can be used to implement mutexes and semaphores similar to:

static volatile bool lockVariable = false;

// this is called a spinlock and it will use up your cpu
// and will fail if there are with two threads waiting for the lock
void lock() { while(lockVariable); lockVariable = true; }

void release() { lockVariable = false; }

Without volatile the lock function could be optimized into something similar:
void lock() {
register bool r0 = lockVariable;
while(r0);
lockVariable = true;
}

and then you easily get an infinite loop.


for writing a spin lock for several threads without making a rely complicated algorithm you need the compiler to support atomic operations then it would look something like this.

// this still will use a lot of cpu while waiting for lock
// test_and_set_if_false is assumed to be atomic
void lock() { while(!test_and_set_if_false(lockVariable, true)); }

most efficient is however to write your code without locks

Share this post


Link to post
Share on other sites
Quote:
Original post by mzeo77
volatile and mutex/semahpores are two completely different things.

...

static volatile bool lockVariable = false;

// this is called a spinlock and it will use up your cpu
// and will fail if there are with two threads waiting for the lock
void lock() { while(lockVariable); lockVariable = true; }
void release() { lockVariable = false; }
There are at least two issues with that. First is the issue of performance, the second is the issue of concurrency.

On the issue of performance, a spinlock is horribly inefficient except in very specific limited situations.

As I mentioned above, there can be a (system dependent) big performance penalty for the volatile variable that is not present with other IPC locking mechanisms. That is because (again, system dependent) volatile variables are not cached at all. They require a full trip out to memory and back. IPC locks on a single-core multithreaded environment can be implemented without any locking, and on a multi-core environment can be implemented with the minimal cost of an internal cache coherency update.


Next is the matter of concurrency. That implementation is fatally flawed. A spinlock implemented with volatile variables requires multiple locks: one to indicate locking and at least one more to indicate locking of that lock.

Also, unless you have a variable that is guaranteed by your system to have atomic operations, you can have other difficulties. Observe the spinlock implementation in the Linux kernel for some of the complexity required, and search the kernel mailing list for many lengthy arguments about if a spinlock is appropriate in a given situation.

Your code still suffers from the non-atomic nature of the code, the fact that instructions can be re-ordered by both the compiler and the hardware (so additional operations take place between the while() and the assignment), and the fact that there is no inherently atomic check-and-set operation in the language. Similarly, the length of each cpu pipeline can be an issue.

Imagine you have two threads waiting for the lock. It is possible for one of them to check the variable and see that it is false. It is then either preempted by another thread or concurrently checked on another processor, or a lock is being processed inside the instruction pipline of another cpu. The second thread also checks the value and sees that it is false. Now BOTH THREADS lock the variable and assume that they have exclusivity.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!