Boost logo

Boost :

Subject: Re: [boost] [thread] countdown_latch
From: Vicente J. Botet Escriba (vicente.botet_at_[hidden])
Date: 2013-04-21 09:55:21


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 <class Rep, class Period>
     cv_statuswait_for( const chrono::duration<Rep, Period>& rel_time );
     
     template <class lock_type, class Clock, class Duration>
     cv_status wait_until( const chrono::time_point<Clock, Duration>& 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 <class Rep, class Period>
     cv_status wait_for( const chrono::duration<Rep, Period>& rel_time );
     template <class lock_type, class Clock, class Duration>
     cv_status wait_until( const chrono::time_point<Clock, Duration>&
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


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk