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_LATCH_HPP 7 #define BOOST_THREAD_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 20 #include <boost/config/abi_prefix.hpp> 21 22 namespace boost 23 { 24 class latch 25 { 26 /// @Requires: count_ must be greater than 0 27 /// Effect: Decrement the count. Unlocks the lock and notify anyone waiting if we reached zero. 28 /// Returns: true if count_ reached the value 0. 29 /// @ThreadSafe ensured by the @c lk parameter count_down(unique_lock<mutex> &)30 bool count_down(unique_lock<mutex> &) 31 /// pre_condition (count_ > 0) 32 { 33 BOOST_ASSERT(count_ > 0); 34 if (--count_ == 0) 35 { 36 ++generation_; 37 //lk.unlock(); 38 cond_.notify_all(); 39 return true; 40 } 41 return false; 42 } 43 /// Effect: Decrement the count is > 0. Unlocks the lock notify anyone waiting if we reached zero. 44 /// Returns: true if count_ is 0. 45 /// @ThreadSafe ensured by the @c lk parameter try_count_down(unique_lock<mutex> & lk)46 bool try_count_down(unique_lock<mutex> &lk) 47 { 48 if (count_ > 0) 49 { 50 return count_down(lk); 51 } 52 return true; 53 } 54 public: 55 BOOST_THREAD_NO_COPYABLE( latch) 56 57 /// Constructs a latch with a given count. latch(std::size_t count)58 latch(std::size_t count) : 59 count_(count), generation_(0) 60 { 61 } 62 63 /// Destructor 64 /// Precondition: No threads are waiting or invoking count_down on @c *this. 65 ~latch()66 ~latch() 67 { 68 69 } 70 71 /// Blocks until the latch has counted down to zero. wait()72 void wait() 73 { 74 boost::unique_lock<boost::mutex> lk(mutex_); 75 if (count_ == 0) return; 76 std::size_t generation(generation_); 77 cond_.wait(lk, detail::not_equal(generation, generation_)); 78 } 79 80 /// @return true if the internal counter is already 0, false otherwise try_wait()81 bool try_wait() 82 { 83 boost::unique_lock<boost::mutex> lk(mutex_); 84 return (count_ == 0); 85 } 86 87 /// try to wait for a specified amount of time is elapsed. 88 /// @return whether there is a timeout or not. 89 template <class Rep, class Period> wait_for(const chrono::duration<Rep,Period> & rel_time)90 cv_status wait_for(const chrono::duration<Rep, Period>& rel_time) 91 { 92 boost::unique_lock<boost::mutex> lk(mutex_); 93 if (count_ == 0) return cv_status::no_timeout; 94 std::size_t generation(generation_); 95 return cond_.wait_for(lk, rel_time, detail::not_equal(generation, generation_)) 96 ? cv_status::no_timeout 97 : cv_status::timeout; 98 } 99 100 /// try to wait until the specified time_point is reached 101 /// @return whether there were a timeout or not. 102 template <class Clock, class Duration> wait_until(const chrono::time_point<Clock,Duration> & abs_time)103 cv_status wait_until(const chrono::time_point<Clock, Duration>& abs_time) 104 { 105 boost::unique_lock<boost::mutex> lk(mutex_); 106 if (count_ == 0) return cv_status::no_timeout; 107 std::size_t generation(generation_); 108 return cond_.wait_until(lk, abs_time, detail::not_equal(generation, generation_)) 109 ? cv_status::no_timeout 110 : cv_status::timeout; 111 } 112 113 /// Decrement the count and notify anyone waiting if we reach zero. 114 /// @Requires count must be greater than 0 count_down()115 void count_down() 116 { 117 boost::unique_lock<boost::mutex> lk(mutex_); 118 count_down(lk); 119 } 120 /// Effect: Decrement the count if it is > 0 and notify anyone waiting if we reached zero. 121 /// Returns: true if count_ was 0 or reached 0. try_count_down()122 bool try_count_down() 123 { 124 boost::unique_lock<boost::mutex> lk(mutex_); 125 return try_count_down(lk); 126 } signal()127 void signal() 128 { 129 count_down(); 130 } 131 132 /// Decrement the count and notify anyone waiting if we reach zero. 133 /// Blocks until the latch has counted down to zero. 134 /// @Requires count must be greater than 0 count_down_and_wait()135 void count_down_and_wait() 136 { 137 boost::unique_lock<boost::mutex> lk(mutex_); 138 std::size_t generation(generation_); 139 if (count_down(lk)) 140 { 141 return; 142 } 143 cond_.wait(lk, detail::not_equal(generation, generation_)); 144 } sync()145 void sync() 146 { 147 count_down_and_wait(); 148 } 149 150 /// Reset the counter 151 /// #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)152 void reset(std::size_t count) 153 { 154 boost::lock_guard<boost::mutex> lk(mutex_); 155 //BOOST_ASSERT(count_ == 0); 156 count_ = count; 157 } 158 159 private: 160 mutex mutex_; 161 condition_variable cond_; 162 std::size_t count_; 163 std::size_t generation_; 164 }; 165 166 } // namespace boost 167 168 #include <boost/config/abi_suffix.hpp> 169 170 #endif 171