Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fields read from/written by several threads, Interlocked vs. volatile

There are a fair share of questions about Interlocked vs. volatile here on SO, I understand and know the concepts of volatile (no re-ordering, always reading from memory, etc.) and I am aware of how Interlocked works in that it performs an atomic operation.

But my question is this: Assume I have a field that is read from several threads, which is some reference type, say: public Object MyObject;. I know that if I do a compare exchange on it, like this: Interlocked.CompareExchange(ref MyObject, newValue, oldValue) that interlocked guarantees to only write newValue to the memory location that ref MyObject refers to, if ref MyObject and oldValue currently refer to the same object.

But what about reading? Does Interlocked guarantee that any threads reading MyObject after the CompareExchange operation succeeded will instantly get the new value, or do I have to mark MyObject as volatile to ensure this?

The reason I'm wondering is that I've implemented a lock-free linked list which updates the "head" node inside itself constantly when you prepend an element to it, like this:

[System.Diagnostics.DebuggerDisplay("Length={Length}")]
public class LinkedList<T>
{
    LList<T>.Cell head;

    // ....

    public void Prepend(T item)
    {
        LList<T>.Cell oldHead;
        LList<T>.Cell newHead;

        do
        {
            oldHead = head;
            newHead = LList<T>.Cons(item, oldHead);

        } while (!Object.ReferenceEquals(Interlocked.CompareExchange(ref head, newHead, oldHead), oldHead));
    }

    // ....
}

Now after Prepend succeeds, are the threads reading head guaranteed to get the latest version, even though it's not marked as volatile?

I have been doing some empirical tests, and it seems to be working fine, and I have searched here on SO but not found a definitive answer (a bunch of different questions and comments/answer in them all say conflicting things).

like image 461
thr Avatar asked Dec 06 '11 11:12

thr


People also ask

What is interlocked exchange?

Interlock. Exchange returns the original value while performing an atomic operation. The whole point is to provide a locking mechanism. So it is actually two operations: read original value and set new value. Those two together are not atomic.

What is interlocked C#?

It lets you do small and well-defined operations safely in a multi-threaded environment: for instance, if you want two threads to increment the same variable, you can use Interlocked to do it instead of acquiring a heavyweight lock and using the "regular increment".

Why volatile is used in C#?

The volatile keyword in C# is used to inform the JIT compiler that the value of the variable should never be cached because it might be changed by the operating system, the hardware, or a concurrently executing thread.

What is volatile C#?

The volatile keyword indicates that a field might be modified by multiple threads that are executing at the same time. The compiler, the runtime system, and even hardware may rearrange reads and writes to memory locations for performance reasons.


1 Answers

Does Interlocked guarantee that any threads reading MyObject after the CompareExchange operation succeeded will instantly get the new value, or do I have to mark MyObject as volatile to ensure this?

Yes, subsequent reads on the same thread will get the new value.

Your loop unrolls to this:

oldHead = head;
newHead = ... ;

Interlocked.CompareExchange(ref head, newHead, oldHead) // full fence

oldHead = head; // this read cannot move before the fence

EDIT:

Normal caching can happen on other threads. Consider:

var copy = head;

while ( copy == head )
{
}

If you run that on another thread, the complier can cache the value of head and never see the update.

like image 166
Nick Butler Avatar answered Oct 03 '22 12:10

Nick Butler