Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lockless using InterlockedCompareExchange

I am trying to make following snip of code lockless using interlocked operations, Any idea how to translate this?

if (m_Ref == 0xFFFF)
    m_Ref = 1;
else
{
    if (++m_Ref == 1)
        CallSomething(); //

}

I was thinking something like

if (InterlockedCompareExchange(&m_Ref, 1, 0xFFFF) != 0xFFFF))
{
    if (InterlockedIncrement(&m_Ref) == 1)
         CallSomething();
}

Is there any issues/race in this?

like image 858
Suresh Avatar asked May 02 '11 22:05

Suresh


2 Answers

This looks correct at a superficial glance, but every time you use two interlocked operations in a row you are exposing yourself to the ABA problem. In this case one thread fail to change it from 0xFFFF to 1 (the ICX returns !=0xFFFF) so it goes ahead and takes the if branch and increments it. Before it runs the InterlockedIncrement another threads changes the m_ref back to 0xFFFF and the original thread increments 0xFFFF. Depending on the type/semantics of m_ref the effect will wary, but will surely be bad.

You should do one single ICX operation, for both the 0xFFF to 1 and X to X+1, and always retry if you lost the ICX:

volatile <type> m_ref;

<type> ref, newRef, icxref;
do
{
   ref = m_ref;
   newRef = (0xFFFF == ref) ? 1 : ++ref;
   icxref = InterlockedCompareExchange (&m_ref, newRef, ref);
} while (icxref != ref);
if (newRef == 1 && ref != 0xFFFF)
{
   DoSomething ();
}
like image 121
Remus Rusanu Avatar answered Sep 28 '22 15:09

Remus Rusanu


Yes there's a race. Another context could do a lot in between InterlockedCompareExchange and InterlockedIncrement

like image 30
Erik Avatar answered Sep 28 '22 15:09

Erik