ThreadBlockerWhileUsed.h

// ThreadBlockerWhileUsed.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2013

#pragma once
#ifndef Ceda_cxMessage_ThreadBlockerWhileUsed_H
#define Ceda_cxMessage_ThreadBlockerWhileUsed_H

#include "cxMessage.h"
#include "Ceda/cxUtils/CedaAssert.h"
#include <mutex>
#include <condition_variable>

namespace ceda
{
class ThreadBlockerWhileUsed
{
    cxNotCloneable(ThreadBlockerWhileUsed)
    
public:
    explicit ThreadBlockerWhileUsed(int count = 0) : count_(count) 
    {
        // todo temporarily commented out because TcpClient is broken
        //cxAssert(count_ > 0);
    }

    ~ThreadBlockerWhileUsed()
    {
        // todo: this tripped on JigsawServer on Linux box
        cxAssert(count_ == 0);
    }

    int operator++() 
    {
        std::lock_guard<std::mutex> lock(mutex_);
        // todo temporarily commented out because TcpClient is broken
        //cxAssert(count_ > 0);                       // Can't allow count to be zero because this implementation assumes there is at most one transition to zero
        ++count_;
        return count_;
    }

    int operator--()
    {
        std::lock_guard<std::mutex> lock(mutex_);
        cxAssert(count_ > 0);
        if (--count_ == 0)
        {
            // It is important that this notification be performed while holding a lock on the mutex,
            // even though a thread waiting on the condition may wake up only to immediately block again.
            // For example, consider that the thread calling Wait() would exit the program if the condition is 
            // satisfied, causing destruction of the condition_variable.  Then a spurious wakeup after mutex 
            // unlock but before notify would result in notify called on a destroyed object.
            cv_.notify_all();
        }
        return count_;
    }

    // Postfix
    int operator++(int) { return operator++()-1; }
    int operator--(int) { return operator--()+1; }

    void Wait()
    {
        std::unique_lock<std::mutex> lock(mutex_);
        cv_.wait(lock,[this]{return count_ == 0;});
    }
private:
    std::mutex mutex_;
    std::condition_variable cv_;
    int count_;
};
} // namespace ceda
#endif // include guard