|
Boost-Commit : |
Subject: [Boost-commit] svn:boost r84075 - in trunk: boost/thread boost/thread/detail libs/thread/doc
From: vicente.botet_at_[hidden]
Date: 2013-04-28 16:09:51
Author: viboes
Date: 2013-04-28 16:09:50 EDT (Sun, 28 Apr 2013)
New Revision: 84075
URL: http://svn.boost.org/trac/boost/changeset/84075
Log:
Thread: refactor latch by adding an internal counter that has an associated condition_variable and add doc.
Added:
trunk/boost/thread/detail/counter.hpp (contents, props changed)
trunk/libs/thread/doc/latch.qbk (contents, props changed)
Text files modified:
trunk/boost/thread/completion_latch.hpp | 149 ++++++++++-----------------------------
trunk/boost/thread/latch.hpp | 62 ++++++----------
2 files changed, 63 insertions(+), 148 deletions(-)
Modified: trunk/boost/thread/completion_latch.hpp
==============================================================================
--- trunk/boost/thread/completion_latch.hpp (original)
+++ trunk/boost/thread/completion_latch.hpp 2013-04-28 16:09:50 EDT (Sun, 28 Apr 2013)
@@ -8,6 +8,7 @@
#include <boost/thread/detail/config.hpp>
#include <boost/thread/detail/delete.hpp>
+#include <boost/thread/detail/counter.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/lock_types.hpp>
@@ -20,7 +21,7 @@
#else
#include <functional>
#endif
-#include <boost/thread/latch.hpp>
+//#include <boost/thread/latch.hpp>
#include <boost/config/abi_prefix.hpp>
@@ -48,86 +49,35 @@
}
private:
-
- void wait_for_no_leaver(unique_lock<mutex> &lk)
- {
- // wait until all preceding waiting threads have leave
- while (leavers_ > 0)
- {
- idle_.wait(lk);
- }
- }
- void wait_for_leavers(unique_lock<mutex> &lk)
+ struct around_wait;
+ friend struct around_wait;
+ struct around_wait
{
- while (leavers_ == 0)
+ completion_latch &that_;
+ boost::unique_lock<boost::mutex> &lk_;
+ around_wait(completion_latch &that, boost::unique_lock<boost::mutex> &lk)
+ : that_(that), lk_(lk)
{
- idle_.wait(lk);
+ that_.leavers_.cond_.wait(lk, detail::counter_is_zero(that_.leavers_));
+ that_.waiters_.inc_and_notify_all();
+ that_.leavers_.cond_.wait(lk, detail::counter_is_not_zero(that_.leavers_));
}
- }
- void inc_waiters(boost::unique_lock<boost::mutex> &)
- {
- ++waiters_;
- waiters_cnd_.notify_all();
- }
- void dec_waiters(boost::unique_lock<boost::mutex> &)
- {
- --waiters_;
- waiters_cnd_.notify_all();
- }
- void pre_wait(boost::unique_lock<boost::mutex> &lk)
- {
- wait_for_no_leaver(lk);
- inc_waiters(lk);
- wait_for_leavers(lk);
- }
- void post_wait(boost::unique_lock<boost::mutex> &lk)
- {
- dec_waiters(lk);
- }
- void wait(boost::unique_lock<boost::mutex> &lk)
- {
- pre_wait(lk);
- while (count_ > 0)
+ ~around_wait()
{
- count_cond_.wait(lk);
+ that_.waiters_.dec_and_notify_all();
}
- post_wait(lk);
- }
+ };
- void wait_for_waiters(unique_lock<mutex> &lk)
- {
- // waits at least for a waiter.
- while (waiters_ == 0)
- {
- waiters_cnd_.wait(lk);
- }
- }
- void set_leavers()
- {
- leavers_ = waiters_;
- idle_.notify_all();
- }
- void wait_for_no_waiter(unique_lock<mutex> &lk)
- {
- while (waiters_ > 0)
- waiters_cnd_.wait(lk);
- }
- void reset_waiters_and_readers()
- {
- waiters_ = 0;
- leavers_ = 0;
- idle_.notify_all();
- }
bool count_down(unique_lock<mutex> &lk)
{
BOOST_ASSERT(count_ > 0);
if (--count_ == 0)
{
- wait_for_waiters(lk);
- set_leavers();
- count_cond_.notify_all();
- wait_for_no_waiter(lk);
- reset_waiters_and_readers();
+ waiters_.cond_.wait(lk, detail::counter_is_not_zero(waiters_));
+ leavers_.assign_and_notify_all(waiters_);
+ count_.cond_.notify_all();
+ waiters_.cond_.wait(lk, detail::counter_is_zero(waiters_));
+ leavers_.assign_and_notify_all(0);
lk.unlock();
funct_();
return true;
@@ -136,7 +86,7 @@
}
public:
- BOOST_THREAD_NO_COPYABLE( completion_latch)
+ BOOST_THREAD_NO_COPYABLE( completion_latch )
/// Constructs a latch with a given count.
completion_latch(std::size_t count) :
@@ -162,23 +112,22 @@
///
~completion_latch()
{
-
}
+
/// Blocks until the latch has counted down to zero.
void wait()
{
boost::unique_lock<boost::mutex> lk(mutex_);
- wait(lk);
+ around_wait aw(*this, lk);
+ count_.cond_.wait(lk, detail::counter_is_zero(count_));
}
/// @return true if the internal counter is already 0, false otherwise
bool try_wait()
{
boost::unique_lock<boost::mutex> lk(mutex_);
- pre_wait(lk);
- bool res = (count_ == 0);
- post_wait(lk);
- return res;
+ around_wait aw(*this, lk);
+ return (count_ == 0);
}
/// try to wait for a specified amount of time
@@ -187,18 +136,10 @@
cv_status wait_for(const chrono::duration<Rep, Period>& rel_time)
{
boost::unique_lock<boost::mutex> lk(mutex_);
- pre_wait(lk);
- cv_status res;
- while(count_ > 0)
- {
- if (count_cond_.wait_for(lk,rel_time)==cv_status::timeout)
- {
- res = (count_ == 0 ? cv_status::no_timeout : cv_status::timeout);
- }
- }
- res = cv_status::no_timeout;
- post_wait(lk);
- return res;
+ around_wait aw(*this, lk);
+ return count_.cond_.wait_for(lk, rel_time, detail::counter_is_zero(count_))
+ ? cv_status::no_timeout
+ : cv_status::timeout;
}
/// try to wait until the specified time_point is reached
@@ -207,18 +148,10 @@
cv_status wait_until(const chrono::time_point<Clock, Duration>& abs_time)
{
boost::unique_lock<boost::mutex> lk(mutex_);
- pre_wait(lk);
- cv_status res;
- while(count_ > 0)
- {
- if (count_cond_.wait_until(lk,abs_time)==cv_status::timeout)
- {
- res = (count_ == 0 ? cv_status::no_timeout : cv_status::timeout);
- }
- }
- res = cv_status::no_timeout;
- post_wait(lk);
- return res;
+ around_wait aw(*this, lk);
+ return count_.cond_.wait_until(lk, abs_time, detail::counter_is_zero(count_))
+ ? cv_status::no_timeout
+ : cv_status::timeout;
}
/// Decrement the count and notify anyone waiting if we reach zero.
@@ -243,7 +176,8 @@
{
return;
}
- wait(lk);
+ around_wait aw(*this, lk);
+ count_.cond_.wait(lk, detail::counter_is_zero(count_));
}
void sync()
{
@@ -255,7 +189,7 @@
void reset(std::size_t count)
{
boost::lock_guard<boost::mutex> lk(mutex_);
- BOOST_ASSERT(count_ == 0);
+ //BOOST_ASSERT(count_ == 0);
count_ = count;
}
@@ -286,13 +220,10 @@
private:
mutex mutex_;
- condition_variable count_cond_;
- std::size_t count_;
+ detail::counter count_;
completion_function funct_;
- condition_variable waiters_cnd_;
- std::size_t waiters_;
- condition_variable idle_;
- std::size_t leavers_;
+ detail::counter waiters_;
+ detail::counter leavers_;
};
} // namespace boost
Added: trunk/boost/thread/detail/counter.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/thread/detail/counter.hpp 2013-04-28 16:09:50 EDT (Sun, 28 Apr 2013)
@@ -0,0 +1,93 @@
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+// (C) Copyright 2013 Vicente J. Botet Escriba
+
+#ifndef BOOST_THREAD_COUNTER_HPP
+#define BOOST_THREAD_COUNTER_HPP
+
+#include <boost/thread/detail/config.hpp>
+#include <boost/thread/detail/delete.hpp>
+
+//#include <boost/thread/mutex.hpp>
+//#include <boost/thread/lock_types.hpp>
+#include <boost/thread/condition_variable.hpp>
+#include <boost/chrono/duration.hpp>
+#include <boost/chrono/time_point.hpp>
+#include <boost/assert.hpp>
+
+#include <boost/config/abi_prefix.hpp>
+
+namespace boost
+{
+ namespace detail {
+ struct counter
+ {
+ condition_variable cond_;
+ std::size_t value_;
+
+ counter(std::size_t value)
+ : value_(value)
+ {
+
+ }
+ counter& operator=(counter const& rhs)
+ {
+ value_ = rhs.value_;
+ return *this;
+ }
+ counter& operator=(std::size_t value)
+ {
+ value_ = value;
+ return *this;
+ }
+
+ operator std::size_t() const
+ {
+ return value_;
+ }
+ operator std::size_t&()
+ {
+ return value_;
+ }
+
+ void inc_and_notify_all()
+ {
+ ++value_;
+ cond_.notify_all();
+ }
+
+ void dec_and_notify_all()
+ {
+ --value_;
+ cond_.notify_all();
+ }
+ void assign_and_notify_all(counter const& rhs)
+ {
+ value_ = rhs.value_;
+ cond_.notify_all();
+ }
+ void assign_and_notify_all(std::size_t value)
+ {
+ value_ = value;
+ cond_.notify_all();
+ }
+ };
+ struct counter_is_not_zero
+ {
+ counter_is_not_zero(const counter& count) : count_(count) {}
+ bool operator()() const { return count_ != 0; }
+ const counter& count_;
+ };
+ struct counter_is_zero
+ {
+ counter_is_zero(const counter& count) : count_(count) {}
+ bool operator()() const { return count_ == 0; }
+ const counter& count_;
+ };
+ }
+} // namespace boost
+
+#include <boost/config/abi_suffix.hpp>
+
+#endif
Modified: trunk/boost/thread/latch.hpp
==============================================================================
--- trunk/boost/thread/latch.hpp (original)
+++ trunk/boost/thread/latch.hpp 2013-04-28 16:09:50 EDT (Sun, 28 Apr 2013)
@@ -8,6 +8,7 @@
#include <boost/thread/detail/config.hpp>
#include <boost/thread/detail/delete.hpp>
+#include <boost/thread/detail/counter.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/lock_types.hpp>
@@ -20,31 +21,24 @@
namespace boost
{
-
class latch
{
- void wait(boost::unique_lock<boost::mutex> &lk)
- {
- while (count_ > 0)
- {
- count_cond_.wait(lk);
- }
- }
- /// Decrement the count and notify anyone waiting if we reach zero.
- /// @Requires count must be greater than 0
- /// @ThreadSafe
+ /// @Requires: count_.value_ must be greater than 0
+ /// Effect: Decrement the count. Unlocks the lock notify anyone waiting if we reached zero.
+ /// Returns: true if count_.value_ reached the value 0.
+ /// @ThreadSafe ensured by the @c lk parameter
bool count_down(unique_lock<mutex> &lk)
+ /// pre_condition (count_.value_ > 0)
{
- BOOST_ASSERT(count_ > 0);
- if (--count_ == 0)
+ BOOST_ASSERT(count_.value_ > 0);
+ if (--count_.value_ == 0)
{
- count_cond_.notify_all();
+ count_.cond_.notify_all();
lk.unlock();
return true;
}
return false;
}
-
public:
BOOST_THREAD_NO_COPYABLE( latch)
@@ -54,7 +48,9 @@
{
}
- ///
+ /// Destructor
+ /// Precondition: No threads are waiting or invoking count_down on @c *this.
+
~latch()
{
@@ -64,7 +60,7 @@
void wait()
{
boost::unique_lock<boost::mutex> lk(mutex_);
- wait(lk);
+ count_.cond_.wait(lk, detail::counter_is_zero(count_));
}
/// @return true if the internal counter is already 0, false otherwise
@@ -80,31 +76,20 @@
cv_status wait_for(const chrono::duration<Rep, Period>& rel_time)
{
boost::unique_lock<boost::mutex> lk(mutex_);
- while(count_ > 0)
- {
- if (count_cond_.wait_for(lk,rel_time)==cv_status::timeout)
- {
- return (count_ == 0 ? cv_status::no_timeout : cv_status::timeout);
- }
- }
- return cv_status::no_timeout;
-
+ return count_.cond_.wait_for(lk, rel_time, detail::counter_is_zero(count_))
+ ? cv_status::no_timeout
+ : cv_status::timeout;
}
/// try to wait until the specified time_point is reached
- /// @return whether there is a timeout or not.
+ /// @return whether there were a timeout or not.
template <class Clock, class Duration>
cv_status wait_until(const chrono::time_point<Clock, Duration>& abs_time)
{
boost::unique_lock<boost::mutex> lk(mutex_);
- while(count_ > 0)
- {
- if (count_cond_.wait_until(lk,abs_time)==cv_status::timeout)
- {
- return (count_ == 0 ? cv_status::no_timeout : cv_status::timeout);
- }
- }
- return cv_status::no_timeout;
+ return count_.cond_.wait_until(lk, abs_time, detail::counter_is_zero(count_))
+ ? cv_status::no_timeout
+ : cv_status::timeout;
}
/// Decrement the count and notify anyone waiting if we reach zero.
@@ -129,7 +114,7 @@
{
return;
}
- wait(lk);
+ count_.cond_.wait(lk, detail::counter_is_zero(count_));
}
void sync()
{
@@ -141,14 +126,13 @@
void reset(std::size_t count)
{
boost::lock_guard<boost::mutex> lk(mutex_);
- BOOST_ASSERT(count_ == 0);
+ //BOOST_ASSERT(count_ == 0);
count_ = count;
}
private:
mutex mutex_;
- condition_variable count_cond_;
- std::size_t count_;
+ detail::counter count_;
};
} // namespace boost
Added: trunk/libs/thread/doc/latch.qbk
==============================================================================
--- (empty file)
+++ trunk/libs/thread/doc/latch.qbk 2013-04-28 16:09:50 EDT (Sun, 28 Apr 2013)
@@ -0,0 +1,396 @@
+[/
+ (C) Copyright 2013 Vicente J. Botet Escriba.
+ Distributed under the Boost Software License, Version 1.0.
+ (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt).
+]
+
+[section:latches Latches -- EXPERIMENTAL]
+
+[section Introdcution]
+
+Latches are a thread co-ordination mechanism that allow one or more threads to block until one or more threads have reached a point. An individual latch is a reusable object; once the operation has been completed, the threads can re-use the same barrier. It is thus useful for managing repeated tasks handled by multiple threads.
+
+A completion latch is like a latch that allows to associate a completion function which will be called once the internal counter reaches the value 0 and all the consumer threads have taken care of the notification.
+
+[endsect]
+[section Examples]
+
+Sample use cases for the latch include:
+
+* Setting multiple threads to perform a task, and then waiting until all threads have reached a common point.
+* Creating multiple threads, which wait for a signal before advancing beyond a common point.
+
+An example of the first use case would be as follows:
+
+ void DoWork(thread_pool* pool) {
+ latch completion_latch(NTASKS);
+ for (int i = 0; i < NTASKS; ++i) {
+ pool->submit([&] {
+ // perform work
+ ...
+ completion_latch.count_down();
+ }));
+ }
+ // Block until work is done
+ completion_latch.wait();
+ }
+
+An example of the second use case is shown below. We need to load data and then process it using a number of threads. Loading the data is I/O bound, whereas starting threads and creating data structures is CPU bound. By running these in parallel, throughput can be increased.
+
+
+ void DoWork() {
+ latch start_latch(1);
+ vector<thread*> workers;
+ for (int i = 0; i < NTHREADS; ++i) {
+ workers.push_back(new thread([&] {
+ // Initialize data structures. This is CPU bound.
+ ...
+ start_latch.wait();
+ // perform work
+ ...
+ }));
+ }
+ // Load input data. This is I/O bound.
+ ...
+ // Threads can now start processing
+ start_latch.count_down();
+ }
+
+The completion latches can be used to co-ordinate also a set of threads carrying out a repeated task. The number of threads can be adjusted dynamically to respond to changing requirements.
+
+In the example below, a number of threads are performing a multi-stage task. Some tasks may require fewer steps than others, meaning that some threads may finish before others. We reduce the number of threads waiting on the latch when this happens.
+
+ void DoWork() {
+ Tasks& tasks;
+ size_t initial_threads;
+ atomic<size_t> current_threads(initial_threads)
+ vector<thread*> workers;
+
+ // Create a barrier, and set a lambda that will be invoked every time the
+ // barrier counts down. If one or more active threads have completed,
+ // reduce the number of threads.
+ completion_latch task_barrier(n_threads);
+ task_barrier.then([&] {
+ task_barrier.reset(current_threads);
+ });
+
+ for (int i = 0; i < n_threads; ++i) {
+ workers.push_back(new thread([&] {
+ bool active = true;
+ while(active) {
+ Task task = tasks.get();
+ // perform task
+ ...
+ if (finished(task)) {
+ current_threads--;
+ active = false;
+ }
+ task_barrier.count_down_and_wait();
+ }
+ });
+ }
+
+ // Read each stage of the task until all stages are complete.
+ while (!finished()) {
+ GetNextStage(tasks);
+ }
+ }
+
+[endsect]
+[section:latch Class `latch`]
+
+ #include <boost/thread/latch.hpp>
+
+ class latch
+ {
+ public:
+ latch(latch const&) = delete;
+ latch& operator=(latch const&) = delete;
+
+ latch(std::size_t count);
+ ~latch();
+
+ 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);
+ void count_down();
+ void count_down_and_wait();
+ void reset(std::size_t count);
+
+ };
+
+
+A latch maintains an internal counter that is initialized when the latch is created. One or more threads may block waiting until the counter is decremented to 0.
+
+Instances of __latch__ are not copyable or movable.
+
+[section Constructor]
+
+ latch(std::size_t count);
+
+[variablelist
+
+[[Effects:] [Construct a latch with is initial value for the internal counter.]]
+
+[[Note:] [The counter could be zero and reset later.]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+[endsect]
+[section Destructor]
+
+ ~latch();
+
+[variablelist
+
+[[Precondition:] [No threads are waiting or invoking count_down on `*this`.]]
+
+[[Effects:] [Destroys `*this` latch.]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+[endsect]
+[section:wait Member function `wait()`]
+
+ void wait();
+
+[variablelist
+
+[[Effects:] [Block the calling thread until the internal count reaches the value zero. Then all waiting threads
+are unblocked. ]]
+
+[[Throws:] [
+ - __thread_resource_error__ if an error occurs.
+
+ - __thread_interrupted__ if the wait was interrupted by a call to __interrupt__ on the __thread__ object associated with the current thread of execution.
+]]
+
+[[Notes:] [`wait()` is an ['interruption point].]]
+
+]
+
+[endsect]
+
+[section:try_wait Member function `try_wait()`]
+
+ bool try_wait();
+
+[variablelist
+
+[[Returns:] [Returns true if the internal count is 0, and false otherwise. Does not block the calling thread. ]]
+
+[[Throws:] [
+ - __thread_resource_error__ if an error occurs.
+]]
+
+]
+
+[endsect]
+
+[section:wait_for Member function `wait_for() `]
+
+ template <class Rep, class Period>
+ cv_status wait_for(const chrono::duration<Rep, Period>& rel_time);
+
+[variablelist
+
+[[Effects:] [Block the calling thread until the internal count reaches the value zero or the duration has been elapsed. If no timeout, all waiting threads are unblocked. ]]
+[[Returns:] [cv_status::no_timeout if the internal count is 0, and cv_status::timeout if duration has been elapsed. ]]
+
+[[Throws:] [
+ - __thread_resource_error__ if an error occurs.
+
+ - __thread_interrupted__ if the wait was interrupted by a call to __interrupt__ on the __thread__ object associated with the current thread of execution.
+]]
+
+[[Notes:] [`wait_for()` is an ['interruption point].]]
+
+]
+
+[endsect]
+[section:wait_until Member function `wait_until()`]
+
+ template <class lock_type, class Clock, class Duration>
+ cv_status wait_until(const chrono::time_point<Clock, Duration>& abs_time);
+
+[variablelist
+
+[[Effects:] [Block the calling thread until the internal count reaches the value zero or the time_point has been reached. If no timeout, all waiting threads are unblocked. ]]
+[[Returns:] [cv_status::no_timeout if the internal count is 0, and cv_status::timeout if time_point has been reached.]]
+
+[[Throws:] [
+ - __thread_resource_error__ if an error occurs.
+
+ - __thread_interrupted__ if the wait was interrupted by a call to __interrupt__ on the __thread__ object associated with the current thread of execution.
+]]
+
+[[Notes:] [`wait_until()` is an ['interruption point].]]
+
+]
+
+[endsect]
+[section:count_down Member function `count_down()`]
+
+ void count_down();
+
+[variablelist
+
+[[Requires:] [The internal counter is non zero.]]
+[[Effects:] [Decrements the internal count by 1, and returns. If the count reaches 0, any threads blocked in wait() will be released. ]]
+
+[[Throws:] [
+ - __thread_resource_error__ if an error occurs.
+
+ - __thread_interrupted__ if the wait was interrupted by a call to __interrupt__ on the __thread__ object associated with the current thread of execution.
+]]
+[[Notes:] [`count_down()` is an ['interruption point].]]
+
+]
+
+[endsect]
+[section:count_down_and_wait Member function `count_down_and_wait()`]
+
+ void count_down_and_wait();
+
+[variablelist
+
+[[Requires:] [The internal counter is non zero.]]
+[[Effects:] [Decrements the internal count by 1. If the resulting count is not 0, blocks the calling thread until the internal count is decremented to 0 by one or more other threads calling count_down() or count_down_and_wait(). ]]
+
+[[Throws:] [
+ - __thread_resource_error__ if an error occurs.
+
+ - __thread_interrupted__ if the wait was interrupted by a call to __interrupt__ on the __thread__ object associated with the current thread of execution.
+]]
+[[Notes:] [`count_down_and_wait()` is an ['interruption point].]]
+
+]
+
+[endsect]
+
+[section:reset Member function `reset()`]
+
+ reset( size_t );
+
+[variablelist
+
+[[Requires:] [This function may only be invoked when there are no other threads currently inside the waiting functions.]]
+
+[[Returns:] [Resets the latch with a new value for the initial thread count. ]]
+
+[[Throws:] [
+ - __thread_resource_error__ if an error occurs.
+]]
+
+]
+
+[endsect]
+
+[endsect] [/ latch]
+
+[section:completion_latch Class `completion_latch `]
+
+ #include <boost/thread/completion_latch.hpp>
+
+ class completion_latch
+ {
+ public:
+ typedef 'implementation defined' completion_function;
+
+ completion_latch(completion_latch const&) = delete;
+ completion_latch& operator=(completion_latch const&) = delete;
+
+ completion_latch(std::size_t count);
+ template <typename F>
+ completion_latch(std::size_t count, F&& funct);
+ ~completion_latch();
+
+ 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);
+ void count_down();
+ void count_down_and_wait();
+ void reset(std::size_t count);
+ template <typename F>
+ completion_function then(F&& funct);
+ };
+
+
+A completion latch is like a latch that allows to associate a completion function which will be called once the internal counter reaches the value 0 and all the consumer threads have taken care of the notification.
+
+Instances of completion_latch are not copyable or movable.
+
+Only the additional functions are documented.
+
+[section:c Constructor]
+
+ completion_latch(std::size_t count);
+
+[variablelist
+
+[[Effects:] [Construct a completion_latch with is initial value for the internal counter and a noop completion function.]]
+
+[[Note:] [The counter could be zero and rest later.]]
+
+[[Throws:] [Nothing.]]
+
+]
+
+[endsect]
+[section:cf Constructor with completion function]
+
+ template <typename F>
+ completion_latch(std::size_t count, F&& funct);
+
+[variablelist
+
+[[Effects:] [Construct a completion_latch with is initial value for the internal counter and the completion function `funct`.]]
+
+[[Note:] [The counter could be zero and reset later.]]
+
+[[Throws:] [
+
+ - Any exception thrown by the copy/move construction of funct.
+
+]]
+
+]
+
+[endsect]
+[section:then Member function `then`]
+
+ template <typename F>
+ completion_function then(F&& funct);
+
+[variablelist
+
+[[Requires:] [This function may only be invoked when there are no other threads currently inside the waiting functions. It may also be invoked from within the registered completion function. ]]
+
+[[Effects:] [Associates the parameter `funct` as completion function of the latch. The next time the internal count reaches 0, this function will be invoked.]]
+[[Returns:] [The old completion function.]]
+
+[[Throws:] [
+ - __thread_resource_error__ if an error occurs.
+
+ - Any exception thrown by the copy/move construction of completion functions.
+]]
+
+]
+
+[endsect]
+
+[endsect] [/ completion_latch]
+
+
+[endsect] [/ Latches]
Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk