'Why can't I cut off the volatile from spinlock implementation?

According to this article, http://msdn.microsoft.com/en-us/magazine/cc163715.aspx, this is the implementation of spinlock class:

class SpinLock
        {
            volatile int isEntered;
            // non-zero if the lock is entered 
            public void Enter()
            {
                while (Interlocked.CompareExchange(ref isEntered, 1, 0) != 0)
                {
                    Thread.Sleep(0); // force a thread context switch 
                }
            }
            public void Exit()
            {
                isEntered = 0;
            }
        }

I know what volatile means and does but I cant understand why its here.

Last thing I wanna ask in another topic- Does reading a object's property count as atomic operation? from my understanding, there are 2 reads here: first, the object reference and second the property reading.



Solution 1:[1]

After (re)studying memory models for a couple of days, I had the same vexing question about the use of volatile in SpinLock. I cannot find any problematic reordering of memory access that requires either of the fences entailed by the volatile access.

  • Acquire fence: Provided by Interlocked.CompareExchange().
  • Release fence: To get lock semantics, we want all side effects of the code "under the lock" to be visible when the lock is released. But the .NET memory model does not permit reordering of writes. Therefore, the writes from under the lock must become visible no later than the clearing of isEntered.

Update
After some more study based on the Core CLR discussions, I figured out where the disconnect is. First, the two popular sources that state writes also include a release fence or just that they cannot be reordered:

Turns out that the ".NET Framework 2 model" is not inherited by all later versions and ports of the framework. The most enlightening article ended up being this one: C# - The C# Memory Model in Theory and Practice, Part 2. The author explains the more relaxed CLR implementations for IA64 and ARM. Essentially, normal writes can be reordered, and thus memory fences are needed in some situations. (How they've decided to insert a release fence before a normal write to a reference type field on the heap strikes me as a desperate hack for maintaining compatibility with code written for x86, but it's well justified in the historical context and the context of the popular lock-free object publication pattern.)

Conclusion: volatile is needed for the SpinLock field in a generic ".NET" implementation to ensure writes under the lock occur before the lock is released--without taking dependence on the stronger ".NET Framework 2 model" or the CPU architecture.

Solution 2:[2]

After reading a few sources, I had the impression that volatile only affects reordering done by compiler/runtime during optimization. The C# 5.0 Reference indicates it's more than this:

These optimizations can be performed by the compiler, by the run-time system, or by hardware

And then I came across this little article http://www.codeproject.com/Articles/31283/Volatile-fields-in-NET-A-look-inside

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Machavity