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