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