//---------------------------------------------------------
// For conditions of distribution and use, see
// https://github.com/preshing/modern-cpp-threading/blob/master/LICENSE
//---------------------------------------------------------

#ifndef __MCPPT_AUTO_RESET_EVENT_COND_VAR_H__
#define __MCPPT_AUTO_RESET_EVENT_COND_VAR_H__

#include <cassert>
#include <mutex>
#include <condition_variable>

//---------------------------------------------------------
// AutoResetEventCondVar
//---------------------------------------------------------
class AutoResetEventCondVar
{
private:
    // m_status == 1: Event object is signaled.
    // m_status == 0: Event object is reset and no threads are waiting.
    // m_status == -N: Event object is reset and N threads are waiting.
    std::mutex m_mutex;
    int m_status;
    std::condition_variable m_condition;

public:
    AutoResetEventCondVar(int initialStatus = 0) : m_status(initialStatus)
    {
        assert(initialStatus >= 0 && initialStatus <= 1);
    }

    void signal()
    {
        // Increment m_status atomically via critical section.
        std::lock_guard<std::mutex> lock(m_mutex);
        int oldStatus = m_status;
        if (oldStatus == 1)
            return; // Event object is already signaled.
        m_status++;
        if (oldStatus < 0)
            m_condition.notify_one(); // Release one waiting thread.
    }

    void wait()
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        int oldStatus = m_status;
        m_status--;
        assert(oldStatus <= 1);
        if (oldStatus < 1) {
            m_condition.wait(lock);
        }
    }
};

#endif // __MCPPT_AUTO_RESET_EVENT_COND_VAR_H__
