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