views:

94

answers:

3

Is there an equivalent of the XADD command in .Net? This is, after all, the most efficient method of locking / checking locks for critical sections or for ensuring accurate increments in a multi-threaded environment.

I looked through the IL opcodes, but couldn't find an equivalent.

+1  A: 

The closest equivelent in .NET would be to use the Interlocked class. For example, you can use Interlocked.Add to have safe, accurate increments in a multi-threaded environment.

Reed Copsey
IanC
A: 

Take a look at the Interlocked class paying particular attention to Interlocked.Add. The following is an example of its usage in IL.

.method private hidebysig static void Main(string[] args) cil managed
{
    .entrypoint
    .maxstack 2
    .locals init (
        [0] int32 a,
        [1] int32 b)
    L_0000: ldc.i4.5 
    L_0001: stloc.0 
    L_0002: ldc.i4.7 
    L_0003: stloc.1 
    L_0004: ldloca.s a
    L_0006: ldloc.1 
    L_0007: call int32 [mscorlib]System.Threading.Interlocked::Add(int32&, int32)
    L_000c: pop 
    L_000d: ret 
}
Brian Gideon
Perfect! Thanks.
IanC
+3  A: 

There's a lot more to acquiring a lock than a simple CPU instruction. The cost of trying to acquire it and not getting it is very high. Documented to be somewhere between 2000 and 10,000 machine instructions. The higher number for thread context switches to a thread in another process, that requires reloading the virtual memory page translation tables.

A very common strategy that makes sense on multi-core CPUs is a spin-wait. The code goes into a tight loop, burning CPU cycles heavily, trying to acquire the lock. The exact amount of time it spends in this loop is technically a tunable item, but doesn't make much difference in practice.

Anyhoo, it is the job of the CLR and the compiler is to hide the implementation details. One core class that does this is the Monitor class. It is used when you use the lock statement in C# for example, the compiler automatically translates that to a Monitor.Enter call, auto-generating a try and finally block, the finally block executes the Leave() method.

The implementation of these methods is in the CLR. There's a fairly good chunk of code there, another thing it does is dealing with a "fairness". Which makes sure that threads cannot starve, trying to acquire the lock. That code is written in C++, still pretty far removed from raw CPU instructions. It ultimately boils down to the actual code that implements the actual lock. Yes, that's written in assembly, at least in the shared source version of the CLR. That code lives in the PAL (Platform Adapter Layer), the x86 version of it looks like this:

FASTCALL_FUNC CompareExchangeMP,12
        _ASSERT_ALIGNED_4_X86 ecx
        mov     eax, [esp+4]    ; Comparand
  lock  cmpxchg [ecx], edx
        retn    4               ; result in EAX
FASTCALL_ENDFUNC CompareExchangeMP

The cmpxchng CPU instruction with the lock prefix is the typical one to implement locks. Your xadd is covered too though, used for Interlocked.Add:

FASTCALL_FUNC ExchangeAddUP,8
        _ASSERT_ALIGNED_4_X86 ecx
        xadd    [ecx], edx      ; Add Value to Target
        mov     eax, edx
        retn
FASTCALL_ENDFUNC ExchangeAddUP

But that isn't typically used for locks. If you want to see this for yourself, download the SSCLI20 source code and have a look-see at clr\src\wm\i386\asmhelpers.asm. Whether that is actually used in the currently shipping version of the CLR is an open question. It's pretty core, so somewhat likely. The Monitor method implementations are in clr\vm\syncblk.cpp, AwareLock class. I'm pretty sure that the SSCLI20 version of it is not what's running on your machine, they've been tinkering with the "fairness" algorithm.

Hans Passant
Thanks for the detailed answer, Hans :)
IanC