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

Tech-Archive recommends: Fix windows errors by optimizing your registry



Once again, there's nothing in the spec which distinguishes the two.
Yes, a lot of pages describing the details go into more detail about
different CPU architectures, but please say where in the spec it says
"You can't rely on any of this as the overall semantics of the program,
because the JIT and the CPU are separate."

Lack of a clause does not make the opposite a spec. requirement. And why
shouldn't it address the two separately? But, the spec *does* distinguish
the two, The whole 12.6.4 section is about optimization and it only
describes single thread of execution guarantees, and makes no mention of
"semantics" ("acquire" or "release"). Everything else you quote is outside
of 12.6.4 and is always in the context of "acquire semantics" or "release
semantics" (ergo in the context of processor caching). The other sections in
12.6 relevant to our conversation deal with locking (and its relationship to
volatile operations) and how volatile operations affect the processor's
cache. 12.6.4 doesn't come out and say the "JIT compiler" but it does say
"the CLI", which can't mean the C#-to-IL compiler and without a JIT we can't
get from IL to native instructions.

The JIT needs to take account of what CPU it's running on in order to
make sure that the overall semantics are correct, that's all.

Agreed, but irrelevant. I'm discussing what is documented as "compliancy"
requirements for that JIT. I agree it needs to be in the spec. and am
willing to view "the CLI" as "The JIT" for the purposes of what is required
of the JIT.

http://msdn2.microsoft.com/en-us/library/ms686355.aspx
Details that prior to VC 2003 "volatile" had no acquire/release semantics
and only dealt with compiler optimizations.

You shouldn't try to reason about the CLR term "volatile" with
reference to what it means outside the CLI, in my view.

Yes, not to the CLR/CLI. But with regard to C#, reusing the keyword
"volatile" in C# pulls that baggage in. If "volatile" in C# wasn't intended
to be the same as "volatile" in C++ it should have been named differently.
If the goal was to ease compilation of C++ code, then they've done
programmers a hell of a disservice. It also goes to MT issues outside of
..NET 2.0 and that the issues of compiler optimizations exist and can be
dealt with (although much more difficultly) in languages that support
volatility. Some language don't, yes, and they fail miserably at MT; but
that's a separate issue. And I'll admit it's anecdotal to my point; just as
your external references are.

It also brings in the issue that use of "volatile" for variables despite
always being synchronized is common-place and shows separation of volatility
and synchronization, and shouldn't be considered abhorrent in .NET 2.0.
Other than your lock protocol of
only-access-that-member-within-a-lock-block-locked-on-the-same-object,
volatility must be addressed through the "volatile" (or always use
Thread.Volatile* or Threading.Interlocked.*, which I don't recommend unless
"volatile" doesn't apply) in .NET 2.0.

If you take 12.6.7 para 2's "after any memory references" to mean anything
other than processor cachings, as soon as you introduce Monitor.Enter or
Monitor.Exit (or volatile) you can't optimize anything, even locals.
That's
clearly not the intention of that paragraph. That paragraph says almost
the
same thing as Joe's blog with respect to st.rel and ld.acq, which only
applies to processor caching. That paragraph is the only place 335
doesn't
associate the volatile. prefix when it talks about what volatile reads and
writes do. There's the almost casual mention of Enter and Exit being an
implicite volatile read and write respectively; but that also makes
perfect
sense if you're only discussing processor caching.

But if you don't read the spec as overall semantics, it's entirely
useless.

If you do, most of the statements in 12.6 are contradictory and nonsensical
and no more useful. You can't seriously tell me that reference to *any*
unrelated memory is guaranteed to execute before a virtual operation?
Clearly, and makes sense if, that's discussing processor caching and not
optimization restrictions. e.g.

void Method() {
this.memberNumber = 10;
lock(this.syncObject)
{
String temp = this.memberString;
this.memberString = "Frank";
//...
}
}

Who cares if the processor instruction to assign 10 to MemberNumber is
executed before or after the lock? Dealing strictly with processor caching
in 12.6.7 para 2, of course if the processor cache is flushed at a virtual
operation it "...is guaranteed to occur prior to any references to memory
that occur after the [operation's] instruction in the CIL instruction
sequence." and any cached writes to this.memberString are flushed before
it's value is assigned to "temp". Notice there's nothing about when an
instruction is executed in 12.6.7 para 2.

Relating to origins, the C++ volatile keyword in VC++ never dealt with
acquire or release semantics until VC++ 2005. Prior to that, for the
past
30+ years it has been used only to tell the compiler not to optimize that
identifier.

Yup - but again, that doesn't alter what the spec says.

Agreed, and neither does any other external reference.

It's not a huge stretch to think of 335:12.6 only in the context of
processor caching.

That's where we disagree. I believe that if the spec says that X will
happen and Y doesn't happen, then if I can show Y happening and X not
happening on a particular implementation, then that implementation is
*broken* - it doesn't conform with the spec.

I agree we disagree (I over generalized with "12.6" I should have said
12.6.4-12.6.8 inclusive, the rest of 12.6 doesn't apply to what we're
talking about), but you haven't referenced anything in the spec that
definitively backs up your opinion. Whereas I believe I've clearly shown
that the 12.6 is only clear and unambiguous if you separate optimizations
from processor caching (and define "acquire semantics" and "release
semantics" as applying only to processor caching) and say that 12.6.4 is the
only section affecting JIT optimizations.

I agree there are pitfalls to MT programming with my view of the spec, but
no more than any other framework. Your view doesn't eliminate pitfalls to
MT programming in .NET 2.0. My view simply means you must consider
volatility, and separately from synchronization and declare members accessed
by multiple threads with "volatile" or with "Volatile{Read|Write}", just
like most other languages with optimizing compilers.

Only if you make the leap that "acquire semantics" and "release
semantics"
don't refer to anything other than processor caching.

No - I'm saying they refer to the overall effect - that you should view
the spec as an absolute in terms of what's allowed to occur and what's
not, regardless of how that's achieved.

And I disagree 335 is making that implication and that it can be viewed as
absolute with your interpretation; otherwise you have to interpret 12.2.7
para 2 as not really meaning *all* references to memory, with regard to CIL
instructions, from "any references memory", for example.

The spec make complete sense if all it's talking about is processor
caching with regard to acquire/relese semantics. The processor does
reorder memory accesses, and similar to Joe's blog, in relation to
the instruction stream. The mere mention of the CIL instruction
sequence in 335 doesn't imply that

If any CLI implementation allows the processor to reorder instructions
in a way which means that 12.6.7 can't be relied upon for visible
effects, that implementation is broken.

12.6.7 makes no mention of not reordering instructions and is completely
unambiguous if it's viewed only in the context of processor caching.

Did you read the section of Grant's page about the locking protocol, by
the way?

Yes, I have, and it's a wonderful read on the Microsoft 2.0 .NET
implementation. If that were in the spec we wouldn't be having this
conversation, not that I believe Vance's article is normatively worded.
Your assertions are about guarantees in 335. If you have to refer to
external documents then you're basically agreeing with me that 335 isn't
clear with respect to JIT optimizations (or there aren't cross-threading
restrictions on JIT optimizations and your locking protocol requires the
addition of "volatile" or "Thread.Volatile{Read|Write}").

Even Joe Duffy describes Vance's article as a "reference on the 2.0 memory
model, as implemented". If all you're writing for are Windows platforms,
you can be reasonably comfortable that your locking protocol is backed up by
Vance's description; but you can't say there are spec guarantees for it.



__________ NOD32 2390 (20070710) Information __________

This message was checked by NOD32 antivirus system.
http://www.eset.com


.



Relevant Pages

  • Re: When is "volatile" used instead of "lock" ?
    ... How is any compiler supposed to guarantee "acquire semantics" and "release ... Or "acquire semantics" from the CLI spec as others have interpreted it: ... The CLI spec must take into account processor caching; ... CPU into account by specifying to flush it's cache before/after volatile ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: When is "volatile" used instead of "lock" ?
    ... Lack of a clause does not make the opposite a spec. ... volatile operations) and how volatile operations affect the processor's ... identical except for the guarantees that their own memory models made. ... still guaranteeing that the locking protocol will work. ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: When is "volatile" used instead of "lock" ?
    ... I've been trying to get to your assertion of no JIT optimizations of members ... between Enter/Exit because of "volatility guarantees" in the CLI spec. ... between Enter/Exit were considered volatile operations-which is probably why ... publicly visible object is used as the lock object, ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: When is "volatile" used instead of "lock" ?
    ... assertion that the spec. ... It's also a bit contradictory to information you've said you received from ... No offence or implication that you didn't receive that ... Section 11.6.7 makes guarantees about volatile reads/writes, ...
    (microsoft.public.dotnet.languages.csharp)