Why does the Boost library use an m_generation variable in its implementation of a thread barrier?
Asked Answered
I

1

2

The boost library (before the C++11 standard), offered support for threads. As part of its support, it also offers the implementation of a "barrier", a simple class which allows synchronization. To quote the boost website:

"A barrier is a simple concept. Also known as a rendezvous, it is a synchronization point between multiple threads. The barrier is configured for a particular number of threads (n), and as threads reach the barrier they must wait until all n threads have arrived. Once the n-th thread has reached the barrier, all the waiting threads can proceed, and the barrier is reset."

The implementation of the main function of the barrier (wait), as of Boost 1.54, is shown below:

bool wait()
{
    boost::mutex::scoped_lock lock(m_mutex);
    unsigned int gen = m_generation;

    if (--m_count == 0)
    {
        m_generation++;
        m_count = m_threshold;
        m_cond.notify_all();
        return true;
    }

    while (gen == m_generation)
        m_cond.wait(lock);
    return false;
}

It can be seen that the barrier is reusable: Once constructed, it doesn't need to be destroyed after its first use.

My question now is: What is the variable m_generation for? I am assuming the writers of the boost library had a reason to include it. It is incremented each time the barrier is reset/ready to be reused, but to what end? It is a private variable, thus it cannot be read out from the outside. The same problem could just as easily be solved with a simple internal bool variable inside the wait() function, without having a private class variable.

Irresoluble answered 4/10, 2014 at 7:56 Comment(0)
H
5

In a nutshell, m_generation is needed to deal with spurious wakeups.

The generation counter is used in conjunction with the condition variable to signal to all threads waiting on the barrier that they are free to proceed:

  • Once there are m_threshold threads that have reached the barrier, its generation number gets bumped up, and the condition variable is signalled. This causes the waiting threads (i.e. those that have reached the barrier earlier) to wake up from m_cond.wait(lock).

  • Now, the waiting threads can wake up from m_cond.wait(lock) for other reasons. This is where m_generation comes in: if it's changed, then the barrier has been reset and the thread can proceed. If m_generation still contains the same value, the thread needs to go back into m_cond.wait(lock).

Having an automatic variable inside wait() would not achieve this, since each thread would have its own instance.

Hulahula answered 4/10, 2014 at 8:7 Comment(1)
Ah! Thank you very much. I had overlooked the fact that an internal variable would lead to every thread having its own instance. Now it makes sense. Thank you very much.Irresoluble

© 2022 - 2024 — McMap. All rights reserved.