Volatile Violates its main job?
Asked Answered
M

3

24

According to MSDN:

The volatile keyword indicates that a field might be modified by multiple threads that are executing at the same time. Fields that are declared volatile are not subject to compiler optimizations that assume access by a single thread. This ensures that the most up-to-date value is present in the field at all times.

Please notice the last sentence:

This ensures that the most up-to-date value is present in the field at all times.

However, there's a problem with this keyword.

I've read that it can change order of instructions:

First instruction       Second instruction         Can they be swapped?
Read                         Read                         No
Read                         Write                        No
Write                       Write                         No 
Write                       Read                          Yes! <----

This means John sets a value to a volatile field, and later Paul wants to read the field, Paul is getting the old value!

What is going here ? Isn't that it's main job ?

I know there are other solutions, but my question is about the volatile keyword.

Should I (as a programmer) need to prevent using this keyword - because of such weird behavior ?

Massachusetts answered 17/5, 2012 at 7:34 Comment(7)
@Corbin press on the ive read link .Massachusetts
Ah ok. That makes sense then. One second and I'll elaborate. Edit: Ok I take that back. It makes sense in my head, but I can't explain it >.<.Trinee
I've been staring at the standard for the past 10 minutes trying to decide if this is aligned with the standard or not. As MS was implementing a standard they wrote, I suppose it is, however, the language is vague at best. For anyone intersted, it's section 10.5.3 of go.microsoft.com/fwlink/?LinkId=199552 Edit: section 3.10 is also of interest.Trinee
Eric Lippert has a nice article on the subject (as usual), as does Joe Duffy.Noheminoil
@Noheminoil it doesnt talk about that particular case of ordering in statements.Massachusetts
Another Joe Duffy post that discusses this is here bluebytesoftware.com/blog/… In particular, he outlines what can and cannot be rearranged. Basically a text form of the table posted, but I like his explanation of "Note that RELEASe is the opposite of ACQUIRE: you can think of an acquire as a one-way fence that prohibits passes downward, and a release as a one-way fence that prohibits passes upward."Trinee
@Trinee I think he has a mistake. it should be acquire as a one-way fence that prohibits passes upward, and a release as a one-way fence that prohibits passes downward.Massachusetts
W
7

The MSDN documentation is wrong. That is most certainly not what volatile does. The C# specification tells you exactly what volatile does and getting a "fresh read" or a "committed write" is not one of them. The specification is correct. volatile only guarantees acquire-fences on reads and release-fences on writes. These are defined as below.

  • acquire-fence: A memory barrier in which other reads and writes are not allowed to move before the fence.
  • release-fence: A memory barrier in which other reads and writes are not allowed to move after the fence.

I will try to explain the table using my arrow notation. A ↓ arrow will mark a volatile read and a ↑ arrow will mark a volatile write. No instruction can move through the arrowhead. Think of the arrowhead as pushing everything away.

In the following analysis I will use to variables; x and y. I will also assume that they are marked as volatile.

Case #1

Notice how the placement of the arrow after the read of x prevents the read of y from moving up. Also notice that the volatility of y is irrelevant in this case.

var localx = x;
↓
var localy = y;
↓

Case #2

Notice how the placement of the arrow after the read of x prevents the write to y from moving up. Also notice that the volatility of either of x or y, but not both, could have been omitted in this case.

var localx = x;
↓
↑
y = 1;

Case #3

Notice how the placement of the arrow before the write to y prevents the write to x from moving down. Notice that the volatility of x is irrelevant in this case.

↑
x = 1;
↑
y = 2;

Case #4

Notice that there is no barrier between the write to x and the read of y. Because of this the either the write to x can float down or the read of y can float up. Either movement is valid. This is why the instructions in the write-read case can be swapped.

↑
x = 1;
var localy = y;
↓

Notable Mentions

It is also important to note that:

  • x86 hardware has volatile semantics on writes.
  • Microsoft's implementation of the CLI (and suspect Mono's as well) has volatile semantics on writes.
  • The ECMA specification does not have volatile semantics on writes.
Westfalen answered 17/5, 2012 at 14:5 Comment(8)
i know, see my question about this topic #10590154Massachusetts
hhh you answered it already there.....lol. i still need to check some more knowledge before i check an answer ....( for the link question)Massachusetts
@RoyiNamir: Take a look at this question where I analyze the double-checked locking pattern using the arrow notation I came up with. The arrow notation really helps when trying to visualize what can and cannot happen.Westfalen
Listen man , you've helped me a lot. great detailed answer. you are trully a Thread Expert.Massachusetts
after reading some articles and seeing images about this topic - i think the read should have its arrow up ( the head tells that instructions from down cant go up - so the head is blocking.)....the write should have its arrow down - so it shows that prev instructions cant go down.... techpubs.sgi.com/library/tpl/cgi-bin/…Massachusetts
No, that would not be right. I use the arrows to show what cannot happen. The link you provided uses them to show what can happen. We both agree on principals though...which is good :)Westfalen
Are those reads/writes regarding to read/writes from the same thread or from other threads too ...?Massachusetts
@RoyiNamir: It is from the perspective of other threads. The same thread will always perceive reads/writes in the order they were entered in the program code...otherwise no program would work correctly. Good question.Westfalen
I
18

Well you are right. It is more elaborated in Joseph Albahari threading book/article.

The MSDN documentation states that use of the volatile keyword ensures that the most up-to-date value is present in the field at all times. This is incorrect, since as we’ve seen, a write followed by a read can be reordered.

http://www.albahari.com/threading/part4.aspx#_The_volatile_keyword

Should I ( as a programmer ) need to prevent using this keyword-because of such weird behavior?

It should be used only after knowing this weired behavior. It should not be used as a Magic keyword to retrieve latest values all the time in multithreaded environment.

IMO, usage of volatile keyword should be avoided as probable bugs are hard to find out.

Imponderable answered 17/5, 2012 at 7:41 Comment(2)
why doesnt msn write this weird behaviour ? why do i need to hear it from joseph ?Massachusetts
I dont know. MSDN is factually incorrect at various places. I can see the user comments pointing out that. In the Joseph's article, the conclusion is backed by suitable example which you can try out yourself.Imponderable
W
7

The MSDN documentation is wrong. That is most certainly not what volatile does. The C# specification tells you exactly what volatile does and getting a "fresh read" or a "committed write" is not one of them. The specification is correct. volatile only guarantees acquire-fences on reads and release-fences on writes. These are defined as below.

  • acquire-fence: A memory barrier in which other reads and writes are not allowed to move before the fence.
  • release-fence: A memory barrier in which other reads and writes are not allowed to move after the fence.

I will try to explain the table using my arrow notation. A ↓ arrow will mark a volatile read and a ↑ arrow will mark a volatile write. No instruction can move through the arrowhead. Think of the arrowhead as pushing everything away.

In the following analysis I will use to variables; x and y. I will also assume that they are marked as volatile.

Case #1

Notice how the placement of the arrow after the read of x prevents the read of y from moving up. Also notice that the volatility of y is irrelevant in this case.

var localx = x;
↓
var localy = y;
↓

Case #2

Notice how the placement of the arrow after the read of x prevents the write to y from moving up. Also notice that the volatility of either of x or y, but not both, could have been omitted in this case.

var localx = x;
↓
↑
y = 1;

Case #3

Notice how the placement of the arrow before the write to y prevents the write to x from moving down. Notice that the volatility of x is irrelevant in this case.

↑
x = 1;
↑
y = 2;

Case #4

Notice that there is no barrier between the write to x and the read of y. Because of this the either the write to x can float down or the read of y can float up. Either movement is valid. This is why the instructions in the write-read case can be swapped.

↑
x = 1;
var localy = y;
↓

Notable Mentions

It is also important to note that:

  • x86 hardware has volatile semantics on writes.
  • Microsoft's implementation of the CLI (and suspect Mono's as well) has volatile semantics on writes.
  • The ECMA specification does not have volatile semantics on writes.
Westfalen answered 17/5, 2012 at 14:5 Comment(8)
i know, see my question about this topic #10590154Massachusetts
hhh you answered it already there.....lol. i still need to check some more knowledge before i check an answer ....( for the link question)Massachusetts
@RoyiNamir: Take a look at this question where I analyze the double-checked locking pattern using the arrow notation I came up with. The arrow notation really helps when trying to visualize what can and cannot happen.Westfalen
Listen man , you've helped me a lot. great detailed answer. you are trully a Thread Expert.Massachusetts
after reading some articles and seeing images about this topic - i think the read should have its arrow up ( the head tells that instructions from down cant go up - so the head is blocking.)....the write should have its arrow down - so it shows that prev instructions cant go down.... techpubs.sgi.com/library/tpl/cgi-bin/…Massachusetts
No, that would not be right. I use the arrows to show what cannot happen. The link you provided uses them to show what can happen. We both agree on principals though...which is good :)Westfalen
Are those reads/writes regarding to read/writes from the same thread or from other threads too ...?Massachusetts
@RoyiNamir: It is from the perspective of other threads. The same thread will always perceive reads/writes in the order they were entered in the program code...otherwise no program would work correctly. Good question.Westfalen
S
4

Another point raised by Joseph Albahari is that process architecture can adversely impact volatile, i.e. AMD in particular can cause values to be swapped.

Since you will probably have no idea what system type your application will run on in production its best to always avoid the volatile keyword.

Also slightly off topic you should always avoid the ReaderWriterLock class as under heavy load on multiprocessor systems this can allow multiple write locks please see here to be taken simultaneously which will cause application hangs which will be extremely difficult to root cause

Shambles answered 17/5, 2012 at 8:8 Comment(1)
ReaderWriterLockSlim is even mentioned in the post you linked, probably best to mention that here, too.Tittup

© 2022 - 2024 — McMap. All rights reserved.