none
Is volatile dangerous? RRS feed

  • Question

  • Hi!

    I read some time ago that use 'volatile' in variables can be dangerous if there is serveral CPUs with unconnected caches, but I don't remember the source blog or article... and again, I'm thinking about this issue... and don't understand it.

    The question is, why:

    Code Snippet

    Object locker= new Object();
    lock(locker)
    {
      // do some stuff
    }



    and not:

    Code Snippet

    volatile Boolean locker;

    if(!locker)
    {
      try
      {
         locker = true;
         // do some stuff
      }
      finally
      {
        locker = false;
      }
    }




    What is the risk with 'volatile' ?

    Cheers
    Monday, February 25, 2008 12:03 AM

Answers

  • The Volatile modifier isn't dangerous, but it's not a substitution for lock-semantic synchronization. The second piece of code is absolutely useless because on a single-CPU machine it doesn't do anything meaningful, and on a multi-threaded CPU it actually breaks any lock you are trying to setup.

     

    For starters, let's look at what Volatile actually does - it's just a modifier in the IL that tells the JIT to never cache the value of the field. That field must be read from normal memory every time. In some cases, the JIT tries to optimze value reads by caching a field value (for example, quite commonly cached in a register, which is specific to a thread since it's set up in the thread's stack frame), but Volatile tells the JIT not to do this. This is because the field value is EXPECTED to change frequently by multiple concurrent threads - so the code is forced to read the most current value (in normal memory, not cache) regardless of which thread may have changed it. In the second example, that means that locker could be true on the test, but then changed by another thread immediately after the test, or any other permutation of scenarios. Volatile is NEVER to be used as a synchronization mechanism like this.

     

    The first example is a normal form of read-write lock synchronization. The two peices of code are not doing anything similar.

    Monday, February 25, 2008 10:15 PM

All replies

  • I tried it in a program with a dual core processor and the second one fails sometimes.

    I did several test with volatile reads and the fails are more frequent  than when I use the 'volatile' keyword instead a Thread.VolatileRead...

    I don't understand what is the purpose of volatile keyword in practice.

    Cheers.
    Monday, February 25, 2008 2:03 AM
  • Hi!

     

    The main difference between two code blocks is that you try to change locker reference within code block.

    c# compiler generates two lines of code Monitor.Enter and Monitor.Exit when you write lock statement. These static methods operates with reference fields, therefore if you change reference within lock statement, your "lock" will never be released.

    Monday, February 25, 2008 9:26 AM
  • Ermm.... I don't think that that be the main difference between these two blocks.

    I'm speaking about memory access and synchronization, the second one has a problem with the word volatile... but I not remember it.

    Regards.
    Monday, February 25, 2008 1:42 PM
  • The Volatile modifier isn't dangerous, but it's not a substitution for lock-semantic synchronization. The second piece of code is absolutely useless because on a single-CPU machine it doesn't do anything meaningful, and on a multi-threaded CPU it actually breaks any lock you are trying to setup.

     

    For starters, let's look at what Volatile actually does - it's just a modifier in the IL that tells the JIT to never cache the value of the field. That field must be read from normal memory every time. In some cases, the JIT tries to optimze value reads by caching a field value (for example, quite commonly cached in a register, which is specific to a thread since it's set up in the thread's stack frame), but Volatile tells the JIT not to do this. This is because the field value is EXPECTED to change frequently by multiple concurrent threads - so the code is forced to read the most current value (in normal memory, not cache) regardless of which thread may have changed it. In the second example, that means that locker could be true on the test, but then changed by another thread immediately after the test, or any other permutation of scenarios. Volatile is NEVER to be used as a synchronization mechanism like this.

     

    The first example is a normal form of read-write lock synchronization. The two peices of code are not doing anything similar.

    Monday, February 25, 2008 10:15 PM
  • Thanks!
    Saturday, March 1, 2008 2:48 AM
  •  vtortola wrote:
    Hi!

    I read some time ago that use 'volatile' in variables can be dangerous if there is serveral CPUs with unconnected caches, but I don't remember the source blog or article... and again, I'm thinking about this issue... and don't understand it.

    The question is, why:

    Code Snippet

    Object locker= new Object();
    lock(locker)
    {
      // do some stuff
    }



    and not:

    Code Snippet

    volatile Boolean locker;

    if(!locker)
    {
      try
      {
         locker = true;
         // do some stuff
      }
      finally
      {
        locker = false;
      }
    }




    What is the risk with 'volatile' ?

    Cheers


    Volatile can be dangerous in my view, but only because people sometimes misunderstand it and use it in ways that it is not intended for. As explained above, "volatile" forces the compiler(s) to never cache a value in a register or temporary, but always read/write directly from its memory location. Volatile ensures a consistent view of data even when one has asynchronous updates ocurring (i.e. other threads, interrupts or exceptions).

    This has little to do with locking, it is simply doing what one intuitively expects, namely storing the data in one place only.

    There is more to this though, as you mentioned "caches" can cloud the issue, and they do.

    We had a lengthy back and forth with various Microsoft techies, trying to get definitive rules for all this and it was not straightforward.

    Modern processors will retain stuff in their local cache, if you have a single processor this is no big deal, because ONLY that processor (irrespective of threads, interrupts etc) will ever access that data. But on machines with > 1 processor, it can get somewhat involved.

    This is why you see MemoryBarrier, ReadBarrier and WriteBarrier, all associated with this caching issue. Furthemore different processors obey different "models" for maintaining cache coherency, and this is also somewhat involved.

    So far as locking is concerned, ultimately these are implemented using "Interlock" functions in the OS. Different OSs and different versions (32-bit v 64-bit) support different subsets of an API, for example the function "InterlockedIncrement64" is NOT available on XP (even 64-bit XP).

    Basically fully understanding these issues and designing a system to work reliably given these mechanisms, is non-trivial. To fully test locking code one MUST heavily stress that code on machines with multiple CPUs, and this is also far from clear if one is testing with VMware, even if one has enabled two CPUs, all in all we never did get 100% clarlity from Microsoft on these issues, all I do know is that their OS itself and its device drivers etc all work in practice, so it is possible to deal with all these, it's just not documented as well as it needs to be in my opinion.

    Hugh

    Monday, March 3, 2008 2:50 PM