// spinlockAcquireRelease.cpp
#include <atomic>
#include <thread>
class Spinlock{
std::atomic_flag flag;
public:
Spinlock(): flag(ATOMIC_FLAG_INIT) {}
void lock(){
while(flag.test_and_set(std::memory_order_acquire) ); // line 12
}
void unlock(){
flag.clear(std::memory_order_release);
}
};
Spinlock spin;
void workOnResource(){
spin.lock();
// shared resource
spin.unlock();
}
int main(){
std::thread t(workOnResource);
std::thread t2(workOnResource);
t.join();
t2.join();
}
In the notes, it is said:
In case more than two threads use the spinlock, the acquire semantic of the lock method is not sufficient. Now the lock method is an acquire-release operation. So the memory model in line 12 [the call to
flag.test_and_set(std::memory_order_acquire)
] has to be changed tostd::memory_order_acq_rel
.
Why does this spinlock work with 2 threads but not with more than 2? What is an example code that cause this spinlock to become wrong?
Source: https://www.modernescpp.com/index.php/acquire-release-semantic
std::memory_order_acq_rel
is not required.
Mutex synchronization is between 2 threads.. one releasing the data and another acquiring it.
As such, it is irrelevant for other threads to perform a release or acquire operation.
Perhaps it is more intuitive (and efficient) if the acquire is handled by a standalone fence:
void lock(){
while(flag.test_and_set(std::memory_order_relaxed) )
;
std::atomic_thread_fence(std::memory_order_acquire);
}
void unlock(){
flag.clear(std::memory_order_release);
}
Multiple threads can spin on flag.test_and_set
,
but one manages to read the updated value and set it again (in a single operation).. only that thread acquires the protected data after the while-loop.