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



"Peter Ritchie [C#MVP]" <prsoco@xxxxxxxxxxxxxxxxx> wrote in message news:O$CaALvxHHA.4476@xxxxxxxxxxxxxxxxxxxxxxx

"Willy Denoyette [MVP]" <willy.denoyette@xxxxxxxxxx> wrote in message news:%233k01ftxHHA.3400@xxxxxxxxxxxxxxxxxxxxxxx
"Peter Ritchie [C#MVP]" <prsoco@xxxxxxxxxxxxxxxxx> wrote in message news:eKNnI1nxHHA.3588@xxxxxxxxxxxxxxxxxxxxxxx
<snip> What Joe says is this:
"The 2.0 memory model does not use ld.acq’s unless you are accessing volatile data (marked w/ the volatile modifier keyword or accessed via the Thread.VolatileRead API)."
which means that reads have acquire semantics when interlocked (reads of "volatile" fields or volatile read operations), all writes have release semantics since v 2.0, that means that all stores have release semantics (st.rel instruction on IA64), irrespective the current HW platform.
Right, changing the double-checked lock pattern to use an additional boolean won't work (and is no longer the double-check lock pattern) because it's not guarding the complex (non-atomic) invariant (the bool AND the instance) so it must change to the single-check lock pattern and take the performance hit. I wasn't saying the non-double-check lock pattern worked, only that Joe has detailed that ld.acq is not emitted unless the field is declared volatile.

In the "working" double-check lock the first read of "myValue" (comparing to null) is not volatile if "myValue" is not volatile. There's no acquire semantics until the lock statement. The write to "myValue" within the lock block is also not volatile, but it's guarded. So, immediately after the write to "myValue" any non-volatile access to "myValue" by another processor may not see that write as it may have been cached. The end of the lock block flushes the write-cache; and if that write was cached it is now flushed. And if there's any code after that write and before the end of the lock block (that isn't a volatile operation), you increase the liklihood of executing code after the write in the processors' instruction sequence without "seeing" it. Declaring "myValue" volatile causes the comparision of null to see any cached writes as well as the write to "myValue" in the lock block to be visible to all other processors.

To save you scouring
Vance's article, it's the last sentence paragraph 3 of the section Technique 4: Lazy Initialization. "In the .NET Framework 2.0 model, the code works without the volatile declarations.", in reference to Figure 7 which is:
public class LazyInitClass {
private static LazyInitClass myValue = null;
private static Object locker = new Object();

public static LazyInitClass GetValue() {
if (myValue == null) {
lock(locker) {
if (myValue == null) {
myValue = new LazyInitClass();
}
}
}
return myValue;
}
};


Above sample works on IA64 as is
Do you have IA64 disassembly of Vance's sample code to show that "if(myValue==null)" emits an instruction with the "acq" completer contrary to Joe's "The 2.0 memory model does not use ld.acq’s unless you are accessing volatile data..." statement?



Yes I do and it's not emitting a ld.acq on IA64, the memory read has no acquire semantics on X86/X64 either, but this is not relevant in case of Vance's sample (fig 7) . What makes this work in V2. (as explained in Vance's article you are referring to) is the strong write ordering in V2's memory model, this irrespective the underlying HW platform.

Willy.


.



Relevant Pages

  • Re: When is "volatile" used instead of "lock" ?
    ... "lock" cannot not apply. ... > synchronized method) shall implicitly perform a volatile write ... reference used as the parameter for the lock. ... There can be no lock acquire semantics for value members. ...
    (microsoft.public.dotnet.languages.csharp)
  • RE: When is "volatile" used instead of "lock" ?
    ... "lock" cannot not apply. ... synchronized method) shall implicitly perform a volatile write ... reference used as the parameter for the lock. ... There can be no lock acquire semantics for value members. ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: When is "volatile" used instead of "lock" ?
    ... the JIT doesn't do any optimizations on the integer as long as I pass ... lock pattern works without making the instance member volatile; ... private static LazyInitClass myValue = null; ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: When is "volatile" used instead of "lock" ?
    ... volatile data (marked w/ the volatile modifier keyword or accessed via the ... semantics since v 2.0, that means that all stores have release semantics ... won't work (and is no longer the double-check lock pattern) because it's not ... null) is not volatile if "myValue" is not volatile. ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: When is "volatile" used instead of "lock" ?
    ... Acquiring a lock has acquire semantics, ... statement block are likely accessible by multiple threads (implicit ... volatile); but that's not its intention and it's certainly not documented ...
    (microsoft.public.dotnet.languages.csharp)