Search code examples
c#.netmultithreadingvolatile

How fields of reference type can be nonvolatile?


Here is what MSDN says about volatile:

The volatile keyword indicates that a field might be modified by multiple threads that are executing at the same time. Fields that are declared volatile are not subject to compiler optimizations that assume access by a single thread. This ensures that the most up-to-date value is present in the field at all times.

The volatile keyword can be applied to fields of these types: Reference types.

This state implies that fields of reference type are not volatile by default.
I think it's ok to treat reference-type field as a field of value type containing the address of the object. Then it becomes similar to int type. Joe Albahari gives some examples.

But!... Unlike usual value types GC moves objects into memory when it compacts the heap and changes the references accordingly. Hence 'the most up-to-date value' must always be there. And if so how does the concept of volatility apply to reference types?


Solution

  • This state implies that fields of reference type are not volatile by default.

    Sure. No field is treated as volatile by default because volatile comes together with the possibility of a sizable performance cost.

    I think it's ok to treat reference-type field as a field of value type containing the address of the object. Then it becomes similar to int type.

    Assume this can be done without problems. So what? Fields of scalar types such as int or bool are also not treated as volatile by default.

    Unlike usual value types GC moves objects into memory when it compacts the heap and changes the references accordingly. Hence 'the most up-to-date value' must always be there. And if so how does the concept of volatility apply to reference types?

    You are slightly confused about the utility of volatile. The problem it is meant to address is not only that (A) the most up to date value is not there (although volatile semantics do guarantee that any writes to the value will be observable by any reads that follow them in an abstract timeline¹).

    Apart from that, it is also meant to address the situation (B) where the compiler assumes that the code it generates is the only party modifying that value (or the value is not being modified at all), which means that it will not choose to read the value from the field and instead use a "cached copy" it already has at hand. If the value is being modified by a third party at the same time this is obviously going to result in the program using the wrong data for its calculations.

    For more information you can refer to this excellent analysis by Igor Ostrovsky, wherein (A) is referred to as "processor optimizations" and (B) as "compiler optimizations".


    ¹ Please note that this is not a rigorous definition but rather just a crude approximation.