completion_latch.hpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. // Distributed under the Boost Software License, Version 1.0. (See
  2. // accompanying file LICENSE_1_0.txt or copy at
  3. // http://www.boost.org/LICENSE_1_0.txt)
  4. // (C) Copyright 2013 Vicente J. Botet Escriba
  5. #ifndef BOOST_THREAD_COMPLETION_LATCH_HPP
  6. #define BOOST_THREAD_COMPLETION_LATCH_HPP
  7. #include <boost/thread/detail/config.hpp>
  8. #include <boost/thread/detail/delete.hpp>
  9. #include <boost/thread/detail/counter.hpp>
  10. #include <boost/thread/mutex.hpp>
  11. #include <boost/thread/lock_types.hpp>
  12. #include <boost/thread/condition_variable.hpp>
  13. #include <boost/chrono/duration.hpp>
  14. #include <boost/chrono/time_point.hpp>
  15. #include <boost/assert.hpp>
  16. //#include <boost/thread/detail/nullary_function.hpp>
  17. #include <boost/thread/csbl/functional.hpp>
  18. #include <boost/config/abi_prefix.hpp>
  19. namespace boost
  20. {
  21. namespace thread_detail
  22. {
  23. void noop()
  24. {
  25. }
  26. }
  27. class completion_latch
  28. {
  29. public:
  30. /// the implementation defined completion function type
  31. //typedef detail::nullary_function<void()> completion_function;
  32. typedef csbl::function<void()> completion_function;
  33. /// noop completion function factory
  34. static completion_function noop()
  35. {
  36. return completion_function(&thread_detail::noop);
  37. }
  38. private:
  39. struct around_wait;
  40. friend struct around_wait;
  41. struct around_wait
  42. {
  43. completion_latch &that_;
  44. boost::unique_lock<boost::mutex> &lk_;
  45. around_wait(completion_latch &that, boost::unique_lock<boost::mutex> &lk)
  46. : that_(that), lk_(lk)
  47. {
  48. that_.leavers_.cond_.wait(lk, detail::counter_is_zero(that_.leavers_));
  49. that_.waiters_.inc_and_notify_all();
  50. that_.leavers_.cond_.wait(lk, detail::counter_is_not_zero(that_.leavers_));
  51. }
  52. ~around_wait()
  53. {
  54. that_.waiters_.dec_and_notify_all();
  55. }
  56. };
  57. bool count_down(unique_lock<mutex> &lk)
  58. {
  59. BOOST_ASSERT(count_ > 0);
  60. if (--count_ == 0)
  61. {
  62. waiters_.cond_.wait(lk, detail::counter_is_not_zero(waiters_));
  63. leavers_.assign_and_notify_all(waiters_);
  64. count_.cond_.notify_all();
  65. waiters_.cond_.wait(lk, detail::counter_is_zero(waiters_));
  66. leavers_.assign_and_notify_all(0);
  67. lk.unlock();
  68. funct_();
  69. return true;
  70. }
  71. return false;
  72. }
  73. public:
  74. BOOST_THREAD_NO_COPYABLE( completion_latch )
  75. /// Constructs a latch with a given count.
  76. completion_latch(std::size_t count) :
  77. count_(count), funct_(noop()), waiters_(0), leavers_(0)
  78. {
  79. }
  80. /// Constructs a latch with a given count and a completion function.
  81. template <typename F>
  82. completion_latch(std::size_t count, BOOST_THREAD_RV_REF(F) funct) :
  83. count_(count),
  84. funct_(boost::move(funct)),
  85. waiters_(0),
  86. leavers_(0)
  87. {
  88. }
  89. completion_latch(std::size_t count, void(*funct)()) :
  90. count_(count), funct_(funct), waiters_(0), leavers_(0)
  91. {
  92. }
  93. ///
  94. ~completion_latch()
  95. {
  96. }
  97. /// Blocks until the latch has counted down to zero.
  98. void wait()
  99. {
  100. boost::unique_lock<boost::mutex> lk(mutex_);
  101. around_wait aw(*this, lk);
  102. count_.cond_.wait(lk, detail::counter_is_zero(count_));
  103. }
  104. /// @return true if the internal counter is already 0, false otherwise
  105. bool try_wait()
  106. {
  107. boost::unique_lock<boost::mutex> lk(mutex_);
  108. around_wait aw(*this, lk);
  109. return (count_ == 0);
  110. }
  111. /// try to wait for a specified amount of time
  112. /// @return whether there is a timeout or not.
  113. template <class Rep, class Period>
  114. cv_status wait_for(const chrono::duration<Rep, Period>& rel_time)
  115. {
  116. boost::unique_lock<boost::mutex> lk(mutex_);
  117. around_wait aw(*this, lk);
  118. return count_.cond_.wait_for(lk, rel_time, detail::counter_is_zero(count_))
  119. ? cv_status::no_timeout
  120. : cv_status::timeout;
  121. }
  122. /// try to wait until the specified time_point is reached
  123. /// @return whether there is a timeout or not.
  124. template <class Clock, class Duration>
  125. cv_status wait_until(const chrono::time_point<Clock, Duration>& abs_time)
  126. {
  127. boost::unique_lock<boost::mutex> lk(mutex_);
  128. around_wait aw(*this, lk);
  129. return count_.cond_.wait_until(lk, abs_time, detail::counter_is_zero(count_))
  130. ? cv_status::no_timeout
  131. : cv_status::timeout;
  132. }
  133. /// Decrement the count and notify anyone waiting if we reach zero.
  134. /// @Requires count must be greater than 0
  135. void count_down()
  136. {
  137. unique_lock<mutex> lk(mutex_);
  138. count_down(lk);
  139. }
  140. void signal()
  141. {
  142. count_down();
  143. }
  144. /// Decrement the count and notify anyone waiting if we reach zero.
  145. /// Blocks until the latch has counted down to zero.
  146. /// @Requires count must be greater than 0
  147. void count_down_and_wait()
  148. {
  149. boost::unique_lock<boost::mutex> lk(mutex_);
  150. if (count_down(lk))
  151. {
  152. return;
  153. }
  154. around_wait aw(*this, lk);
  155. count_.cond_.wait(lk, detail::counter_is_zero(count_));
  156. }
  157. void sync()
  158. {
  159. count_down_and_wait();
  160. }
  161. /// Reset the counter
  162. /// #Requires This method may only be invoked when there are no other threads currently inside the count_down_and_wait() method.
  163. void reset(std::size_t count)
  164. {
  165. boost::lock_guard<boost::mutex> lk(mutex_);
  166. //BOOST_ASSERT(count_ == 0);
  167. count_ = count;
  168. }
  169. /// Resets the latch with the new completion function.
  170. /// The next time the internal count reaches 0, this function will be invoked.
  171. /// This completion function may only be invoked when there are no other threads
  172. /// currently inside the count_down and wait related functions.
  173. /// It may also be invoked from within the registered completion function.
  174. /// @Returns the old completion function if any or noop if
  175. #ifdef BOOST_NO_CXX11_HDR_FUNCTIONAL
  176. template <typename F>
  177. completion_function then(BOOST_THREAD_RV_REF(F) funct)
  178. {
  179. boost::lock_guard<boost::mutex> lk(mutex_);
  180. completion_function tmp(funct_);
  181. funct_ = boost::move(funct);
  182. return tmp;
  183. }
  184. #endif
  185. completion_function then(void(*funct)())
  186. {
  187. boost::lock_guard<boost::mutex> lk(mutex_);
  188. completion_function tmp(funct_);
  189. funct_ = completion_function(funct);
  190. return tmp;
  191. }
  192. private:
  193. mutex mutex_;
  194. detail::counter count_;
  195. completion_function funct_;
  196. detail::counter waiters_;
  197. detail::counter leavers_;
  198. };
  199. } // namespace boost
  200. #include <boost/config/abi_suffix.hpp>
  201. #endif