Search code examples
multithreadingc++11barrier

Implementing boost::barrier in C++11


I've been trying to get a project rid of every boost reference and switch to pure C++11.

At one point, thread workers are created which wait for a barrier to give the 'go' command, do the work (spread through the N threads) and synchronize when all of them finish. The basic idea is that the main loop gives the go order (boost::barrier .wait()) and waits for the result with the same function.

I had implemented in a different project a custom made Barrier based on the Boost version and everything worked perfectly. Implementation is as follows:

Barrier.h:

class Barrier {
public:
    Barrier(unsigned int n);
    void Wait(void);
private:
    std::mutex counterMutex;
    std::mutex waitMutex;

    unsigned int expectedN;
    unsigned int currentN;
};

Barrier.cpp

Barrier::Barrier(unsigned int n) {
    expectedN = n;
    currentN = expectedN;
}

void Barrier::Wait(void) {
    counterMutex.lock();

    // If we're the first thread, we want an extra lock at our disposal

    if (currentN == expectedN) {
        waitMutex.lock();
    }

    // Decrease thread counter

    --currentN;

    if (currentN == 0) {
        currentN = expectedN;
        waitMutex.unlock();

        currentN = expectedN;
        counterMutex.unlock();
    } else {
        counterMutex.unlock();

        waitMutex.lock();
        waitMutex.unlock();
    }
}

This code has been used on iOS and Android's NDK without any problems, but when trying it on a Visual Studio 2013 project it seems only a thread which locked a mutex can unlock it (assertion: unlock of unowned mutex).

Is there any non-spinning (blocking, such as this one) version of barrier that I can use that works for C++11? I've only been able to find barriers which used busy-waiting which is something I would like to prevent (unless there is really no reason for it).


Solution

  • class Barrier {
    public:
        explicit Barrier(std::size_t iCount) : 
          mThreshold(iCount), 
          mCount(iCount), 
          mGeneration(0) {
        }
    
        void Wait() {
            std::unique_lock<std::mutex> lLock{mMutex};
            auto lGen = mGeneration;
            if (!--mCount) {
                mGeneration++;
                mCount = mThreshold;
                mCond.notify_all();
            } else {
                mCond.wait(lLock, [this, lGen] { return lGen != mGeneration; });
            }
        }
    
    private:
        std::mutex mMutex;
        std::condition_variable mCond;
        std::size_t mThreshold;
        std::size_t mCount;
        std::size_t mGeneration;
    };