I am confused with the documentation for .NET/C# regarding the volatile
keyword vs System.Threading.Thread.VolatileRead
/VolatileWrite
and System.Threading.Volatile.Read/Write
. I am trying to understand what exactly is guaranteed for a volatile field and what exactly these methods are doing.
I thought volatile
provides the release/acquire semantics, but the documentation for Thread.VolatileRead
/VolatileWrite
makes me wonder if my understanding is actually correct.
This is the language reference for volatile: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/volatile
Adding the volatile modifier ensures that all threads will observe volatile writes performed by any other thread in the order in which they were performed. There is no guarantee of a single total ordering of volatile writes as seen from all threads of execution.
So far makes sense. This is the language specification: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/classes#volatile-fields
For volatile fields, such reordering optimizations are restricted:
A read of a volatile field is called a volatile read. A volatile read has "acquire semantics"; that is, it is guaranteed to occur prior to any references to memory that occur after it in the instruction sequence. A write of a volatile field is called a volatile write. A volatile write has "release semantics"; that is, it is guaranteed to happen after any memory references prior to the write instruction in the instruction sequence.
These restrictions ensure that all threads will observe volatile writes performed by any other thread in the order in which they were performed. A conforming implementation is not required to provide a single total ordering of volatile writes as seen from all threads of execution.
Again, this looks like volatile
provides the release/acquire semantics.
But then I look at the documentation for Thread.VolatileRead: https://learn.microsoft.com/en-us/dotnet/api/system.threading.thread.volatileread?view=netframework-4.8#System_Threading_Thread_VolatileRead_System_Int64__
Reads the value of a field. The value is the latest written by any processor in a computer, regardless of the number of processors or the state of processor cache. ... On a multiprocessor system, VolatileRead obtains the very latest value written to a memory location by any processor. This might require flushing processor caches.
For Thread.VolatileWrite:
Writes a value to a field immediately, so that the value is visible to all processors in the computer.
This looks stricter then an individual store/load fence (release/acquire), specifically the part about flushing processor caches, i.e. more strict guarantee than just volatile
. But then the same document says:
In C#, using the volatile modifier on a field guarantees that all access to that field uses VolatileRead or VolatileWrite
So my question is - what is guaranteed for a volatile
field with respect to the store buffer - just release/acquire, or the stronger Thread.VolatileRead/Write guarantees? Or is my understanding about VolatileRead/Write
wrong and these are the same as volatile
?