Skip to content

Latest commit

 

History

History
580 lines (456 loc) · 11.3 KB

04_spurious_wakeup_animation.en.md

File metadata and controls

580 lines (456 loc) · 11.3 KB

Spurious wake-up

Coders School

Spurious wake-up

template <typename T>
class WaitQueue {
    deque<T> queue_;
    mutable mutex m_;
    condition_variable nonEmpty_;
    using Lock = unique_lock<mutex>;

public:
    void push(const T & element) {
        {
            Lock l(m_);
            queue_.push_front(element);
        }
        nonEmpty_.notify_all();
    }
    T pop() {
        Lock l(m_);
        auto hasData = [&]{ return not queue_.empty(); };
        nonEmpty_.wait(l, hasData);
        auto top = queue_.back();
        queue_.pop_back();
        return top;
    }
};
Producer
Queue
Element 1
Condvar
Mutex
Consumer A
Consumer B

Spurious wake-up

template <typename T>
class WaitQueue {
    deque<T> queue_;
    mutable mutex m_;
    condition_variable nonEmpty_;
    using Lock = unique_lock<mutex>;

public:
    void push(const T & element) {
        {
            Lock l(m_);
            queue_.push_front(element);
        }
        nonEmpty_.notify_all();
    }
    T pop() {
        Lock l(m_);
        auto hasData = [&]{ return not queue_.empty(); };
        nonEmpty_.wait(l, hasData);
        auto top = queue_.back();
        queue_.pop_back();
        return top;
    }
};
Producer
Queue
Element 1
Condvar
Mutex
Consumer A
Consumer B

Spurious wake-up

template <typename T>
class WaitQueue {
    deque<T> queue_;
    mutable mutex m_;
    condition_variable nonEmpty_;
    using Lock = unique_lock<mutex>;

public:
    void push(const T & element) {
        {
            Lock l(m_);
            queue_.push_front(element);
        }
        nonEmpty_.notify_all();
    }
    T pop() {
        Lock l(m_);
        auto hasData = [&]{ return not queue_.empty(); };
        nonEmpty_.wait(l, hasData);
        auto top = queue_.back();
        queue_.pop_back();
        return top;
    }
};
Producer
Queue
Element 1
Condvar
Consumer A
Mutex
Consumer B

Spurious wake-up

template <typename T>
class WaitQueue {
    deque<T> queue_;
    mutable mutex m_;
    condition_variable nonEmpty_;
    using Lock = unique_lock<mutex>;

public:
    void push(const T & element) {
        {
            Lock l(m_);
            queue_.push_front(element);
        }
        nonEmpty_.notify_all();
    }
    T pop() {
        Lock l(m_);
        auto hasData = [&]{ return not queue_.empty(); };
        nonEmpty_.wait(l, hasData);
        auto top = queue_.back();
        queue_.pop_back();
        return top;
    }
};
Producer
Queue
Condvar
Consumer A
Mutex
Element 1
Consumer B

Spurious wake-up

template <typename T>
class WaitQueue {
    deque<T> queue_;
    mutable mutex m_;
    condition_variable nonEmpty_;
    using Lock = unique_lock<mutex>;

public:
    void push(const T & element) {
        {
            Lock l(m_);
            queue_.push_front(element);
        }
        nonEmpty_.notify_all();
    }
    T pop() {
        Lock l(m_);
        auto hasData = [&]{ return not queue_.empty(); };
        nonEmpty_.wait(l, hasData);
        auto top = queue_.back();
        queue_.pop_back();
        return top;
    }
};
Producer
Queue
Condvar
Mutex
Consumer A
Element 1
Consumer B

Spurious wake-up

template <typename T>
class WaitQueue {
    deque<T> queue_;
    mutable mutex m_;
    condition_variable nonEmpty_;
    using Lock = unique_lock<mutex>;

public:
    void push(const T & element) {
        {
            Lock l(m_);
            queue_.push_front(element);
        }
        nonEmpty_.notify_all();
    }
    T pop() {
        Lock l(m_);
        nonEmpty_.wait(l);
        auto top = queue_.back();
        queue_.pop_back();
        return top;
    }
};
Producer
Queue
Condvar
Mutex
Consumer A
Element 1
Consumer B

Spurious wake-up

template <typename T>
class WaitQueue {
    deque<T> queue_;
    mutable mutex m_;
    condition_variable nonEmpty_;
    using Lock = unique_lock<mutex>;

public:
    void push(const T & element) {
        {
            Lock l(m_);
            queue_.push_front(element);
        }
        nonEmpty_.notify_all();
    }
    T pop() {
        Lock l(m_);
        nonEmpty_.wait(l);
        auto top = queue_.back();
        queue_.pop_back();
        return top;
    }
};
Producer
Queue
Condvar
Consumer A
Element 1
Consumer B
Mutex

Spurious wake-up

template <typename T>
class WaitQueue {
    deque<T> queue_;
    mutable mutex m_;
    condition_variable nonEmpty_;
    using Lock = unique_lock<mutex>;

public:
    void push(const T & element) {
        {
            Lock l(m_);
            queue_.push_front(element);
        }
        nonEmpty_.notify_all();
    }
    T pop() {
        Lock l(m_);
        nonEmpty_.wait(l);
        auto top = queue_.back();
        queue_.pop_back();
        return top;
    }
};
Producer
Queue
Condvar
Consumer A
Element 1
Consumer B
Mutex
???

Undefined Behavior


Spurious wake-up

template <typename T>
class WaitQueue {
    deque<T> queue_;
    mutable mutex m_;
    condition_variable nonEmpty_;
    using Lock = unique_lock<mutex>;

public:
    void push(const T & element) {
        {
            Lock l(m_);
            queue_.push_front(element);
        }
        nonEmpty_.notify_all();
    }
    T pop() {
        Lock l(m_);
        nonEmpty_.wait(l);
        auto top = queue_.back();
        queue_.pop_back();
        return top;
    }
};
Producer
Queue
Condvar
Mutex
Consumer A
Element 1
Consumer B
???

Undefined Behavior


Spurious wake-up

  • The thread waiting on the condition_variable periodically wakes up from time to time.
  • A unique_lock is required to wait on a condition variable. During sleep, the thread unlocks it, and when it wakes up, it locks it again.
  • Predicate added to the wait() function makes spurious wake-ups not dangerous.
    • Only when the condition is met, the thread will continue its work.
    • If the condition is not met, the thread releases the lock and goes to sleep again.
  • ALWAYS use wait() with predicate to avoid potential problems.