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_;
};