Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r84055 - in trunk: boost/thread libs/thread/test
From: vicente.botet_at_[hidden]
Date: 2013-04-27 02:51:20


Author: viboes
Date: 2013-04-27 02:51:18 EDT (Sat, 27 Apr 2013)
New Revision: 84055
URL: http://svn.boost.org/trac/boost/changeset/84055

Log:
Thread: Added latch and completion_latch classes.
Added:
   trunk/boost/thread/completion_latch.hpp (contents, props changed)
   trunk/boost/thread/latch.hpp (contents, props changed)
   trunk/libs/thread/test/test_completion_latch.cpp (contents, props changed)
   trunk/libs/thread/test/test_latch.cpp (contents, props changed)
Text files modified:
   trunk/libs/thread/test/Jamfile.v2 | 4 +++-
   trunk/libs/thread/test/test_barrier.cpp | 9 +--------
   2 files changed, 4 insertions(+), 9 deletions(-)

Added: trunk/boost/thread/completion_latch.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/thread/completion_latch.hpp 2013-04-27 02:51:18 EDT (Sat, 27 Apr 2013)
@@ -0,0 +1,296 @@
+// 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_COMPLETION_LATCH_HPP
+#define BOOST_THREAD_COMPLETION_LATCH_HPP
+
+#include <boost/thread/detail/config.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>
+#ifdef BOOST_NO_CXX11_HDR_FUNCTIONAL
+#include <boost/function.hpp>
+#else
+#include <functional>
+#endif
+#include <boost/thread/latch.hpp>
+
+#include <boost/config/abi_prefix.hpp>
+
+namespace boost
+{
+ namespace thread_detail
+ {
+ void noop()
+ {
+ }
+ }
+ class completion_latch
+ {
+ public:
+ /// the implementation defined completion function type
+#ifdef BOOST_NO_CXX11_HDR_FUNCTIONAL
+ typedef function<void()> completion_function;
+#else
+ typedef std::function<void()> completion_function;
+#endif
+ /// noop completion function factory
+ static completion_function noop()
+ {
+ return completion_function (&thread_detail::noop);
+ }
+
+ 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)
+ {
+ while (leavers_ == 0)
+ {
+ idle_.wait(lk);
+ }
+ }
+ 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)
+ {
+ count_cond_.wait(lk);
+ }
+ 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();
+ lk.unlock();
+ funct_();
+ return true;
+ }
+ return false;
+ }
+
+ public:
+ BOOST_THREAD_NO_COPYABLE( completion_latch)
+
+ /// Constructs a latch with a given count.
+ completion_latch(std::size_t count) :
+ count_(count), funct_(noop()), waiters_(0), leavers_(0)
+ {
+ }
+
+ /// Constructs a latch with a given count and a completion function.
+ template <typename F>
+ completion_latch(std::size_t count, BOOST_THREAD_RV_REF(F) funct) :
+ count_(count),
+ funct_(boost::move(funct)),
+ waiters_(0),
+ leavers_(0)
+ {
+ }
+ template <typename F>
+ completion_latch(std::size_t count, void(*funct)()) :
+ count_(count), funct_(funct), waiters_(0), leavers_(0)
+ {
+ }
+
+ /// Blocks until the latch has counted down to zero.
+ void wait()
+ {
+ boost::unique_lock<boost::mutex> lk(mutex_);
+ wait(lk);
+ }
+
+ /// @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;
+ }
+
+ /// try to wait for a specified amount of time
+ /// @return whether there is a timeout or not.
+ template <class Rep, class Period>
+ cv_status wait_for(const chrono::duration<Rep, Period>& rel_time)
+ {
+ boost::unique_lock<boost::mutex> lk(mutex_);
+ pre_wait(lk);
+ cv_status res;
+ if (count_ > 0)
+ {
+ res = count_cond_.wait_for(rel_time);
+ }
+ else
+ {
+ res = cv_status::no_timeout;
+ }
+ post_wait(lk);
+ return res;
+ }
+
+ /// try to wait until the specified time_point is reached
+ /// @return whether there is a timeout or not.
+ template <class lock_type, class Clock, class Duration>
+ 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;
+ if (count_ > 0)
+ {
+ res = count_cond_.wait_until(abs_time);
+ }
+ else
+ {
+ res = cv_status::no_timeout;
+ }
+ post_wait(lk);
+ return res;
+ }
+
+ /// Decrement the count and notify anyone waiting if we reach zero.
+ /// @Requires count must be greater than 0
+ void count_down()
+ {
+ unique_lock<mutex> lk(mutex_);
+ count_down(lk);
+ }
+ void signal()
+ {
+ 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()
+ {
+ boost::unique_lock<boost::mutex> lk(mutex_);
+ if (count_down(lk))
+ {
+ return;
+ }
+ wait(lk);
+ }
+ void sync()
+ {
+ 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)
+ {
+ boost::lock_guard<boost::mutex> lk(mutex_);
+ BOOST_ASSERT(count_ == 0);
+ count_ = count;
+ }
+
+ /// Resets the latch with the new completion function.
+ /// The next time the internal count reaches 0, this function will be invoked.
+ /// This completion function 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
+
+#ifdef BOOST_NO_CXX11_HDR_FUNCTIONAL
+ template <typename F>
+ completion_function then(BOOST_THREAD_RV_REF(F) funct)
+ {
+ boost::lock_guard<boost::mutex> lk(mutex_);
+ completion_function tmp(funct_);
+ funct_ = boost::move(funct);
+ return tmp;
+ }
+#endif
+ completion_function then(void(*funct)())
+ {
+ boost::lock_guard<boost::mutex> lk(mutex_);
+ completion_function tmp(funct_);
+ funct_ = completion_function(funct);
+ return tmp;
+ }
+
+ private:
+ mutex mutex_;
+ condition_variable count_cond_;
+ std::size_t count_;
+ completion_function funct_;
+ condition_variable waiters_cnd_;
+ std::size_t waiters_;
+ condition_variable idle_;
+ std::size_t leavers_;
+ };
+
+} // namespace boost
+
+#include <boost/config/abi_suffix.hpp>
+
+#endif

Added: trunk/boost/thread/latch.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/thread/latch.hpp 2013-04-27 02:51:18 EDT (Sat, 27 Apr 2013)
@@ -0,0 +1,148 @@
+// 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_LATCH_HPP
+#define BOOST_THREAD_LATCH_HPP
+
+#include <boost/thread/detail/config.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/throw_exception.hpp>
+//#include <stdexcept>
+//#include <string>
+
+#include <boost/config/abi_prefix.hpp>
+
+namespace boost
+{
+
+ class latch
+ {
+ void wait(boost::unique_lock<boost::mutex> &lk)
+ {
+ while (count_ > 0)
+ {
+ cond_.wait(lk);
+ }
+ }
+ /// Decrement the count and notify anyone waiting if we reach zero.
+ /// @Requires count must be greater than 0
+ /// @ThreadSafe
+ bool count_down(unique_lock<mutex> &lk)
+ {
+ BOOST_ASSERT(count_ > 0);
+ if (--count_ == 0)
+ {
+ cond_.notify_all();
+ lk.unlock();
+ return true;
+ }
+ return false;
+ }
+
+ public:
+ BOOST_THREAD_NO_COPYABLE( latch )
+
+ /// Constructs a latch with a given count.
+ latch(std::size_t count) :
+ count_(count)
+ {
+ }
+
+ /// Blocks until the latch has counted down to zero.
+ void wait()
+ {
+ boost::unique_lock<boost::mutex> lk(mutex_);
+ wait(lk);
+ }
+
+ /// @return true if the internal counter is already 0, false otherwise
+ bool try_wait()
+ {
+ boost::unique_lock<boost::mutex> lk(mutex_);
+ return (count_ == 0);
+ }
+
+ /// try to wait for a specified amount of time is elapsed.
+ /// @return whether there is a timeout or not.
+ template <class Rep, class Period>
+ cv_status wait_for(const chrono::duration<Rep, Period>& rel_time)
+ {
+ boost::unique_lock<boost::mutex> lk(mutex_);
+ if (count_ > 0)
+ {
+ return cond_.wait_for(rel_time);
+ }
+ else
+ {
+ return cv_status::no_timeout;
+ }
+ }
+
+ /// try to wait until the specified time_point is reached
+ /// @return whether there is a timeout or not.
+ template <class lock_type, class Clock, class Duration>
+ cv_status wait_until(const chrono::time_point<Clock, Duration>& abs_time)
+ {
+ boost::unique_lock<boost::mutex> lk(mutex_);
+ if (count_ > 0)
+ {
+ return cond_.wait_until(abs_time);
+ }
+ else
+ {
+ return cv_status::no_timeout;
+ }
+ }
+
+ /// Decrement the count and notify anyone waiting if we reach zero.
+ /// @Requires count must be greater than 0
+ void count_down()
+ {
+ boost::unique_lock<boost::mutex> lk(mutex_);
+ count_down(lk);
+ }
+ void signal() {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()
+ {
+ boost::unique_lock<boost::mutex> lk(mutex_);
+ if (count_down(lk))
+ {
+ return;
+ }
+ wait(lk);
+ }
+ void sync() {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)
+ {
+ boost::lock_guard<boost::mutex> lk(mutex_);
+ BOOST_ASSERT(count_ == 0);
+ count_ = count;
+ }
+
+ private:
+ mutex mutex_;
+ condition_variable cond_;
+ std::size_t count_;
+ };
+
+} // namespace boost
+
+#include <boost/config/abi_suffix.hpp>
+
+#endif

Modified: trunk/libs/thread/test/Jamfile.v2
==============================================================================
--- trunk/libs/thread/test/Jamfile.v2 (original)
+++ trunk/libs/thread/test/Jamfile.v2 2013-04-27 02:51:18 EDT (Sat, 27 Apr 2013)
@@ -220,9 +220,11 @@
           [ thread-test test_condition_notify_all.cpp ]
           [ thread-test test_condition.cpp ]
           [ thread-test test_once.cpp ]
- [ thread-test test_barrier.cpp ]
+ [ thread-run test_barrier.cpp ]
           [ thread-test test_lock_concept.cpp ]
           [ thread-test test_generic_locks.cpp ]
+ [ thread-run test_latch.cpp ]
+ [ thread-run test_completion_latch.cpp ]
     ;
 
     test-suite t_shared

Modified: trunk/libs/thread/test/test_barrier.cpp
==============================================================================
--- trunk/libs/thread/test/test_barrier.cpp (original)
+++ trunk/libs/thread/test/test_barrier.cpp 2013-04-27 02:51:18 EDT (Sat, 27 Apr 2013)
@@ -1,5 +1,6 @@
 // Copyright (C) 2001-2003
 // William E. Kempf
+// (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)
@@ -12,7 +13,6 @@
 #include <boost/thread/barrier.hpp>
 
 #include <boost/detail/lightweight_test.hpp>
-//#include <boost/test/unit_test.hpp>
 #include <vector>
 
 namespace {
@@ -55,21 +55,14 @@
         throw;
     }
 
- //BOOST_CHECK_EQUAL(global_parameter,5);
     BOOST_TEST(global_parameter==5);
 
 }
 
-//boost::unit_test::test_suite* init_unit_test_suite(int, char*[])
 int main()
 {
-// boost::unit_test::test_suite* test =
-// BOOST_TEST_SUITE("Boost.Threads: barrier test suite");
-//
-// test->add(BOOST_TEST_CASE(&test_barrier));
 
     test_barrier();
     return boost::report_errors();
- //return test;
 }
 

Added: trunk/libs/thread/test/test_completion_latch.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/thread/test/test_completion_latch.cpp 2013-04-27 02:51:18 EDT (Sat, 27 Apr 2013)
@@ -0,0 +1,118 @@
+// 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
+
+#define BOOST_THREAD_PROVIDES_INTERRUPTIONS
+
+#include <boost/thread/detail/config.hpp>
+
+#include <boost/thread/thread.hpp>
+#include <boost/thread/completion_latch.hpp>
+
+#include <boost/detail/lightweight_test.hpp>
+#include <vector>
+
+namespace
+{
+
+ // Shared variables for generation completion_latch test
+ const int N_THREADS = 10;
+ boost::completion_latch gen_latch(N_THREADS);
+ boost::mutex mutex;
+ long global_parameter;
+
+ void latch_thread()
+ {
+ {
+ boost::unique_lock<boost::mutex> lock(mutex);
+ global_parameter++;
+ }
+ gen_latch.count_down();
+ //do something else
+ }
+
+} // namespace
+
+void test_global_parameter()
+{
+ boost::unique_lock<boost::mutex> lock(mutex);
+ BOOST_TEST_EQ(global_parameter, N_THREADS);
+}
+
+void reset_gen_latch()
+{
+ {
+ boost::unique_lock<boost::mutex> lock(mutex);
+ BOOST_TEST_EQ(global_parameter, N_THREADS);
+ }
+ gen_latch.reset(N_THREADS);
+}
+
+void test_completion_latch_reset()
+{
+ boost::thread_group g;
+ boost::thread_group g2;
+
+ gen_latch.then(&reset_gen_latch);
+
+ {
+ global_parameter = 0;
+ try
+ {
+ for (int i = 0; i < N_THREADS; ++i)
+ g.create_thread(&latch_thread);
+
+ if (!gen_latch.try_wait())
+ gen_latch.wait(); // All the threads have been updated the global_parameter
+ //std::cout << __FILE__ << ':' << __LINE__ << std::endl;
+ g.join_all();
+ }
+ catch (...)
+ {
+ g.interrupt_all();
+ g.join_all();
+ throw;
+ }
+ }
+ //std::cout << __FILE__ << ':' << __LINE__ << std::endl;
+ gen_latch.then(&test_global_parameter);
+ {
+ //std::cout << __FILE__ << ':' << __LINE__ << std::endl;
+ global_parameter = 0;
+ try
+ {
+ //std::cout << __FILE__ << ':' << __LINE__ << std::endl;
+ for (int i = 0; i < N_THREADS; ++i)
+ g2.create_thread(&latch_thread);
+
+ //std::cout << __FILE__ << ':' << __LINE__ << std::endl;
+ //if (!gen_latch.try_wait())
+ gen_latch.wait(); // All the threads have been updated the global_parameter
+ //std::cout << __FILE__ << ':' << __LINE__ << std::endl;
+
+ g2.join_all();
+ }
+ catch (...)
+ {
+ g2.interrupt_all();
+ g2.join_all();
+ throw;
+ }
+ }
+}
+//template <bool b>
+//struct xx {
+// static BOOST_CONSTEXPR_OR_CONST bool value = !b;
+//};
+
+int main()
+{
+ //test_completion_latch();
+ test_completion_latch_reset();
+ return boost::report_errors();
+
+ //BOOST_CONSTEXPR_OR_CONST bool a = boost::integral_constant<bool,false>::value;
+ //xx<a>
+}
+

Added: trunk/libs/thread/test/test_latch.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/thread/test/test_latch.cpp 2013-04-27 02:51:18 EDT (Sat, 27 Apr 2013)
@@ -0,0 +1,67 @@
+// 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
+
+#define BOOST_THREAD_PROVIDES_INTERRUPTIONS
+
+#include <boost/thread/detail/config.hpp>
+
+#include <boost/thread/thread.hpp>
+#include <boost/thread/latch.hpp>
+
+#include <boost/detail/lightweight_test.hpp>
+#include <vector>
+
+namespace
+{
+
+ // Shared variables for generation latch test
+ const int N_THREADS = 10;
+ boost::latch gen_latch(N_THREADS);
+ boost::mutex mutex;
+ long global_parameter;
+
+ void latch_thread()
+ {
+ {
+ boost::unique_lock<boost::mutex> lock(mutex);
+ global_parameter++;
+ }
+ gen_latch.count_down();
+ //do something else
+ }
+
+} // namespace
+
+void test_latch()
+{
+ boost::thread_group g;
+ global_parameter = 0;
+
+ try
+ {
+ for (int i = 0; i < N_THREADS; ++i)
+ g.create_thread(&latch_thread);
+
+ if (! gen_latch.try_wait())
+ gen_latch.wait(); // All the threads have been updated the global_parameter
+ BOOST_TEST_EQ(global_parameter, N_THREADS);
+
+ g.join_all();
+ }
+ catch (...)
+ {
+ g.interrupt_all();
+ g.join_all();
+ throw;
+ }
+
+}
+
+int main()
+{
+ test_latch();
+ return boost::report_errors();
+}
+


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