Search code examples
c#.netmultithreadingvolatile

Why not volatile on System.Double and System.Long?


A question like mine has been asked, but mine is a bit different. The question is, "Why is the volatile keyword not allowed in C# on types System.Double and System.Int64, etc.?"

On first blush, I answered my colleague, "Well, on a 32-bit machine, those types take at least two ticks to even enter the processor, and the .Net framework has the intention of abstracting away processor-specific details like that." To which he responds, "It's not abstracting anything if it's preventing you from using a feature because of a processor-specific problem!"

He's implying that a processor-specific detail should not show up to a person using a framework that "abstracts" details like that away from the programmer. So, the framework (or C#) should abstract away those and do what it needs to do to offer the same guarantees for System.Double, etc. (whether that's a Semaphore, memory barrier, or whatever). I argued that the framework shouldn't add the overhead of a Semaphore on volatile, because the programmer isn't expecting such overhead with such a keyword, because a Semaphore isn't necessary for the 32-bit types. The greater overhead for the 64-bit types might come as a surprise, so, better for the .Net framework to just not allow it, and make you do your own Semaphore on larger types if the overhead is acceptable.

That led to our investigating what the volatile keyword is all about. (see this page). That page states, in the notes:

In C#, using the volatile modifier on a field guarantees that all access to that field uses VolatileRead or VolatileWrite.

Hmmm.....VolatileRead and VolatileWrite both support our 64-bit types!! My question, then, is,

"Why is the volatile keyword not allowed in C# on types System.Double and System.Int64, etc.?"


Solution

  • Not really an answer to your question, but...

    I'm pretty sure that the MSDN documentation you've referenced is incorrect when it states that "using the volatile modifier on a field guarantees that all access to that field uses VolatileRead or VolatileWrite".

    Directly reading or writing to a volatile field only generates a half-fence (an acquire-fence when reading and a release-fence when writing).

    The VolatileRead and VolatileWrite methods use MemoryBarrier internally, which generates a full-fence.

    Joe Duffy knows a thing or two about concurrent programming; this is what he has to say about volatile:

    (As an aside, many people wonder about the difference between loads and stores of variables marked as volatile and calls to Thread.VolatileRead and Thread.VolatileWrite. The difference is that the former APIs are implemented stronger than the jitted code: they achieve acquire/release semantics by emitting full fences on the right side. The APIs are more expensive to call too, but at least allow you to decide on a callsite-by-callsite basis which individual loads and stores need the MM guarantees.)