Volatile is a keyword that allows you to turn off certain optimizations. Unfortunately, it’s also used incorrectly many times as a way to synchronize threads.
If you have code that checks a variable for a specific value and you know that the value can be changed through some means that your compiler is not aware of, then declaring the variable to be volatile is perfect. That’s what it’s intended for.
Where some developers run into problem though is when trying to use this technique to synchronize the activities between threads. Maybe by creating a variable called done that will be set to true once other variable have been set properly. There are many problems with this that this episode describes.
My advice is that while volatile can be useful sometimes, don’t try to use it for any type of thread synchronization. There’s just too many ways it can go wrong. Take the time to use proper thread locking and synchronization methods. Listen to the full episode for more or you can also read the full transcript below.
The dictionary lists several meanings for volatile including a tendency to erupt into violence. That’s not the definition for us. The proper meaning for volatile as it applies to programming is when something has a tendency to change unexpectedly.
The exact meaning of volatile gets a little more complicated and it’s worse because different languages have slightly different rules. And even the same language can lead to different behavior if your code needs to be portable across different compilers.
The original intention of volatile is simple. Let’s say that you have a program that reads a value from memory but never changes the value itself. The memory value can be changed by some hardware device, maybe some kind of extension card that you installed in your computer.
Here’s where you can run into problems. Your compiler will look at your code and try to find places where it can do things better, or simpler, or faster. These are called optimizations and in this case, the compiler will notice that since you never change the value anywhere in your code, then the value must not change. So even though you read the value many times, the compiler may change your code to only read the value once and then just use that same value again and again.
We do the same thing in real life. How many times do you actually look up the number for your favorite TV station? Probably never. I know that I just type in the same number that I’m already familiar with. How many times do you actually read each speed limit sign on roads that you drive on every day? It never changes and you just remember what it’s always been, right? It would be very difficult to get halfway through our days if we had to constantly double check everything.
So imagine now what a compiler goes through as it analyzes your code. It has the ability to prove that your code never changes a variable. This is more than just a shortcut. This is proof. It’s no wonder then that the compiler chooses to change your code.
It’s up to you to let the compiler know that a variable can change beyond its ability to detect. And you do that by marking the variable as volatile.
This actually gives you three benefits:
◦ The compiler now knows that the content of a volatile variable is unstable and can change at any time so must not cache the value. It will read the value fresh each time it’s used.
◦ Each write to a volatile variable is observable and must be maintained. Just because the compiler sees no reason to write the same value many times doesn’t mean that this can be removed from your code. There could be something beyond your application that does get value from the multiple writes.
◦ And all operations done with a volatile variable must be done in the same order as in the source code. This does not help maintain the order between volatile and non-volatile variables. But at least the volatile operations will keep their order.
That’s it. But because some platforms and languages promise more and because there’s a lot of documentation out there that says you can do more, many developers think volatile is a great way to handle thread synchronization too. I’ll help dispel those stories and set things straight right after this message from our sponsor.
( Message from Sponsor )
First of all, let me say that while volatile might seems simple, it’s not. Before using it, please research more on your own for your particular language and platform.
Many languages have the concept of volatile and some such as Java and .Net add an additional concept called acquire/release which means this:
◦ Any read of a volatile variable must be done before any other variables are accessed in the code that follows.
◦ And any write to a volatile variable must be done after all previous variables have been accessed.
When volatile obeys the additional constraints of acquire/release, then it does help with the ordering of operations between volatile and non-volatile variables. But even this is not always enough.
Before we get too far though, what do I mean about ordering read and writes? Shouldn’t the compiler keep things in the same order that you wrote them in your code?
Compilers usually have a rule that they’re allowed to change things around as long as the end result is the same as if you had wrote the code that way.
Let’s say you go to a fast food place and they’re out of medium cups. Instead, though, the person taking your order gives you a large cup and explains that it contains the same amount as the medium drink you ordered. Since the size of the cup is really secondary to how much liquid you get, this is acceptable.
Sometimes this can cause problems especially with multiple threads. Let’s say that you heard that locks can sometimes be slow and decided to write a critical routine without locks so it’ll be faster. You define a boolean variable called done and set it to false initially. Then in your code, you make sure that your first update the money available in the bank account balance before setting done to true. The idea is that another thread could be waiting for done to be true before making sure you have enough money available for a large purchase. The compiler is not aware of this other thread though and sees no reason why your code can’t be rearranged a bit. It ends up setting done to true and then goes to work on the balance. The other thread spots the done signal right away and reads the old balance.
You might then try to declare the done variable as volatile but unless your compiler guarantees acquire/release semantics, it can still end up rearranging the done and the balance because balance is not volatile.
Okay, so you try making balance volatile too. And this time, the compiler just ignores you. What? How can it ignore what you specifically wrote? Well there are rules regarding what types of variables can and cannot be volatile and maybe the currency type that you’re using for the balance can’t be volatile.
Let’s say though that your compiler recognizes acquire/release and guarantees that the write to the done variable will occur last. You could still end up with problems. And these are the types of problems that will be extremely hard to find and figure out. Maybe the application is running on a multiprocessor system and the done variable and the balance variable are in different cache lines. While your compiler might guarantee that they get written in the correct order, the hardware might have it’s own ideas and decide that it’s more efficient to propagate the done cache first before updating the other processors about the new balance.
My advice is that while volatile can be useful sometimes, don’t try to use it for any type of thread synchronization. There’s just too many ways it can go wrong. Take the time to use proper thread locking and synchronization methods. If you don’t, then you might find that the dictionary was right about the tendency to cause violence. People can get upset when their available balance is wrong.