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