简体   繁体   中英

Is it safe to use a dummy fake lock with std::condition_variable_any?

What I am looking for is something like the wait_queue_t in linux kernel. My underlying data structure related to the synchronization is lockfree thus does not need protection of a mutex.

Having to acquire std::mutex just to be able to use std::condition_variable for blocking wait seems to introduce unnecessary overhead.

I know there is futex for linux and WaitOnAddress for Windows but I am more interested in language standard stuff here.

According to cppreference wiki std::condition_variable_any can work with any custom Lockable .

So what if I use it with a dummy fake lock like the following:

class FakeLock
{
public:
    inline void lock() {}
    inline void unlock() {}
    inline bool try_lock() { return true; }
};

template<typename WaitLock=FakeLock>
class WaitQueue
{
private:
    WaitLock m_lock;
    std::condition_variable_any m_cond;

public:
    inline void wait()
    {   
        std::unique_lock<WaitLock> lock(m_lock);

        m_cond.wait(lock);
    }   

    inline void notify_one()
    {   
        m_cond.notify_one();
    }   

    inline void notify_all()
    {   
        m_cond.notify_all();
    }   
};

Is there any potential risk of unexpected behavior to use the WaitQueue above in the way linux kernel wait_queue_t is used?

Thinking about the problem again I think I got an answer.

Given a class LockFreeStack which is a lockfree stack.

consider the following code:

WaitQueue wq;
LockFreeStack<std::string> stack;

std::thread t1([&]() {
    while (true) {
         // sleep for some time
         stack.push(/* random string */);          // (1.1)
         wq.notify_one();                          // (1.2)
    }
});

std::thread t2([&]() {
    while (true) {
         if (stack.empty())                        // (2.1)
             wq.wait();                            // (2.2)
         auto str = stack.pop();
         // print string
    }
});

Without guarding wait_queue / conditional variable with a real lock, it is possible for the two threads to execute in the following sequence:

(2.1)
(1.1)
(1.2)
(2.2)

Making thread t2 perfectly miss the latest update from t1 . t2 will only be able to resume execution after the next notify_one() call.

A real lock is necessary to guarantee that the conditional variable is changed atomically with the actual data/state of interest.

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