On 4/21/2013 8:55 AM, Vicente J. Botet Escriba wrote:
Le 21/04/13 13:18, Michael Marcin a écrit :
On 4/21/2013 5:45 AM, Vicente J. Botet Escriba wrote:
Le 21/04/13 10:27, Michael Marcin a écrit :
I recently ran across the need to spawn a thread and wait for it to finish its setup before continuing.
The accepted answer seems to be using a mutex and condition variable to achieve this.
However that work clutters up the code quite a bit with the implementation details.
I came across Java's CountDownLatch which does basically the same work but bundles it up into a tidy package.
What about using boost::barrier [1]?
Best, Vicente
Ah didn't know that existed. Looks like it serves a similar purpose.
Still there are a few differences.
Looks like barrier is a bit fatter to let it handle restarting on a new generation.
Usage requires you to know the number of people that are going to call wait at construction time as well.
IF you replaced the countdown_latch with barrier in my example you introduce more synchronization than is necessary. In addition to the constructor waiting on thread_func now thread_func must wait for the constructor.
You are right. After further analysis the split of the wait() count_down() of the latch class could be complementary to the current boost::barrier class.
The difference been that with a latch the thread will not block on (2) while using a barrier would synchronize all the threads in (1) and (2) as the barrier::wait() is equivalent to a latch::count_down() and latch::wait()
There is a C++1y proposal [1] that propose having two classes. However having two classes that are really quite close seems not desirable. The problem is that boost::barrier use already a wait() function that do the count down and synchronize up to the count is zero.
I guess that the better would be to define a new set of latch classes that provides wait/count_down/count_down_and_wait. The current barrier class could be deprecated once the new classes are ready.
[1] adds the possibility to reset the counter. This doesn't seems to add any complexity to the basic latch
[1] adds the possibility to set a function that is called when the counter reach the value 0. This is in my opinion useful but would need an additional class. I don't think the names latch and barrier would be the good ones if the single difference is to be able to set this function.
boost::barrier auto reset itself once the counter reaches the value zero.
I would propose 2/3 classes
Basic Latch respond to your needs and adds some more function that don't make the implementation less efficient.
class latch { public: latch( latch const&) = delete; latch& operator=( latch const&) = delete;
/// Constructs a latch with a given count. latch( std::size_t count );
/// Blocks until the latch has counted down to zero. void wait();
bool try_wait();
template
cv_statuswait_for( const chrono::duration & rel_time ); template cv_status wait_until( const chrono::time_point & abs_time ); /// Decrement the count and notify anyone waiting if we reach zero. /// @Requires count must be greater than 0 void count_down();
/// Decrement the count and notify anyone waiting if we reach zero. /// Blocks until the latch has counted down to zero. /// @Requires count must be greater than 0 void count_down_and_wait();
/// Reset the counter /// #Requires This method may only be invoked when there are no other threads currently inside the|count_down_and_wait()| method.
void reset(std::size_t count_ );
};
A completion latch has in addition to its internal counter a completion function that will be invoked when the counter reaches zero. The completion function is any nullary function returning nothing.
class completion_latch { public: typedef 'implementation defined' completion_function; static const completion_function noop;
completion_latch( completion_latch const& ) = delete; completion_latch& operator=( completion_latch const& ) = delete;
/// Constructs a latch with a given count and a noop completion function. completion_latch( std::size_t count);
/// Constructs a latch with a given count and a completion function. template <typename F> completion_latch( std::size_t count, F&& fct);
/// Blocks until the latch has counted down to zero. void wait(); bool try_wait(); template
cv_status wait_for( const chrono::duration & rel_time ); template cv_status wait_until( const chrono::time_point & abs_time ); /// Decrement the count and notify anyone waiting if we reach zero. /// @Requires count must be greater than 0 or undefined behavior void count_down();
/// Decrement the count and notify anyone waiting if we reach zero. /// Blocks until the latch has counted down to zero. /// @Requires count must be greater than 0 void count_down_and_wait();
/// Reset the counter with a new value for the initial count. /// #Requires This method may only be invoked when there are no other threads /// currently inside the count_down and wait related functions. /// It may also be invoked from within the registered completion function.
void reset( std::size_t count );
/// Resets the latch with the new completion function. /// The next time the internal count reaches 0, this function will be invoked. /// #Requires This method may only be invoked when there are no other threads /// currently inside the count_down and wait related functions. /// It may also be invoked from within the registered completion function. /// Returns the old completion function if any or noop if template typename F> completion_function then(F&&);
};
Optionally we could add a Cyclic latch provides the same interface than latch but that reset itself when zero is reached (as boost::barrier). This would be more efficient than been forced to add a completion function that reset the counter.
What do you think of these interfaces?
Best, Vicente
[1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3600.html
I'm not a threading guru so forgive me if I'm totally off. /// Resets the latch with the new completion function. /// The next time the internal count reaches 0, this function will be invoked. /// #Requires This method may only be invoked when there are no other threads /// currently inside the count_down and wait related functions. /// It may also be invoked from within the registered completion function. /// Returns the old completion function if any or noop if How is it that this function can be invoked with no threads inside count_down and wait related functions? bool count_down_and_wait() { boost::unique_lockboost::mutex lock(m_mutex); unsigned int gen = m_generation; if (--m_count == 0) { m_generation++; 1) completion_func(); < waiters are stil waiting m_cond.notify_all(); 2) completion_func(); < waiters are blocked trying to acquire the lock lock.unlock(); 3) completion_func(); < waiters could be all gone, but I don't think this guaranteed return true; } while (gen == m_generation) m_cond.wait(lock); return false; }