When is "volatile" used instead of "lock" ?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Willy Denoyette [MVP]

    #16
    Re: When is "volatile& quot; used instead of "lock&quot ; ?

    "Ben Voigt" <rbv@nospam.nos pamwrote in message
    news:%23MmyV$gn HHA.4196@TK2MSF TNGP06.phx.gbl. ..
    >
    "Willy Denoyette [MVP]" <willy.denoyett e@telenet.bewro te in message
    news:BFF370C9-379B-4B12-A00F-CF8195C64E1E@mi crosoft.com...
    >"Ben Voigt" <rbv@nospam.nos pamwrote in message
    >news:uXg8juTnH HA.2296@TK2MSFT NGP03.phx.gbl.. .
    >>>
    >>"Willy Denoyette [MVP]" <willy.denoyett e@telenet.bewro te in message
    >>news:1B1A6E 75-67F7-4B6E-B858-C7517C5BEA43@mi crosoft.com...
    >>>"Ben Voigt" <rbv@nospam.nos pamwrote in message
    >>>news:uln5JOM nHHA.3968@TK2MS FTNGP06.phx.gbl ...
    >>>>>
    >>>><ben.biddin gton@gmail.comw rote in message
    >>>>news:117976 1300.206885.512 30@z28g2000prd. googlegroups.co m...
    >>>>>On May 21, 3:35 pm, Samuel R. Neff <samueln...@nom ail.comwrote:
    >>>>>>When is it appropriate to use "volatile" keyword? The docs simply
    >>>>>>state:
    >>>>>>>
    >>>>>>"
    >>>>>>The volatile modifier is usually used for a field that is accessed
    >>>>>>by
    >>>>>>multipl e threads without using the lock Statement (C# Reference)
    >>>>>>stateme nt to serialize access.
    >>>>>>"
    >>>>>>>
    >>>>>>But when is it better to use "volatile" instead of "lock" ?
    >>>>>>>
    >>>>>>Thanks,
    >>>>>>>
    >>>>>>Sam
    >>>>>>>
    >>>>>>------------------------------------------------------------
    >>>>>>We're hiring! B-Line Medical is seeking .NET
    >>>>>>Developer s for exciting positions in medical product
    >>>>>>developme nt in MD/DC. Work with a variety of technologies
    >>>>>>in a relaxed team environment. See ads on Dice.com.
    >>>>>>
    >>>>>You can also the System.Threadin g.Interlocked class which maintains
    >>>>>volatile semantics.
    >>>>>
    >>>>You should use volatile and Interlocked together, neither fully
    >>>>replaces the other.
    >>>>>
    >>>>
    >>>Not necessarily, there is no need for volatile, as long you Interlock
    >>>consistent ly across all threads in the process. This means that once
    >>>you access a shared variable using Interlock, all threads should use
    >>>Interlock.
    >>>
    >>I don't think so, actually. Without volatile semantics, the compiler is
    >>free to cache the value of any parameter, including in/out parameters.
    >>Say you are calling an Interlocked method in a loop. If the variable is
    >>not volatile, the compiler can actually call Interlocked on a local
    >>copy, and then write the value to the real variable once, at the end of
    >>the loop (and worse, it can do so in a non-atomic way). Anything that
    >>maintains correct operation from the perspective of the calling thread
    >>is permissible for non-volatile variable access. Why would a compiler
    >>do this? For optimal use of cache. By using a local copy of a variable
    >>passed byref, locality of reference is improved, and additionally, a
    >>thread's stack (almost) never incurs cache coherency costs.
    >>>
    >>Note that this is not a problem for pass-by-pointer, which must use the
    >>true address of the referenced variable in order to enable pointer
    >>arithmetic. But pointer arithmetic isn't allowed for tracking handles, a
    >>handle is an opaque value anyway.
    >>>
    >>For lockless data structures, always use volatile. And then stick that
    >>volatile variable close in memory to what it is protecting, because CPU
    >>cache has to load and flush an entire cache line at once, and volatile
    >>write semantics require flushing all pending writes.
    >>>
    >>>>
    >>>Willy.
    >>>>
    >>>
    >>>
    >>
    >>
    >>
    >No, not at all. Interlocked operations imply a full fence, that is, reads
    >have acquire and writes have release semantics. That means that the JIT
    >may not register these variables nor store them locally and cannot move
    >stuff around them.
    >
    Let's look at the Win32 declaration for an Interlocked function:
    >
    LONG InterlockedExch ange(
    LONG volatile* Target,
    LONG Value
    );Clearly, Target is intended to be the address of a volatile variable.
    Sure, you can pass a non-volatile pointer, and there is an implicit
    conversion, but if you do *the variable will be treated as volatile only
    inside InterlockedExch ange*. The compiler can still do anything outside
    InterlockedExch ange, because it is dealing with a non-volatile variable.
    Sure, but this was not my point, the point is that Interlocked operations
    imply barriers, all or not full. "volatile" implies full barriers, so they
    both imply barriers, but they serve different purposes. One does not exclude
    the other, but that doesn't mean they should always be used in tandem, all
    depends on what you want to achieve in your code, what guarantees you want.
    Anyway, the docs do not impose it, the C# docs on Interlocked don't even
    mention volatile, and the Win32 docs (Interlocked API's) don't spend a word
    on the volatile argument. (note that the volatile was added to the
    signature after NT4 SP1).
    And, it can't possibly change behavior when InterlockedExch ange is called,
    because the call could be made from a different library, potentially not
    yet loaded.
    >
    Sorry but you are mixing native code and managed code semantics. What I
    mean, is that the semantics of the C (native) volatile is not the same as
    the semantics of C# 'volatile'. So when I refered to C++ supporting
    "volatile" I was refering to managed dialects (VC7.x and VC8) who's volatile
    semantics are obviously the same as all other languages
    I don't wanna discuss the semantics of volatile in standard C/c++ here, they
    are so imprecise that IMO it will lead to an endless dicussion, not relevant
    to C#.
    Also I don't wanna discuss the semantics of Win32 Interlocked either, "Win32
    interlocked API's" do accept pointers to volatile items, while .NET does
    accept "volatile pointers" (in unsafe context) as arguments of a method
    call, but treats the item as non volatile. Also, C#, will issue a warning
    when passing a volatile field (passed by ref is required by Interlocked
    operations), that means that the item will be treated as volatile, but the
    reference itself will not.

    Consider this:
    >
    /* compilation unit one */
    void DoIt(LONG *target)
    {
    LONG value = /* some long calculation here */;
    if (value != InterlockedExch ange(target, value))
    {
    /* some complex operation here */
    }
    }
    >
    /* compilation unit two */
    >
    extern void DoIt(LONG * target);
    extern LONG shared;
    >
    void outer(void)
    {
    for( int i = 0; i < 1000; i++ )
    {
    DoIt(&shared);
    }
    }
    >
    Now, clearly, the compiler has no way of telling that DoIt uses
    Interlocked access, since DoIt didn't declare volatile semantics on the
    pointer passed in. So the compiler can, if desired, transform outer
    thusly:
    >
    void outer(void)
    {
    LONG goodLocalityOfR eference = shared;
    for( int i = 0; i < 1000; i++ )
    {
    DoIt(&goodLocal ityOfReference) ;
    }
    shared = goodLocalityOfR eference;
    }
    >
    Except for one thing. In native code, pointers have values that can be
    compared, subtracted, etc. So the compiler has to honestly pass the
    address of shared. In managed code, with tracking handles, the compiler
    doesn't have to preserve the address of the variable (that would, after
    all, defeat compacting garbage collection). Oh, sure, the JIT has a lot
    more information about what is being called than a native compiler does,
    it almost gets rid of separate compilation units.... but not quite. With
    dynamically loaded assemblies and reflection in the mix, it is just a
    helpless as a "compile-time" compiler.
    >
    I'm fairly sure that the current .NET runtime doesn't actually do any such
    optimization as I've described. But I wouldn't bet against such things
    being added in the future, when NUMA architectures become so widespread
    that the compiler has to optimize for them.
    >
    Be safe, use volatile on every variable you want to act volatile, which
    includes every variable passed to Interlocked.
    >
    >Think of this, what would be the use of Interlocked operation when used
    >in languages that don't support volatile (like VB.NET) or good old C/C++
    >(except VC7 and up).
    >
    VC++, all versions, and all other PC compilers that I'm aware of (as in,
    not embedded), support volatile to the extent needed to invoke an
    interlocked operation. That is, the real variable is always accessed at
    the time specified by the compiler. The memory fences are provided by the
    implementation of Interlocked*, independent of the compiler version.
    >
    Where in the docs (MSDN Platform SDK etc..) do they state that Interlocked
    should always be on volatile items?

    >I also don't agree with your statement that you should *always* use
    >volatile in lock free or low lock scenario's. IMO, you should almost
    >never use volatile, unless you perfectly understand the semantics of the
    >memory model of the CLR/CLI (ECMA differs from V1.X differs from V2 for
    >instance) and the memory model of the CPU (IA32 vs. IA64). The last year
    >I was involved in the resolution of a number of nasty bugs , all of them
    >where the result of people trying to out-smart the system by applying
    >lock free or low lock techniques using volatile, since then whenever I
    >see volatile I'm getting very suspicious, really.......
    >
    You are claiming that you should almost never use lock free techniques,
    and thus volatile should be rare. This hardly contradicts my statement
    that volatile should always be used in lock free programming.
    Kind of, I'm claiming that you should rarely use lock-free techniques when
    using C# in mainstream applications, I've seen too many people trying to
    implement lock free code, and if you ask "why", the answer is mostly
    "performanc e", and if you asked if the measured their "locked "
    implementation, the answer is mostly, well I have no 'locked'
    implementation, this is what I call "premature optimization" without any
    guarantees, other than probably producing unrealiable code, which is (IMO)
    more important than performant code .
    IMO the use of volatile should be rare in the sense that you better use
    locks and only use volatile for the most simple cases (which doesn't imply
    'rare'), for instance when you need to guarantee that all possible observers
    of a field (of type accepted by volatile) see the same value when that value
    has been written to by another observer.
    Remember "volatile" is something taken care of by the JIT, all it does is
    eliminate some of the possible optimizations like (but not restricted to):
    - volatile items cannot be registered...
    - multiple stores cannot be suppressed...
    - re-ordering is restricted.
    - ...
    But keep in mind that, 'volatile' suppresses optimizations for all possible
    accesses, even when not subject to multiple observers (threads), and that
    volatile fields accesses can move, some people think they can't....

    Willy.




    Comment

    • =?Utf-8?B?UGV0ZXIgUml0Y2hpZSBbQyMgTVZQXQ==?=

      #17
      RE: When is &quot;volatile& quot; used instead of &quot;lock&quot ; ?

      Sorry, coming in late; but this are some poor implications with respect to
      "volatile" and "lock" in this thread (other statements like "..there is no
      need for volatile [when] you Interlock consistently across all threads in the
      process." are valid).

      "lock" and "volatile" are two different things. You may not always need
      "lock" with a type that can be declared volatile; but you should always use
      volatile with a member that is accessed by multiple threads (an optimization
      would be that you wouldn't need "volatile" if Interlocked were always used
      with the member in question, if applicable--as has been noted). For example,
      why would anyone assume the the line commented with "// *" was thread-safe
      simply because "i" was declared with "volatile":

      volatile int i;
      static Random random = new Random();
      static int Transmogrify(in t value)
      {
      return value *= random.Next();
      }

      void Method()
      {
      i = Transmogrify(i) ; // *
      }

      "volatile" doesn't make a member thread-safe, the above operation still
      requires at least two instructions (likely four), which are entirely likely
      to be separated by preemption to another thread that modifies i.

      By the same token, the lock statement surrounding access to a member doesn't
      stop the compiler from having optimized use of a member by caching it to a
      register especially if that member is declared in a different assembly that
      was compiled for this code was written:

      lock(lockObject )
      {
      i = i + 1;
      }

      ....yes, the compiler *could* assume that all members within the lock
      statement block are likely accessible by multiple threads (implicit
      volatile); but that's not its intention and it's certainly not documented as
      doing that (and it would be pointless, other code knows nothing about this
      block and could have optimized use of i by changing its order of access or
      caching to a registry).

      volatile and lock should be used in conjunction, one is not a replacement
      for the other.

      --
      Browse http://connect.microsoft.com/VisualStudio/feedback/ and vote.

      Microsoft MVP, Visual Developer - Visual C#

      Comment

      • Jon Skeet [C# MVP]

        #18
        RE: When is &quot;volatile& quot; used instead of &quot;lock&quot ; ?

        Peter Ritchie [C# MVP] <PRSoCo@newsgro ups.nospamwrote :

        <snip>
        By the same token, the lock statement surrounding access to a member doesn't
        stop the compiler from having optimized use of a member by caching it to a
        register especially if that member is declared in a different assembly that
        was compiled for this code was written:
        >
        lock(lockObject )
        {
        i = i + 1;
        }
        Acquiring a lock has acquire semantics, and releasing a lock has
        release semantics. You don't need any volatility if all access to any
        particular item of shared data is always made having acquired a certain
        lock.

        If different locks are used, you could be in trouble, but if you always
        lock on the same reference (when accessing the same shared data) you're
        guaranteed to be okay.
        ...yes, the compiler *could* assume that all members within the lock
        statement block are likely accessible by multiple threads (implicit
        volatile); but that's not its intention and it's certainly not documented as
        doing that (and it would be pointless, other code knows nothing about this
        block and could have optimized use of i by changing its order of access or
        caching to a registry).
        It certainly *is* documented. ECMA 335, section 12.6.5:

        <quote>
        Acquiring a lock (System.Threadi ng.Monitor.Ente r or entering a
        synchronized method) shall implicitly
        perform a volatile read operation, and releasing a lock
        (System.Threadi ng.Monitor.Exit or leaving a
        synchronized method) shall implicitly perform a volatile write
        operation.
        </quote>
        volatile and lock should be used in conjunction, one is not a replacement
        for the other.
        If you lock appropriately, you never need to use volatile.

        --
        Jon Skeet - <skeet@pobox.co m>
        http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
        If replying to the group, please do not mail me too

        Comment

        • Willy Denoyette [MVP]

          #19
          Re: When is &quot;volatile& quot; used instead of &quot;lock&quot ; ?

          "Jon Skeet [C# MVP]" <skeet@pobox.co mwrote in message
          news:MPG.20db8f ad270511ef21e@m snews.microsoft .com...
          Peter Ritchie [C# MVP] <PRSoCo@newsgro ups.nospamwrote :
          >
          <snip>
          >
          >By the same token, the lock statement surrounding access to a member
          >doesn't
          >stop the compiler from having optimized use of a member by caching it to
          >a
          >register especially if that member is declared in a different assembly
          >that
          >was compiled for this code was written:
          >>
          >lock(lockObjec t)
          >{
          > i = i + 1;
          >}
          >
          Acquiring a lock has acquire semantics, and releasing a lock has
          release semantics. You don't need any volatility if all access to any
          particular item of shared data is always made having acquired a certain
          lock.
          >
          If different locks are used, you could be in trouble, but if you always
          lock on the same reference (when accessing the same shared data) you're
          guaranteed to be okay.
          >
          >...yes, the compiler *could* assume that all members within the lock
          >statement block are likely accessible by multiple threads (implicit
          >volatile); but that's not its intention and it's certainly not documented
          >as
          >doing that (and it would be pointless, other code knows nothing about
          >this
          >block and could have optimized use of i by changing its order of access
          >or
          >caching to a registry).
          >
          It certainly *is* documented. ECMA 335, section 12.6.5:
          >
          <quote>
          Acquiring a lock (System.Threadi ng.Monitor.Ente r or entering a
          synchronized method) shall implicitly
          perform a volatile read operation, and releasing a lock
          (System.Threadi ng.Monitor.Exit or leaving a
          synchronized method) shall implicitly perform a volatile write
          operation.
          </quote>
          >
          >volatile and lock should be used in conjunction, one is not a replacement
          >for the other.
          >
          If you lock appropriately, you never need to use volatile.
          >

          True, when using locks, make sure you do it consistently. And that's exactly
          why I said that I'm getting suspicious when I see a "volatile" field. Most
          of the time this modifier is used because the author doesn't understand the
          semantics of "volatile", or he's not sure about his own locking policy or he
          has no locking policy at all. Also some may think that volatile implies a
          fence, which is not the case, it only tells the JIT to turn off some of the
          optimizations like register allocation and load/store reordering, but it
          doesn't prevent possible re-ordering and write buffering done by the CPU,
          note, that this is a non issue on X86 and X64 like CPU's , given the memory
          model enforced by the CLR, but it is an issue on IA64.

          Willy.

          Comment

          • =?Utf-8?B?UGV0ZXIgUml0Y2hpZSBbQyMgTVZQXQ==?=

            #20
            RE: When is &quot;volatile& quot; used instead of &quot;lock&quot ; ?

            Acquiring a lock has acquire semantics, and releasing a lock has
            release semantics. You don't need any volatility if all access to any
            particular item of shared data is always made having acquired a certain
            lock.
            ....which only applies to reference types. Most of this discussion has been
            revolving around value types (by virtue of Interlocked.Inc rement), for which
            "lock" cannot not apply. e.g. you can't switch from using lock on a member
            to using Interlocked.Inc rement on that member, one works with references and
            the other with value types (specifically Int32 and Int64). This is what
            raised my concern.
            It certainly *is* documented. ECMA 335, section 12.6.5:
            >
            <quote>
            Acquiring a lock (System.Threadi ng.Monitor.Ente r or entering a
            synchronized method) shall implicitly
            perform a volatile read operation, and releasing a lock
            (System.Threadi ng.Monitor.Exit or leaving a
            synchronized method) shall implicitly perform a volatile write
            operation.
            </quote>
            ....still doesn't document anything about the members/variables within the
            locked block (please read my example). That quote applies only to the
            reference used as the parameter for the lock.

            There can be no lock acquire semantics for value members. Suggesting
            "locking appropriately" cannot apply here and can be misconstrued by some
            people by creating something like "lock(myLocker) {intMember = SomeMethod();}"
            which does not do the same thing as making intMember volatile, increases
            overhead needlessly, and still leaves a potential bug.
            >
            volatile and lock should be used in conjunction, one is not a replacement
            for the other.
            >
            If you lock appropriately, you never need to use volatile.
            Even if the discussion hasn't been about value types, a dangerous statement;
            because it could only apply to reference types (i.e. if myObject is wrapped
            with lock(myObject) in every thread, yes I don't need to declare it with
            volatile--but that's probably not why I'm using lock). In the context of
            reference types, volatile only applies to the pointer (reference) not
            anything within the object it references. Reference assignment is atomic,
            there's no need to use lock to guard that sort of thing. You use lock to
            guard a non-atomic invariant, volatile has nothing to do with that--it has to
            do with the optimization (ordering, caching) of pointer/value reads and
            writes.

            Calling Monitor.Enter/Minitor.Exit is a pretty heavy-weight means of
            ensuring acquire semantics; at least 5 times slower if volatile is all you
            need.

            -- Peter

            Comment

            • =?Utf-8?B?UGV0ZXIgUml0Y2hpZSBbQyMgTVZQXQ==?=

              #21
              Re: When is &quot;volatile& quot; used instead of &quot;lock&quot ; ?

              Do you think the following is suspicous:?

              volatile int intMember;

              ....assumes you didn't read my last post, I suppose :-)

              -- Peter

              --
              Browse http://connect.microsoft.com/VisualStudio/feedback/ and vote.

              Microsoft MVP, Visual Developer - Visual C#


              "Willy Denoyette [MVP]" wrote:
              "Jon Skeet [C# MVP]" <skeet@pobox.co mwrote in message
              news:MPG.20db8f ad270511ef21e@m snews.microsoft .com...
              Peter Ritchie [C# MVP] <PRSoCo@newsgro ups.nospamwrote :

              <snip>
              By the same token, the lock statement surrounding access to a member
              doesn't
              stop the compiler from having optimized use of a member by caching it to
              a
              register especially if that member is declared in a different assembly
              that
              was compiled for this code was written:
              >
              lock(lockObject )
              {
              i = i + 1;
              }
              Acquiring a lock has acquire semantics, and releasing a lock has
              release semantics. You don't need any volatility if all access to any
              particular item of shared data is always made having acquired a certain
              lock.

              If different locks are used, you could be in trouble, but if you always
              lock on the same reference (when accessing the same shared data) you're
              guaranteed to be okay.
              ...yes, the compiler *could* assume that all members within the lock
              statement block are likely accessible by multiple threads (implicit
              volatile); but that's not its intention and it's certainly not documented
              as
              doing that (and it would be pointless, other code knows nothing about
              this
              block and could have optimized use of i by changing its order of access
              or
              caching to a registry).
              It certainly *is* documented. ECMA 335, section 12.6.5:

              <quote>
              Acquiring a lock (System.Threadi ng.Monitor.Ente r or entering a
              synchronized method) shall implicitly
              perform a volatile read operation, and releasing a lock
              (System.Threadi ng.Monitor.Exit or leaving a
              synchronized method) shall implicitly perform a volatile write
              operation.
              </quote>
              volatile and lock should be used in conjunction, one is not a replacement
              for the other.
              If you lock appropriately, you never need to use volatile.
              >
              >
              True, when using locks, make sure you do it consistently. And that's exactly
              why I said that I'm getting suspicious when I see a "volatile" field. Most
              of the time this modifier is used because the author doesn't understand the
              semantics of "volatile", or he's not sure about his own locking policy or he
              has no locking policy at all. Also some may think that volatile implies a
              fence, which is not the case, it only tells the JIT to turn off some of the
              optimizations like register allocation and load/store reordering, but it
              doesn't prevent possible re-ordering and write buffering done by the CPU,
              note, that this is a non issue on X86 and X64 like CPU's , given the memory
              model enforced by the CLR, but it is an issue on IA64.
              >
              Willy.
              >
              >

              Comment

              • Jon Skeet [C# MVP]

                #22
                RE: When is &quot;volatile& quot; used instead of &quot;lock&quot ; ?

                Peter Ritchie [C# MVP] <PRSoCo@newsgro ups.nospamwrote :
                Acquiring a lock has acquire semantics, and releasing a lock has
                release semantics. You don't need any volatility if all access to any
                particular item of shared data is always made having acquired a certain
                lock.
                >
                ...which only applies to reference types. Most of this discussion has been
                revolving around value types (by virtue of Interlocked.Inc rement), for which
                "lock" cannot not apply. e.g. you can't switch from using lock on a member
                to using Interlocked.Inc rement on that member, one works with references and
                the other with value types (specifically Int32 and Int64). This is what
                raised my concern.
                It's not a case of using a lock on a particular value - taking the lock
                out creates a memory barrier beyond which *no* reads can pass, not just
                reads on the locked expression.
                It certainly *is* documented. ECMA 335, section 12.6.5:

                <quote>
                Acquiring a lock (System.Threadi ng.Monitor.Ente r or entering a
                synchronized method) shall implicitly
                perform a volatile read operation, and releasing a lock
                (System.Threadi ng.Monitor.Exit or leaving a
                synchronized method) shall implicitly perform a volatile write
                operation.
                </quote>
                >
                ...still doesn't document anything about the members/variables within the
                locked block (please read my example). That quote applies only to the
                reference used as the parameter for the lock.
                >
                There can be no lock acquire semantics for value members. Suggesting
                "locking appropriately" cannot apply here and can be misconstrued by some
                people by creating something like "lock(myLocker) {intMember = SomeMethod();}"
                which does not do the same thing as making intMember volatile, increases
                overhead needlessly, and still leaves a potential bug.
                No, it *doesn't* leave a bug - you've misunderstood the effect of lock
                having acquire semantics.
                volatile and lock should be used in conjunction, one is not a replacement
                for the other.
                If you lock appropriately, you never need to use volatile.
                >
                Even if the discussion hasn't been about value types, a dangerous statement;
                because it could only apply to reference types (i.e. if myObject is wrapped
                with lock(myObject) in every thread, yes I don't need to declare it with
                volatile--but that's probably not why I'm using lock). In the context of
                reference types, volatile only applies to the pointer (reference) not
                anything within the object it references. Reference assignment is atomic,
                there's no need to use lock to guard that sort of thing. You use lock to
                guard a non-atomic invariant, volatile has nothing to do with that--it has to
                do with the optimization (ordering, caching) of pointer/value reads and
                writes.
                Atomicity and volatility are very different things, and shouldn't be
                confused.

                Locks do more than just guarding non-atomic invariants though - they
                have the acquire/release semantics which make volatility unnecessary.

                To be absolutely clear on this, if I have:

                int someValue;
                object myLock;

                ....

                lock (myLock)
                {
                int x = someValue;
                someValue = x+1;
                }

                then the read of someValue *cannot* be from a cache - it *must* occur
                after the lock has been taken out. Likewise before the lock is
                released, the write back to someValue *must* have been made effectively
                flushed (it can't occur later than the release in the logical memory
                model).

                Here's how that's guaranteed by the spec:

                "Acquiring a lock (System.Threadi ng.Monitor.Ente r or entering a
                synchronized method) shall implicitly perform a volatile read
                operation"

                and

                "A volatile read has =3Facquire semantics=3F meaning that the read is
                guaranteed to occur prior to any references to memory that occur after
                the read instruction in the CIL instruction sequence."

                That means that the volatile read due to the lock is guaranteed to
                occur prior to the "reference to memory" (reading someValue) which
                occurs later in the CIL instruction sequence.

                The same thing happens the other way round for releasing the lock.
                Calling Monitor.Enter/Minitor.Exit is a pretty heavy-weight means of
                ensuring acquire semantics; at least 5 times slower if volatile is all you
                need.
                But still fast enough for almost everything I've ever needed to do, and
                I find it a lot easier to reason about a single way of doing things
                than having multiple ways for multiple situations. Just a personal
                preference - but it definitely *is* safe, without ever needing to
                declare anything volatile.

                --
                Jon Skeet - <skeet@pobox.co m>
                http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
                If replying to the group, please do not mail me too

                Comment

                • Willy Denoyette [MVP]

                  #23
                  Re: When is &quot;volatile& quot; used instead of &quot;lock&quot ; ?

                  "Peter Ritchie [C# MVP]" <PRSoCo@newsgro ups.nospamwrote in message
                  news:53C56549-0FD9-4D24-AFBC-C35BCC4ADE62@mi crosoft.com...
                  Do you think the following is suspicous:?
                  >
                  volatile int intMember;
                  >
                  ...assumes you didn't read my last post, I suppose :-)
                  >
                  -- Peter
                  Yes, I do, maybe it's a sign that someone is trying to write lock free
                  code....

                  But , I get even more suspicious is when I see this:

                  ....
                  volatile int intMember;
                  ....
                  void Foo()
                  {
                  lock(myLock)
                  {
                  // use intMember here and protect it's shared state by preventing
                  other threads to touch intMember
                  // for the duration of the critical section
                  }
                  ...
                  }

                  In above case, when you apply a consistent locking policy to protect your
                  invariants, there is no need for a volatile intMember. Else, it can be an
                  indication that some one is trying to play smart, by not taking a lock to
                  access intMember.


                  Willy.


                  Comment

                  • Willy Denoyette [MVP]

                    #24
                    Re: When is &quot;volatile& quot; used instead of &quot;lock&quot ; ?

                    "Jon Skeet [C# MVP]" <skeet@pobox.co mwrote in message
                    news:MPG.20dbba 8120b2e63e220@m snews.microsoft .com...
                    Peter Ritchie [C# MVP] <PRSoCo@newsgro ups.nospamwrote :
                    Acquiring a lock has acquire semantics, and releasing a lock has
                    release semantics. You don't need any volatility if all access to any
                    particular item of shared data is always made having acquired a certain
                    lock.
                    >>
                    >...which only applies to reference types. Most of this discussion has
                    >been
                    >revolving around value types (by virtue of Interlocked.Inc rement), for
                    >which
                    >"lock" cannot not apply. e.g. you can't switch from using lock on a
                    >member
                    >to using Interlocked.Inc rement on that member, one works with references
                    >and
                    >the other with value types (specifically Int32 and Int64). This is what
                    >raised my concern.
                    >
                    It's not a case of using a lock on a particular value - taking the lock
                    out creates a memory barrier beyond which *no* reads can pass, not just
                    reads on the locked expression.
                    >
                    It certainly *is* documented. ECMA 335, section 12.6.5:
                    >
                    <quote>
                    Acquiring a lock (System.Threadi ng.Monitor.Ente r or entering a
                    synchronized method) shall implicitly
                    perform a volatile read operation, and releasing a lock
                    (System.Threadi ng.Monitor.Exit or leaving a
                    synchronized method) shall implicitly perform a volatile write
                    operation.
                    </quote>
                    >>
                    >...still doesn't document anything about the members/variables within the
                    >locked block (please read my example). That quote applies only to the
                    >reference used as the parameter for the lock.
                    >>
                    >There can be no lock acquire semantics for value members. Suggesting
                    >"locking appropriately" cannot apply here and can be misconstrued by some
                    >people by creating something like "lock(myLocker) {intMember =
                    >SomeMethod();} "
                    >which does not do the same thing as making intMember volatile, increases
                    >overhead needlessly, and still leaves a potential bug.
                    >
                    No, it *doesn't* leave a bug - you've misunderstood the effect of lock
                    having acquire semantics.
                    >
                    volatile and lock should be used in conjunction, one is not a
                    replacement
                    for the other.
                    >
                    If you lock appropriately, you never need to use volatile.
                    >>
                    >Even if the discussion hasn't been about value types, a dangerous
                    >statement;
                    >because it could only apply to reference types (i.e. if myObject is
                    >wrapped
                    >with lock(myObject) in every thread, yes I don't need to declare it with
                    >volatile--but that's probably not why I'm using lock). In the context of
                    >reference types, volatile only applies to the pointer (reference) not
                    >anything within the object it references. Reference assignment is
                    >atomic,
                    >there's no need to use lock to guard that sort of thing. You use lock to
                    >guard a non-atomic invariant, volatile has nothing to do with that--it
                    >has to
                    >do with the optimization (ordering, caching) of pointer/value reads and
                    >writes.
                    >
                    Atomicity and volatility are very different things, and shouldn't be
                    confused.
                    >
                    Locks do more than just guarding non-atomic invariants though - they
                    have the acquire/release semantics which make volatility unnecessary.
                    >
                    To be absolutely clear on this, if I have:
                    >
                    int someValue;
                    object myLock;
                    >
                    ...
                    >
                    lock (myLock)
                    {
                    int x = someValue;
                    someValue = x+1;
                    }
                    >
                    then the read of someValue *cannot* be from a cache - it *must* occur
                    after the lock has been taken out. Likewise before the lock is
                    released, the write back to someValue *must* have been made effectively
                    flushed (it can't occur later than the release in the logical memory
                    model).
                    >
                    Actually on modern processors (others aren't supported anyway, unless you
                    are running W98 on a 80386) , the read and writes will come/go from/to the
                    cache (L1, L2 ..), the cache coherency protocol will guarantee consistency
                    across the cache lines holding the variable has changed. That way, the
                    "software" has a uniform view of what is called the "memory" irrespective
                    the number of HW threads (not talking about NUMA here!).

                    Here's how that's guaranteed by the spec:
                    >
                    "Acquiring a lock (System.Threadi ng.Monitor.Ente r or entering a
                    synchronized method) shall implicitly perform a volatile read
                    operation"
                    >
                    and
                    >
                    "A volatile read has =3Facquire semantics=3F meaning that the read is
                    guaranteed to occur prior to any references to memory that occur after
                    the read instruction in the CIL instruction sequence."
                    >
                    That means that the volatile read due to the lock is guaranteed to
                    occur prior to the "reference to memory" (reading someValue) which
                    occurs later in the CIL instruction sequence.
                    >
                    The same thing happens the other way round for releasing the lock.
                    >
                    >Calling Monitor.Enter/Minitor.Exit is a pretty heavy-weight means of
                    >ensuring acquire semantics; at least 5 times slower if volatile is all
                    >you
                    >need.
                    >
                    But still fast enough for almost everything I've ever needed to do, and
                    I find it a lot easier to reason about a single way of doing things
                    than having multiple ways for multiple situations. Just a personal
                    preference - but it definitely *is* safe, without ever needing to
                    declare anything volatile.
                    >
                    Probably one of the reasons why I've never seen a volatile modifier on a
                    field in the FCL.
                    And to repeat myself, volatile is not a guarantee against re-ordering and
                    write buffering by CPU's implementing a weak memory model, like the IA64.
                    Volatile serves only one thing, that is, prevent optimizations like
                    re-registering and re-ordering as there would be done by the JIT compiler.

                    Willy.

                    Comment

                    • Jon Skeet [C# MVP]

                      #25
                      Re: When is &quot;volatile& quot; used instead of &quot;lock&quot ; ?

                      Willy Denoyette [MVP] <willy.denoyett e@telenet.bewro te:

                      <snip>
                      then the read of someValue *cannot* be from a cache - it *must* occur
                      after the lock has been taken out. Likewise before the lock is
                      released, the write back to someValue *must* have been made effectively
                      flushed (it can't occur later than the release in the logical memory
                      model).
                      >
                      Actually on modern processors (others aren't supported anyway, unless you
                      are running W98 on a 80386) , the read and writes will come/go from/to the
                      cache (L1, L2 ..), the cache coherency protocol will guarantee consistency
                      across the cache lines holding the variable has changed. That way, the
                      "software" has a uniform view of what is called the "memory" irrespective
                      the number of HW threads (not talking about NUMA here!).
                      Yes - I've been using "cache" here somewhat naughtily (because it's the
                      terminology Peter was using). The sensible way to talk about it is in
                      terms of the .NET memory model, which is
                      But still fast enough for almost everything I've ever needed to do, and
                      I find it a lot easier to reason about a single way of doing things
                      than having multiple ways for multiple situations. Just a personal
                      preference - but it definitely *is* safe, without ever needing to
                      declare anything volatile.
                      >
                      Probably one of the reasons why I've never seen a volatile modifier on a
                      field in the FCL.
                      And to repeat myself, volatile is not a guarantee against re-ordering and
                      write buffering by CPU's implementing a weak memory model, like the IA64.
                      Volatile serves only one thing, that is, prevent optimizations like
                      re-registering and re-ordering as there would be done by the JIT compiler.
                      No, I disagree with that. Volatile *does* prevent (some) reordering and
                      write buffering as far as the visible effect to the code is concerned,
                      whether the effect comes from the JIT or the CPU. Suppose variables a
                      and b are volatile, then:

                      int c = a;
                      int d = b;

                      will guarantee that the visible effect is the value of "a" being read
                      before the value of "b" (which wouldn't be the case if they weren't
                      volatile). In particular, if the variables both start out at 0, then we
                      do:

                      b = 1;
                      a = 1;

                      in parallel with the previous code, then you might get c=d=1, or c=d=0,
                      or c=0, d=1, but you're guaranteed *not* to get c=1, d=0.

                      Whether that involves the JIT doing extra work to get round a weak CPU
                      memory model is unimportant - if it doesn't prevent that last
                      situation, it's failed to meet the spec.

                      --
                      Jon Skeet - <skeet@pobox.co m>
                      http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
                      If replying to the group, please do not mail me too

                      Comment

                      • Willy Denoyette [MVP]

                        #26
                        Re: When is &quot;volatile& quot; used instead of &quot;lock&quot ; ?

                        "Jon Skeet [C# MVP]" <skeet@pobox.co mwrote in message
                        news:MPG.20dbc8 612d9f61cd224@m snews.microsoft .com...
                        Willy Denoyette [MVP] <willy.denoyett e@telenet.bewro te:
                        >
                        <snip>
                        >
                        then the read of someValue *cannot* be from a cache - it *must* occur
                        after the lock has been taken out. Likewise before the lock is
                        released, the write back to someValue *must* have been made effectively
                        flushed (it can't occur later than the release in the logical memory
                        model).
                        >>
                        >Actually on modern processors (others aren't supported anyway, unless you
                        >are running W98 on a 80386) , the read and writes will come/go from/to
                        >the
                        >cache (L1, L2 ..), the cache coherency protocol will guarantee
                        >consistency
                        >across the cache lines holding the variable has changed. That way, the
                        >"software" has a uniform view of what is called the "memory" irrespective
                        >the number of HW threads (not talking about NUMA here!).
                        >
                        Yes - I've been using "cache" here somewhat naughtily (because it's the
                        terminology Peter was using). The sensible way to talk about it is in
                        terms of the .NET memory model, which is
                        >
                        But still fast enough for almost everything I've ever needed to do, and
                        I find it a lot easier to reason about a single way of doing things
                        than having multiple ways for multiple situations. Just a personal
                        preference - but it definitely *is* safe, without ever needing to
                        declare anything volatile.
                        >>
                        >Probably one of the reasons why I've never seen a volatile modifier on a
                        >field in the FCL.
                        >And to repeat myself, volatile is not a guarantee against re-ordering and
                        >write buffering by CPU's implementing a weak memory model, like the IA64.
                        >Volatile serves only one thing, that is, prevent optimizations like
                        >re-registering and re-ordering as there would be done by the JIT
                        >compiler.
                        >
                        No, I disagree with that. Volatile *does* prevent (some) reordering and
                        write buffering as far as the visible effect to the code is concerned,
                        whether the effect comes from the JIT or the CPU. Suppose variables a
                        and b are volatile, then:
                        >
                        int c = a;
                        int d = b;
                        >
                        will guarantee that the visible effect is the value of "a" being read
                        before the value of "b" (which wouldn't be the case if they weren't
                        volatile). In particular, if the variables both start out at 0, then we
                        do:
                        >
                        b = 1;
                        a = 1;
                        >
                        in parallel with the previous code, then you might get c=d=1, or c=d=0,
                        or c=0, d=1, but you're guaranteed *not* to get c=1, d=0.
                        >
                        Whether that involves the JIT doing extra work to get round a weak CPU
                        memory model is unimportant - if it doesn't prevent that last
                        situation, it's failed to meet the spec.
                        >
                        --
                        Jon Skeet - <skeet@pobox.co m>
                        http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
                        If replying to the group, please do not mail me too

                        Comment

                        • =?Utf-8?B?UGV0ZXIgUml0Y2hpZSBbQyMgTVZQXQ==?=

                          #27
                          Re: When is &quot;volatile& quot; used instead of &quot;lock&quot ; ?

                          For the record, I've been talking about the compiler re-organizing the code
                          during optimization. And I thought I was pretty clear about the compiler
                          "caching" values to a register, not the CPUs caches.
                          It's not a case of using a lock on a particular value - taking the lock
                          out creates a memory barrier beyond which *no* reads can pass, not just
                          reads on the locked expression.
                          I don't see you how you get that from:
                          "Acquiring a lock (System.Threadi ng.Monitor.Ente r or entering a
                          synchronized method) shall implicitly perform a volatile read
                          operation"
                          >
                          and
                          >
                          "A volatile read has "acquire semantics" meaning that the read is
                          guaranteed to occur prior to any references to memory that occur after
                          the read instruction in the CIL instruction sequence."
                          I would agree that a volatile read/write is performed on the parameter for
                          Monitor.Enter and Monitor.Exit.
                          To be absolutely clear on this, if I have:
                          >
                          int someValue;
                          object myLock;
                          >
                          ....
                          >
                          lock (myLock)
                          {
                          int x = someValue;
                          someValue = x+1;
                          }
                          >
                          then the read of someValue *cannot* be from a cache - it *must* occur
                          after the lock has been taken out. Likewise before the lock is
                          released, the write back to someValue *must* have been made effectively
                          flushed (it can't occur later than the release in the logical memory
                          model).
                          You're talking about CPU re-organizations and CPU cachings, I've been
                          talking about compiler optimizations.

                          None of the quotes affect code already optimized by the compiler. If the
                          compiler decides writing code that doesn't write a temporary value directly
                          back to the member/variable because it's faster and it doesn't know it's
                          volatile, nothing you've quoted will have a bearing on that.

                          Monitor.Enter may create memory barrier for the current thread, it's unclear
                          from 335; but it could not have affected code that accesses members outside
                          of a lock block.

                          335 says nothing about what the compiler does with code within a locked block.

                          Comment

                          • Willy Denoyette [MVP]

                            #28
                            Re: When is &quot;volatile& quot; used instead of &quot;lock&quot ; ?

                            "Jon Skeet [C# MVP]" <skeet@pobox.co mwrote in message
                            news:MPG.20dbc8 612d9f61cd224@m snews.microsoft .com...
                            Willy Denoyette [MVP] <willy.denoyett e@telenet.bewro te:
                            >
                            <snip>
                            >
                            then the read of someValue *cannot* be from a cache - it *must* occur
                            after the lock has been taken out. Likewise before the lock is
                            released, the write back to someValue *must* have been made effectively
                            flushed (it can't occur later than the release in the logical memory
                            model).
                            >>
                            >Actually on modern processors (others aren't supported anyway, unless you
                            >are running W98 on a 80386) , the read and writes will come/go from/to
                            >the
                            >cache (L1, L2 ..), the cache coherency protocol will guarantee
                            >consistency
                            >across the cache lines holding the variable has changed. That way, the
                            >"software" has a uniform view of what is called the "memory" irrespective
                            >the number of HW threads (not talking about NUMA here!).
                            >
                            Yes - I've been using "cache" here somewhat naughtily (because it's the
                            terminology Peter was using). The sensible way to talk about it is in
                            terms of the .NET memory model, which is
                            >
                            But still fast enough for almost everything I've ever needed to do, and
                            I find it a lot easier to reason about a single way of doing things
                            than having multiple ways for multiple situations. Just a personal
                            preference - but it definitely *is* safe, without ever needing to
                            declare anything volatile.
                            >>
                            >Probably one of the reasons why I've never seen a volatile modifier on a
                            >field in the FCL.
                            >And to repeat myself, volatile is not a guarantee against re-ordering and
                            >write buffering by CPU's implementing a weak memory model, like the IA64.
                            >Volatile serves only one thing, that is, prevent optimizations like
                            >re-registering and re-ordering as there would be done by the JIT
                            >compiler.
                            >
                            No, I disagree with that. Volatile *does* prevent (some) reordering and
                            write buffering as far as the visible effect to the code is concerned,
                            whether the effect comes from the JIT or the CPU. Suppose variables a
                            and b are volatile, then:
                            >
                            int c = a;
                            int d = b;
                            >
                            will guarantee that the visible effect is the value of "a" being read
                            before the value of "b" (which wouldn't be the case if they weren't
                            volatile). In particular, if the variables both start out at 0, then we
                            do:
                            >
                            b = 1;
                            a = 1;
                            >
                            in parallel with the previous code, then you might get c=d=1, or c=d=0,
                            or c=0, d=1, but you're guaranteed *not* to get c=1, d=0.
                            >
                            Whether that involves the JIT doing extra work to get round a weak CPU
                            memory model is unimportant - if it doesn't prevent that last
                            situation, it's failed to meet the spec.
                            >

                            Agreed, reads (all or not volatile) cannot move before a volatile read, and
                            writes cannot move after a volatile write.

                            But this is not my point, what I'm referring to is the following (assuming a
                            and b are volatile):

                            a = 5;
                            int d = b;

                            here it's allowed for the write to move after the read, they are referring
                            to different locations and they have no (visible) dependencies).


                            Willy.

                            Comment

                            • Jon Skeet [C# MVP]

                              #29
                              Re: When is &quot;volatile& quot; used instead of &quot;lock&quot ; ?

                              Peter Ritchie [C# MVP] <PRSoCo@newsgro ups.nospamwrote :
                              For the record, I've been talking about the compiler re-organizing the code
                              during optimization. And I thought I was pretty clear about the compiler
                              "caching" values to a register, not the CPUs caches.
                              That's all irrelevant - the important thing is the visible effect.

                              <snip>
                              then the read of someValue *cannot* be from a cache - it *must* occur
                              after the lock has been taken out. Likewise before the lock is
                              released, the write back to someValue *must* have been made effectively
                              flushed (it can't occur later than the release in the logical memory
                              model).
                              >
                              You're talking about CPU re-organizations and CPU cachings, I've been
                              talking about compiler optimizations.
                              As I said to Willy, I shouldn't have used the word "cache". Quite what
                              could make things appear to be out of order is irrelevant - they're all
                              forbidden by the spec in this case.
                              None of the quotes affect code already optimized by the compiler. If the
                              compiler decides writing code that doesn't write a temporary value directly
                              back to the member/variable because it's faster and it doesn't know it's
                              volatile, nothing you've quoted will have a bearing on that.
                              So here are you talking about the C# compiler rather than the JIT
                              compiler?

                              If so, I agree there appears to be a hole in the C# spec. I don't
                              believe the C# compiler *will* move any reads/writes around, however.
                              For the rest of the post, however, I'll assume you were actually still
                              talking about the JIT.
                              Monitor.Enter may create memory barrier for the current thread, it's unclear
                              from 335; but it could not have affected code that accesses members outside
                              of a lock block.
                              Agreed, but irrelevant.
                              335 says nothing about what the compiler does with code within a locked block.
                              Agreed, but irrelevant.

                              The situation I've been talking about is where a particular variable is
                              only referenced *inside* lock blocks, and where all the lock blocks
                              which refer to that variable are all locking against the same
                              reference.

                              At that point, there is an absolute ordering in terms of the execution
                              of those lock blocks - only one can execute at a time, because that's
                              the main point of locking.

                              Furthermore, while the ordering *within* the lock can be moved, none of
                              the reads which are inside the lock can be moved to before the lock is
                              acquired (in terms of the memory model, however that is achieved) and
                              none of the writes which are inside the lock can be moved to after the
                              lock is released.

                              Therefore any change to the variable is seen by each thread, with no
                              "stale" values being involved.

                              Now I totally agree that *if* you start accessing the variable from
                              outside a lock block, all bets are off - but so long as you keep
                              everything within locked sections of code, all locked with the same
                              lock, you're fine.

                              --
                              Jon Skeet - <skeet@pobox.co m>
                              http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
                              If replying to the group, please do not mail me too

                              Comment

                              • Jon Skeet [C# MVP]

                                #30
                                Re: When is &quot;volatile& quot; used instead of &quot;lock&quot ; ?

                                Willy Denoyette [MVP] <willy.denoyett e@telenet.bewro te:

                                <snip>
                                Agreed, reads (all or not volatile) cannot move before a volatile read, and
                                writes cannot move after a volatile write.
                                >
                                But this is not my point, what I'm referring to is the following (assuming a
                                and b are volatile):
                                >
                                a = 5;
                                int d = b;
                                >
                                here it's allowed for the write to move after the read, they are referring
                                to different locations and they have no (visible) dependencies).
                                Assuming they're not volatile, you're absolutely right - but I thought
                                you were talking about what could happen with *volatile* variables,
                                given that you said:

                                <quote>
                                And to repeat myself, volatile is not a guarantee against re-ordering
                                and write buffering by CPU's implementing a weak memory model, like the
                                IA64.
                                </quote>

                                I believe volatile *is* a guarantee against the reordering of volatile
                                operations. Volatile isn't a guarantee against the reordering of two
                                non-volatile operations with no volatile operation between them, but
                                that's the case for the JIT as well as the CPU.

                                I don't believe it's necessary to talk about the JIT separately from
                                the CPU when thinking on a purely spec-based level. If we were looking
                                at generated code we'd need to consider the platform etc, but at a
                                higher level than that we can just talk about the memory model that the
                                CLR provides, however it provides it.

                                --
                                Jon Skeet - <skeet@pobox.co m>
                                http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
                                If replying to the group, please do not mail me too

                                Comment

                                Working...