mercredi 30 octobre 2019

Why std::memory_order_relaxed is preferred at CAS loop when failing?

When it comes to implementing CAS Loop using std::atmoic, cppreference in this link gives the following demonstration for push:

template<typename T>
class stack
{
    std::atomic<node<T>*> head;
 public:
    void push(const T& data)
    {
      node<T>* new_node = new node<T>(data);
      new_node->next = head.load(std::memory_order_relaxed);

      while(!head.compare_exchange_weak(new_node->next, new_node,
                                        std::memory_order_release,
                                        std::memory_order_relaxed /* Eh? */));
    }
};

Now, I don't understand how come std::memory_order_relaxed is used for the failure case, because as far as I understand, compare_exchange_weak (same for -strong but I'll just use the weak version for convenience) is a load operation at failure, which means it loads from a successful CAS operation in another thread with std::memory_order_release, and thus it should use std::memory_order_acquire instead to be synchronized-with...?

What if, hypothetically, the 'relaxed load' gets one of the old values, ending up failing again and again, staying in the loop for extra time?

The following scratchy picture is where my brain is stuck at.

enter image description here

So to sum up my question,

  • Why not std::memory_order_acquire, instead of std::memory_order_relaxed at failure?
  • What makes std::memory_order_relaxed sufficient?
  • Does std::memory_order_relaxed on failure mean (potentially) more looping?
  • Likewise, does std::memory_order_acquire on failure mean (potentially) less looping? (beside the downside of the performance)

Aucun commentaire:

Enregistrer un commentaire