It is possible to getting stuck in the compare_exchange's loop?

192 Views Asked by At

Consider this code (from 'C++ concurrency in action' [Second edition]: page 212):

void LockFreeStack::pop (stack_data& result)
{
    Node* old_head = head.load();
    while(!head.compare_exchange_weak(old_head, old_head->next))
        ;
    result = old_head->data;
}

I think it's possible to thread-one execute pop() and after executing first line (loading head) time slicing happened to thread-two, which is executing push(T&). So now value of head atomic variable is not equal to old_head and while-loop won't break.
It's right ?

2

There are 2 best solutions below

0
On BEST ANSWER

assuming head is a std::atomic<Node*> then the code is correct as when compare_exchange_weak fails it loads the current value of the variable into its first parameter, after the compare_exchange_weak call old_head will always contain the current value of head.

The code is roughly equivalent to doing the following without atomics:

Node* old_head = head;
while (true)
{
  std::unique_lock lock(mutex);
  if (old_head == head)
  {
     head = old_head->next;
     break;
  }
  else
  {
     old_head = head;
  }
}

Concurrent calls to pop are therefore safe and shouldn't block forever (other than due to other threads constantly calling pop and somehow always setting the value before the current thread gets a chance).

0
On

As already explained by @Alan Birtles, you will not be stuck in the while-loop. However, it is important to note that your code will most likely suffer from the ABA problem. Consider to the following scenario:

  • the stack looks like this: A->B->C (i.e., head points to A)
  • thread 1 loads the head (i.e., the address of A) and A's next pointer (B), but before it can perform the CAS, it is interrupted.
  • thread 2 pops A, then B, and then pushes A, i.e., the stack now looks like this: A->C
  • thread 1 resumes and happily updates the head from A to B -> boom!

There are several possible solutions to avoid the ABA problem, like tagged pointers or concurrent memory reclamation schemes.