31 ThreadBlockerWhileUsedFast3
No longer used so the code is only recorded here in this web-page.
Incorrect version
Provides a thread safe counter which can be incremented and decremented (with both prefix and postfix ++, -- operators).
Allows for waiting until the counter reaches zero. Assumes the counter never goes below zero.
This specialised version assumes Wait() is called exactly once, allowing for a more efficient implementation.
In the following implementation count_ is offset by +1 in order to avoid zero transitions until Wait() is called. Indeed it is assumed that count_ transitions to zero exactly once after Wait() has been called. Therefore operator++() never needs to deal with the case of a 0-->1 transition (this is asserted with cxAssert(c > 1)).
class ThreadBlockerWhileUsedFast3_bad
{
public:
ThreadBlockerWhileUsedFast3_bad(int count = 0) : count_(count+1), zero_(false) { cxAssert(count >= 0); }
~ThreadBlockerWhileUsedFast3_bad()
{
cxAssert(count_ == 0);
cxAssert(zero_);
}
int operator++()
{
int c = ++count_;
cxAssert(c > 1); // assert no 0-->1 transition
return c-1; // return c-1 not c to account for the +1 offset
}
int operator--()
{
int c = --count_;
cxAssert(c >= 0);
if (c == 0)
{
std::lock_guard<std::mutex> lock(mutex_);
zero_ = true;
cv_.notify_one();
return 0;
}
else
{
return c-1; // return c-1 not c to account for the +1 offset
}
}
int operator++(int) { return operator++()-1; }
int operator--(int) { return operator--()+1; }
void Wait()
{
std::unique_lock<std::mutex> lock(mutex_);
if (--count_ == 0)
{
zero_ = true;
}
else
{
// We have decremented count_, now wait for it to transition to 0.
cv_.wait(lock,[this]{return zero_;});
}
}
private:
std::atomic<int> count_;
std::mutex mutex_;
std::condition_variable cv_; // signalled <=> zero_ = true
bool zero_;
};
Problem: ThreadBlockerWhileUsedFast3_bad doesn't return the count correctly and this breaks TcpServer and TcpClient which rely on the return value of ++ and -- operators.
Fixed version
So instead we use two atomic counters
class ThreadBlockerWhileUsedFast3
{
public:
ThreadBlockerWhileUsedFast3(int count = 0) : offset_count_(count+1), count_(count), zero_(false) { cxAssert(count >= 0); }
~ThreadBlockerWhileUsedFast3()
{
cxAssert(count_ == 0);
cxAssert(offset_count_ == 0);
cxAssert(zero_);
}
int operator++()
{
int c = ++count_;
++offset_count_;
cxAssert(c > 0); // assert no 0-->1 transition
return c;
}
int operator--()
{
int c = --count_;
cxAssert(c >= 0);
if (--offset_count_ == 0)
{
std::lock_guard<std::mutex> lock(mutex_);
zero_ = true;
cv_.notify_one();
}
return c;
}
int operator++(int) { return operator++()-1; }
int operator--(int) { return operator--()+1; }
void Wait()
{
std::unique_lock<std::mutex> lock(mutex_);
if (--offset_count_ == 0)
{
zero_ = true;
}
else
{
// We have decremented count_, now wait for it to transition to 0.
cv_.wait(lock,[this]{return zero_;});
}
}
private:
std::atomic<int> offset_count_;
std::atomic<int> count_;
std::mutex mutex_;
std::condition_variable cv_; // signalled <=> zero_ = true
bool zero_;
};