Question

According to Antony Williams' book C++ Concurrency in Action a spinlock can be implemented as follows:

class spinlock_mutex {
  std::atomic_flag flag;

public:
  spinlock_mutex() : flag(ATOMIC_FLAG_INIT) {}

  void lock() {
    while (flag.test_and_set(std::memory_order_acquire)) ;
  }

  void unlock() {
   flag.clear(std::memory_order_release);
  }
};

If I understand correctly, the memory_order_acquire tag ensures visibility of the most recent store operation that used the memory_order_release tag. ("A release operation synchronizes-with an acquire operation.")

Since test_and_set is also a store operation I would expect that a memory_order_acq_rel tag is required inside the lock() method in order to ensure visibility of the locked state in other threads that attempt to lock the mutex.

Why is memory_order_acquire sufficient?

Was it helpful?

Solution

The acquire and release are related not to this atomic variable, but to the rest of variables read/written in each one of the threads.

To make it more obvious, without the release in unlock() and the acquire in the lock() the flag would still be visible. The problem is that a write to a variable protected by the spin lock may be pushed after the lock is released, introducing a race condition. In the same way a read of that variable inside the lock might be moved to before the lock()

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top