1 
2 //          Copyright Oliver Kowalke 2013.
3 // Distributed under the Boost Software License, Version 1.0.
4 //    (See accompanying file LICENSE_1_0.txt or copy at
5 //          http://www.boost.org/LICENSE_1_0.txt)
6 
7 #ifndef BOOST_FIBERS_CONDITION_VARIABLE_H
8 #define BOOST_FIBERS_CONDITION_VARIABLE_H
9 
10 #include <algorithm>
11 #include <atomic>
12 #include <chrono>
13 #include <functional>
14 #include <mutex>
15 
16 #include <boost/assert.hpp>
17 #include <boost/config.hpp>
18 
19 #include <boost/fiber/context.hpp>
20 #include <boost/fiber/detail/config.hpp>
21 #include <boost/fiber/detail/convert.hpp>
22 #include <boost/fiber/detail/spinlock.hpp>
23 #include <boost/fiber/exceptions.hpp>
24 #include <boost/fiber/mutex.hpp>
25 #include <boost/fiber/operations.hpp>
26 
27 #ifdef BOOST_HAS_ABI_HEADERS
28 #  include BOOST_ABI_PREFIX
29 #endif
30 
31 #ifdef _MSC_VER
32 # pragma warning(push)
33 //# pragma warning(disable:4251)
34 #endif
35 
36 namespace boost {
37 namespace fibers {
38 
39 enum class cv_status {
40     no_timeout = 1,
41     timeout
42 };
43 
44 class BOOST_FIBERS_DECL condition_variable_any {
45 private:
46     typedef context::wait_queue_t   wait_queue_t;
47 
48     detail::spinlock    wait_queue_splk_{};
49     wait_queue_t        wait_queue_{};
50 
51 public:
52     condition_variable_any() = default;
53 
54     ~condition_variable_any() {
55         BOOST_ASSERT( wait_queue_.empty() );
56     }
57 
58     condition_variable_any( condition_variable_any const&) = delete;
59     condition_variable_any & operator=( condition_variable_any const&) = delete;
60 
61     void notify_one() noexcept;
62 
63     void notify_all() noexcept;
64 
65     template< typename LockType >
66     void wait( LockType & lt) {
67         context * active_ctx = context::active();
68         // atomically call lt.unlock() and block on *this
69         // store this fiber in waiting-queue
70         detail::spinlock_lock lk{ wait_queue_splk_ };
71         BOOST_ASSERT( ! active_ctx->wait_is_linked() );
72         active_ctx->wait_link( wait_queue_);
73         active_ctx->twstatus.store( static_cast< std::intptr_t >( 0), std::memory_order_release);
74         // unlock external lt
75         lt.unlock();
76         // suspend this fiber
77         active_ctx->suspend( lk);
78         // relock external again before returning
79         try {
80             lt.lock();
81         } catch (...) {
82             std::terminate();
83         }
84         // post-conditions
85         BOOST_ASSERT( ! active_ctx->wait_is_linked() );
86     }
87 
88     template< typename LockType, typename Pred >
89     void wait( LockType & lt, Pred pred) {
90         while ( ! pred() ) {
91             wait( lt);
92         }
93     }
94 
95     template< typename LockType, typename Clock, typename Duration >
96     cv_status wait_until( LockType & lt, std::chrono::time_point< Clock, Duration > const& timeout_time_) {
97         context * active_ctx = context::active();
98         cv_status status = cv_status::no_timeout;
99         std::chrono::steady_clock::time_point timeout_time = detail::convert( timeout_time_);
100         // atomically call lt.unlock() and block on *this
101         // store this fiber in waiting-queue
102         detail::spinlock_lock lk{ wait_queue_splk_ };
103         BOOST_ASSERT( ! active_ctx->wait_is_linked() );
104         active_ctx->wait_link( wait_queue_);
105         active_ctx->twstatus.store( reinterpret_cast< std::intptr_t >( this), std::memory_order_release);
106         // unlock external lt
107         lt.unlock();
108         // suspend this fiber
109         if ( ! active_ctx->wait_until( timeout_time, lk) ) {
110             status = cv_status::timeout;
111             // relock local lk
112             lk.lock();
113             // remove from waiting-queue
114             wait_queue_.remove( * active_ctx);
115             // unlock local lk
116             lk.unlock();
117         }
118         // relock external again before returning
119         try {
120             lt.lock();
121         } catch (...) {
122             std::terminate();
123         }
124         // post-conditions
125         BOOST_ASSERT( ! active_ctx->wait_is_linked() );
126         return status;
127     }
128 
129     template< typename LockType, typename Clock, typename Duration, typename Pred >
130     bool wait_until( LockType & lt,
131                      std::chrono::time_point< Clock, Duration > const& timeout_time, Pred pred) {
132         while ( ! pred() ) {
133             if ( cv_status::timeout == wait_until( lt, timeout_time) ) {
134                 return pred();
135             }
136         }
137         return true;
138     }
139 
140     template< typename LockType, typename Rep, typename Period >
141     cv_status wait_for( LockType & lt, std::chrono::duration< Rep, Period > const& timeout_duration) {
142         return wait_until( lt,
143                            std::chrono::steady_clock::now() + timeout_duration);
144     }
145 
146     template< typename LockType, typename Rep, typename Period, typename Pred >
147     bool wait_for( LockType & lt, std::chrono::duration< Rep, Period > const& timeout_duration, Pred pred) {
148         return wait_until( lt,
149                            std::chrono::steady_clock::now() + timeout_duration,
150                            pred);
151     }
152 };
153 
154 class BOOST_FIBERS_DECL condition_variable {
155 private:
156     condition_variable_any      cnd_;
157 
158 public:
159     condition_variable() = default;
160 
161     condition_variable( condition_variable const&) = delete;
162     condition_variable & operator=( condition_variable const&) = delete;
163 
164     void notify_one() noexcept {
165         cnd_.notify_one();
166     }
167 
168     void notify_all() noexcept {
169         cnd_.notify_all();
170     }
171 
172     void wait( std::unique_lock< mutex > & lt) {
173         // pre-condition
174         BOOST_ASSERT( lt.owns_lock() );
175         BOOST_ASSERT( context::active() == lt.mutex()->owner_);
176         cnd_.wait( lt);
177         // post-condition
178         BOOST_ASSERT( lt.owns_lock() );
179         BOOST_ASSERT( context::active() == lt.mutex()->owner_);
180     }
181 
182     template< typename Pred >
183     void wait( std::unique_lock< mutex > & lt, Pred pred) {
184         // pre-condition
185         BOOST_ASSERT( lt.owns_lock() );
186         BOOST_ASSERT( context::active() == lt.mutex()->owner_);
187         cnd_.wait( lt, pred);
188         // post-condition
189         BOOST_ASSERT( lt.owns_lock() );
190         BOOST_ASSERT( context::active() == lt.mutex()->owner_);
191     }
192 
193     template< typename Clock, typename Duration >
194     cv_status wait_until( std::unique_lock< mutex > & lt,
195                           std::chrono::time_point< Clock, Duration > const& timeout_time) {
196         // pre-condition
197         BOOST_ASSERT( lt.owns_lock() );
198         BOOST_ASSERT( context::active() == lt.mutex()->owner_);
199         cv_status result = cnd_.wait_until( lt, timeout_time);
200         // post-condition
201         BOOST_ASSERT( lt.owns_lock() );
202         BOOST_ASSERT( context::active() == lt.mutex()->owner_);
203         return result;
204     }
205 
206     template< typename Clock, typename Duration, typename Pred >
207     bool wait_until( std::unique_lock< mutex > & lt,
208                      std::chrono::time_point< Clock, Duration > const& timeout_time, Pred pred) {
209         // pre-condition
210         BOOST_ASSERT( lt.owns_lock() );
211         BOOST_ASSERT( context::active() == lt.mutex()->owner_);
212         bool result = cnd_.wait_until( lt, timeout_time, pred);
213         // post-condition
214         BOOST_ASSERT( lt.owns_lock() );
215         BOOST_ASSERT( context::active() == lt.mutex()->owner_);
216         return result;
217     }
218 
219     template< typename Rep, typename Period >
220     cv_status wait_for( std::unique_lock< mutex > & lt,
221                         std::chrono::duration< Rep, Period > const& timeout_duration) {
222         // pre-condition
223         BOOST_ASSERT( lt.owns_lock() );
224         BOOST_ASSERT( context::active() == lt.mutex()->owner_);
225         cv_status result = cnd_.wait_for( lt, timeout_duration);
226         // post-condition
227         BOOST_ASSERT( lt.owns_lock() );
228         BOOST_ASSERT( context::active() == lt.mutex()->owner_);
229         return result;
230     }
231 
232     template< typename Rep, typename Period, typename Pred >
233     bool wait_for( std::unique_lock< mutex > & lt,
234                    std::chrono::duration< Rep, Period > const& timeout_duration, Pred pred) {
235         // pre-condition
236         BOOST_ASSERT( lt.owns_lock() );
237         BOOST_ASSERT( context::active() == lt.mutex()->owner_);
238         bool result = cnd_.wait_for( lt, timeout_duration, pred);
239         // post-condition
240         BOOST_ASSERT( lt.owns_lock() );
241         BOOST_ASSERT( context::active() == lt.mutex()->owner_);
242         return result;
243     }
244 };
245 
246 }}
247 
248 #ifdef _MSC_VER
249 # pragma warning(pop)
250 #endif
251 
252 #ifdef BOOST_HAS_ABI_HEADERS
253 #  include BOOST_ABI_SUFFIX
254 #endif
255 
256 #endif // BOOST_FIBERS_CONDITION_VARIABLE_H
257