简体   繁体   中英

Are the memory barriers correct for this lock?

Is this correct? And am I correct in assuming that applying the memory ordering on the std::atomic_flag does NOT provide synchronization for general-purpose locks?

#include <atomic>

class Spinlock
{
public:
    Spinlock(): f(ATOMIC_FLAG_INIT) {}

    void lock()
    {
        while(f.test_and_set(std::memory_order_relaxed));
        std::atomic_thread_fence(std::memory_order_acquire);
    }
    void unlock()
    {
        std::atomic_thread_fence(std::memory_order_release);
        f.clear(std::memory_order_relaxed);
    }

private:
    std::atomic_flag f;
};

I'm sorry if this is a stupid question, but I feel like an std::atmoic_thread_fence IS necessary for a general-purpose lock, and that applying memory_order_acquire on the test_and_set and memory_order_release on the clear is not enough, but I'm also not sure.

Overall it is correct. Since you used 'std::memory_order_relaxed' in the test_and_set function then without the 'atomic_thread_fence' call there is nothing that would prevent reordering of operations done before and after the Mutex::lock. Since it is expected that 'Mutex::lock' acts as a memory barrier then the call to 'atomic_thread_fence' becomes necessary. But I believe the same effect could be achieved by using 'std::memory_order_acquire' in the 'test_and_set function'. See this: http://en.cppreference.com/w/cpp/atomic/atomic_flag

The usual pattern is to use test_and_set(memory_order_acquire) and clear(memory_order_release) . But I suspect you know that already.

According to the standard section 29.8 [atomic.fences] (2):

A release fence A synchronizes with an acquire fence B if there exist atomic operations X and Y, both operating on some atomic object M, such that A is sequenced before X, X modifies M, Y is sequenced before B, and Y reads the value written by X or a value written by any side effect in the hypothetical release sequence X would head if it were a release operation.

In your code, A is the fence in your unlock() function; X is the clear() ; Y is the fence in your lock() function; and B is the test_and_set() . So your code meets the requirements of this section of the standard and therefore your unlock() and lock() functions are properly synchronized.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM