Using Interlocked.CompareExchange with a class

Bhargav Mangipudi picture Bhargav Mangipudi · Jul 14, 2011 · Viewed 13.5k times · Source

System.Threading.Interlocked.CompareExchange operator provides atomic (thus thread-safe) C# implementation of the Compare-And-Swap operation.

For example int i = 5; Interlocked.CompareExchange(ref i, 10, 5); After this command, the int i would have a value = 10. And also the compare and exchange happens atomically (single operation).

When I tried using this with a class instance, the compare fails and the values are not exchanged.

   public class X
   {
       public int y;
       public X(int val) { y = val; }
   }

Now when I do

    X a = new X(1);
    X b = new X(1);
    X c = new X(2);
    Interlocked.CompareExchange<X>(ref a, c, b);

The compare and Exchange operation fails. So, I overrided the Equals and the == operator for the class X as

    public override bool Equals(object obj) { return y == ((X) obj).y; }

So, now I get Interlocked.Equals(a,b) as true, but the CompareExchange operations still fails.

Is there any method to do this? I want to compare two class instances and assign one of them a value based on the comparision.

Answer

jalf picture jalf · Jul 14, 2011

No. It can't be done.

Interlocked.CompareExchange basically maps directly to an assembly instruction which is able to atomically compare and swap the contents of a memory address. I believe in 32-bit mode, a 64-bit version of the instruction is available (as well as 32- and 16-bit versions), and in 64-bit mode, I think a 128-bit version is available. But that's all. The CPU doesnt' have a "swap .NET class based on its specific Equals function" instruction.

If you want to swap arbitrary objects, using arbitrary equality functions, you have to do it yourself, using locks or other synchronization mechanisms.

There is an overload of the Interlocked.CompareExchange function which works on object references, but it uses reference equality for the above reason. It simply compares the references, and then swaps them.

In response to your comment, using structs would not solve the problem. Again, the CPU can only atomically compare and swap values of certain fixed sizes, and it has no notion of abstract datatypes. Reference types can be used because the reference itself has a valid size, and can be compared against another reference by the CPU. But the CPU knows nothing about the object that the reference points to.