13 ThreadBlockerWhileUsed
ThreadBlockerWhileUsed is defined purely in a header file.
This is a problematic, deprecated approach which involves trying to block a thread until a reference count is decremented down to zero.
It was being used to block a thread trying to close an object that had pending asynchronous I/O requests.
class X
{
X() : blocker_(1) {}
void Close()
{
...
--blocker_;
blocker_.Wait();
delete this;
}
void PostSomeAsyncOperation()
{
++blocker_;
post([this]()
{
// handle completion
--blocker_;
});
}
ThreadBlockerWhileUsed blocker_;
};
ThreadBlockerWhileUsed.h
ThreadBlockerWhileUsed provides a thread safe counter which can be incremented and decremented
(with both prefix and postfix ++, -- operators).
It allows for waiting until the counter is zero.
It assumes the counter never goes below zero.
// ThreadBlockerWhileUsed.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2013
#pragma once
#ifndef Ceda_cxThread_ThreadBlockerWhileUsed_H
#define Ceda_cxThread_ThreadBlockerWhileUsed_H
#include "cxThread.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