1 | // <condition_variable> -*- C++ -*- |
2 | |
3 | // Copyright (C) 2008-2021 Free Software Foundation, Inc. |
4 | // |
5 | // This file is part of the GNU ISO C++ Library. This library is free |
6 | // software; you can redistribute it and/or modify it under the |
7 | // terms of the GNU General Public License as published by the |
8 | // Free Software Foundation; either version 3, or (at your option) |
9 | // any later version. |
10 | |
11 | // This library is distributed in the hope that it will be useful, |
12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | // GNU General Public License for more details. |
15 | |
16 | // Under Section 7 of GPL version 3, you are granted additional |
17 | // permissions described in the GCC Runtime Library Exception, version |
18 | // 3.1, as published by the Free Software Foundation. |
19 | |
20 | // You should have received a copy of the GNU General Public License and |
21 | // a copy of the GCC Runtime Library Exception along with this program; |
22 | // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see |
23 | // <http://www.gnu.org/licenses/>. |
24 | |
25 | /** @file include/condition_variable |
26 | * This is a Standard C++ Library header. |
27 | */ |
28 | |
29 | #ifndef _GLIBCXX_CONDITION_VARIABLE |
30 | #define _GLIBCXX_CONDITION_VARIABLE 1 |
31 | |
32 | #pragma GCC system_header |
33 | |
34 | #if __cplusplus < 201103L |
35 | # include <bits/c++0x_warning.h> |
36 | #else |
37 | |
38 | #include <chrono> |
39 | |
40 | #include <bits/std_mutex.h> |
41 | #include <bits/unique_lock.h> |
42 | #include <bits/alloc_traits.h> |
43 | #include <bits/shared_ptr.h> |
44 | #include <bits/cxxabi_forced.h> |
45 | |
46 | #if __cplusplus > 201703L |
47 | # include <stop_token> |
48 | #endif |
49 | |
50 | #if defined(_GLIBCXX_HAS_GTHREADS) |
51 | |
52 | namespace std _GLIBCXX_VISIBILITY(default) |
53 | { |
54 | _GLIBCXX_BEGIN_NAMESPACE_VERSION |
55 | |
56 | /** |
57 | * @defgroup condition_variables Condition Variables |
58 | * @ingroup concurrency |
59 | * |
60 | * Classes for condition_variable support. |
61 | * @{ |
62 | */ |
63 | |
64 | /// cv_status |
65 | enum class cv_status { no_timeout, timeout }; |
66 | |
67 | /// condition_variable |
68 | class condition_variable |
69 | { |
70 | using steady_clock = chrono::steady_clock; |
71 | using system_clock = chrono::system_clock; |
72 | #ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT |
73 | using __clock_t = steady_clock; |
74 | #else |
75 | using __clock_t = system_clock; |
76 | #endif |
77 | |
78 | __condvar _M_cond; |
79 | |
80 | public: |
81 | typedef __gthread_cond_t* native_handle_type; |
82 | |
83 | condition_variable() noexcept; |
84 | ~condition_variable() noexcept; |
85 | |
86 | condition_variable(const condition_variable&) = delete; |
87 | condition_variable& operator=(const condition_variable&) = delete; |
88 | |
89 | void |
90 | notify_one() noexcept; |
91 | |
92 | void |
93 | notify_all() noexcept; |
94 | |
95 | void |
96 | wait(unique_lock<mutex>& __lock) noexcept; |
97 | |
98 | template<typename _Predicate> |
99 | void |
100 | wait(unique_lock<mutex>& __lock, _Predicate __p) |
101 | { |
102 | while (!__p()) |
103 | wait(__lock); |
104 | } |
105 | |
106 | #ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT |
107 | template<typename _Duration> |
108 | cv_status |
109 | wait_until(unique_lock<mutex>& __lock, |
110 | const chrono::time_point<steady_clock, _Duration>& __atime) |
111 | { return __wait_until_impl(__lock, __atime); } |
112 | #endif |
113 | |
114 | template<typename _Duration> |
115 | cv_status |
116 | wait_until(unique_lock<mutex>& __lock, |
117 | const chrono::time_point<system_clock, _Duration>& __atime) |
118 | { return __wait_until_impl(__lock, __atime); } |
119 | |
120 | template<typename _Clock, typename _Duration> |
121 | cv_status |
122 | wait_until(unique_lock<mutex>& __lock, |
123 | const chrono::time_point<_Clock, _Duration>& __atime) |
124 | { |
125 | #if __cplusplus > 201703L |
126 | static_assert(chrono::is_clock_v<_Clock>); |
127 | #endif |
128 | using __s_dur = typename __clock_t::duration; |
129 | const typename _Clock::time_point __c_entry = _Clock::now(); |
130 | const __clock_t::time_point __s_entry = __clock_t::now(); |
131 | const auto __delta = __atime - __c_entry; |
132 | const auto __s_atime = __s_entry + |
133 | chrono::__detail::ceil<__s_dur>(__delta); |
134 | |
135 | if (__wait_until_impl(__lock, __s_atime) == cv_status::no_timeout) |
136 | return cv_status::no_timeout; |
137 | // We got a timeout when measured against __clock_t but |
138 | // we need to check against the caller-supplied clock |
139 | // to tell whether we should return a timeout. |
140 | if (_Clock::now() < __atime) |
141 | return cv_status::no_timeout; |
142 | return cv_status::timeout; |
143 | } |
144 | |
145 | template<typename _Clock, typename _Duration, typename _Predicate> |
146 | bool |
147 | wait_until(unique_lock<mutex>& __lock, |
148 | const chrono::time_point<_Clock, _Duration>& __atime, |
149 | _Predicate __p) |
150 | { |
151 | while (!__p()) |
152 | if (wait_until(__lock, __atime) == cv_status::timeout) |
153 | return __p(); |
154 | return true; |
155 | } |
156 | |
157 | template<typename _Rep, typename _Period> |
158 | cv_status |
159 | wait_for(unique_lock<mutex>& __lock, |
160 | const chrono::duration<_Rep, _Period>& __rtime) |
161 | { |
162 | using __dur = typename steady_clock::duration; |
163 | return wait_until(__lock, |
164 | steady_clock::now() + |
165 | chrono::__detail::ceil<__dur>(__rtime)); |
166 | } |
167 | |
168 | template<typename _Rep, typename _Period, typename _Predicate> |
169 | bool |
170 | wait_for(unique_lock<mutex>& __lock, |
171 | const chrono::duration<_Rep, _Period>& __rtime, |
172 | _Predicate __p) |
173 | { |
174 | using __dur = typename steady_clock::duration; |
175 | return wait_until(__lock, |
176 | steady_clock::now() + |
177 | chrono::__detail::ceil<__dur>(__rtime), |
178 | std::move(__p)); |
179 | } |
180 | |
181 | native_handle_type |
182 | native_handle() |
183 | { return _M_cond.native_handle(); } |
184 | |
185 | private: |
186 | #ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT |
187 | template<typename _Dur> |
188 | cv_status |
189 | __wait_until_impl(unique_lock<mutex>& __lock, |
190 | const chrono::time_point<steady_clock, _Dur>& __atime) |
191 | { |
192 | auto __s = chrono::time_point_cast<chrono::seconds>(__atime); |
193 | auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); |
194 | |
195 | __gthread_time_t __ts = |
196 | { |
197 | .tv_sec: static_cast<std::time_t>(__s.time_since_epoch().count()), |
198 | .tv_nsec: static_cast<long>(__ns.count()) |
199 | }; |
200 | |
201 | _M_cond.wait_until(m&: *__lock.mutex(), CLOCK_MONOTONIC, abs_time&: __ts); |
202 | |
203 | return (steady_clock::now() < __atime |
204 | ? cv_status::no_timeout : cv_status::timeout); |
205 | } |
206 | #endif |
207 | |
208 | template<typename _Dur> |
209 | cv_status |
210 | __wait_until_impl(unique_lock<mutex>& __lock, |
211 | const chrono::time_point<system_clock, _Dur>& __atime) |
212 | { |
213 | auto __s = chrono::time_point_cast<chrono::seconds>(__atime); |
214 | auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); |
215 | |
216 | __gthread_time_t __ts = |
217 | { |
218 | .tv_sec: static_cast<std::time_t>(__s.time_since_epoch().count()), |
219 | .tv_nsec: static_cast<long>(__ns.count()) |
220 | }; |
221 | |
222 | _M_cond.wait_until(m&: *__lock.mutex(), abs_time&: __ts); |
223 | |
224 | return (system_clock::now() < __atime |
225 | ? cv_status::no_timeout : cv_status::timeout); |
226 | } |
227 | }; |
228 | |
229 | void |
230 | notify_all_at_thread_exit(condition_variable&, unique_lock<mutex>); |
231 | |
232 | struct __at_thread_exit_elt |
233 | { |
234 | __at_thread_exit_elt* _M_next; |
235 | void (*_M_cb)(void*); |
236 | }; |
237 | |
238 | inline namespace _V2 { |
239 | |
240 | /// condition_variable_any |
241 | // Like above, but mutex is not required to have try_lock. |
242 | class condition_variable_any |
243 | { |
244 | #ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT |
245 | using __clock_t = chrono::steady_clock; |
246 | #else |
247 | using __clock_t = chrono::system_clock; |
248 | #endif |
249 | condition_variable _M_cond; |
250 | shared_ptr<mutex> _M_mutex; |
251 | |
252 | // scoped unlock - unlocks in ctor, re-locks in dtor |
253 | template<typename _Lock> |
254 | struct _Unlock |
255 | { |
256 | explicit _Unlock(_Lock& __lk) : _M_lock(__lk) { __lk.unlock(); } |
257 | |
258 | #pragma GCC diagnostic push |
259 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations" |
260 | ~_Unlock() noexcept(false) |
261 | { |
262 | if (uncaught_exception()) |
263 | { |
264 | __try |
265 | { _M_lock.lock(); } |
266 | __catch(const __cxxabiv1::__forced_unwind&) |
267 | { __throw_exception_again; } |
268 | __catch(...) |
269 | { } |
270 | } |
271 | else |
272 | _M_lock.lock(); |
273 | } |
274 | #pragma GCC diagnostic pop |
275 | |
276 | _Unlock(const _Unlock&) = delete; |
277 | _Unlock& operator=(const _Unlock&) = delete; |
278 | |
279 | _Lock& _M_lock; |
280 | }; |
281 | |
282 | public: |
283 | condition_variable_any() : _M_mutex(std::make_shared<mutex>()) { } |
284 | ~condition_variable_any() = default; |
285 | |
286 | condition_variable_any(const condition_variable_any&) = delete; |
287 | condition_variable_any& operator=(const condition_variable_any&) = delete; |
288 | |
289 | void |
290 | notify_one() noexcept |
291 | { |
292 | lock_guard<mutex> __lock(*_M_mutex); |
293 | _M_cond.notify_one(); |
294 | } |
295 | |
296 | void |
297 | notify_all() noexcept |
298 | { |
299 | lock_guard<mutex> __lock(*_M_mutex); |
300 | _M_cond.notify_all(); |
301 | } |
302 | |
303 | template<typename _Lock> |
304 | void |
305 | wait(_Lock& __lock) |
306 | { |
307 | shared_ptr<mutex> __mutex = _M_mutex; |
308 | unique_lock<mutex> __my_lock(*__mutex); |
309 | _Unlock<_Lock> __unlock(__lock); |
310 | // *__mutex must be unlocked before re-locking __lock so move |
311 | // ownership of *__mutex lock to an object with shorter lifetime. |
312 | unique_lock<mutex> __my_lock2(std::move(__my_lock)); |
313 | _M_cond.wait(lock&: __my_lock2); |
314 | } |
315 | |
316 | |
317 | template<typename _Lock, typename _Predicate> |
318 | void |
319 | wait(_Lock& __lock, _Predicate __p) |
320 | { |
321 | while (!__p()) |
322 | wait(__lock); |
323 | } |
324 | |
325 | template<typename _Lock, typename _Clock, typename _Duration> |
326 | cv_status |
327 | wait_until(_Lock& __lock, |
328 | const chrono::time_point<_Clock, _Duration>& __atime) |
329 | { |
330 | shared_ptr<mutex> __mutex = _M_mutex; |
331 | unique_lock<mutex> __my_lock(*__mutex); |
332 | _Unlock<_Lock> __unlock(__lock); |
333 | // *__mutex must be unlocked before re-locking __lock so move |
334 | // ownership of *__mutex lock to an object with shorter lifetime. |
335 | unique_lock<mutex> __my_lock2(std::move(__my_lock)); |
336 | return _M_cond.wait_until(__my_lock2, __atime); |
337 | } |
338 | |
339 | template<typename _Lock, typename _Clock, |
340 | typename _Duration, typename _Predicate> |
341 | bool |
342 | wait_until(_Lock& __lock, |
343 | const chrono::time_point<_Clock, _Duration>& __atime, |
344 | _Predicate __p) |
345 | { |
346 | while (!__p()) |
347 | if (wait_until(__lock, __atime) == cv_status::timeout) |
348 | return __p(); |
349 | return true; |
350 | } |
351 | |
352 | template<typename _Lock, typename _Rep, typename _Period> |
353 | cv_status |
354 | wait_for(_Lock& __lock, const chrono::duration<_Rep, _Period>& __rtime) |
355 | { return wait_until(__lock, __clock_t::now() + __rtime); } |
356 | |
357 | template<typename _Lock, typename _Rep, |
358 | typename _Period, typename _Predicate> |
359 | bool |
360 | wait_for(_Lock& __lock, |
361 | const chrono::duration<_Rep, _Period>& __rtime, _Predicate __p) |
362 | { return wait_until(__lock, __clock_t::now() + __rtime, std::move(__p)); } |
363 | |
364 | #ifdef __cpp_lib_jthread |
365 | template <class _Lock, class _Predicate> |
366 | bool wait(_Lock& __lock, |
367 | stop_token __stoken, |
368 | _Predicate __p) |
369 | { |
370 | if (__stoken.stop_requested()) |
371 | { |
372 | return __p(); |
373 | } |
374 | |
375 | std::stop_callback __cb(__stoken, [this] { notify_all(); }); |
376 | shared_ptr<mutex> __mutex = _M_mutex; |
377 | while (!__p()) |
378 | { |
379 | unique_lock<mutex> __my_lock(*__mutex); |
380 | if (__stoken.stop_requested()) |
381 | { |
382 | return false; |
383 | } |
384 | // *__mutex must be unlocked before re-locking __lock so move |
385 | // ownership of *__mutex lock to an object with shorter lifetime. |
386 | _Unlock<_Lock> __unlock(__lock); |
387 | unique_lock<mutex> __my_lock2(std::move(__my_lock)); |
388 | _M_cond.wait(lock&: __my_lock2); |
389 | } |
390 | return true; |
391 | } |
392 | |
393 | template <class _Lock, class _Clock, class _Duration, class _Predicate> |
394 | bool wait_until(_Lock& __lock, |
395 | stop_token __stoken, |
396 | const chrono::time_point<_Clock, _Duration>& __abs_time, |
397 | _Predicate __p) |
398 | { |
399 | if (__stoken.stop_requested()) |
400 | { |
401 | return __p(); |
402 | } |
403 | |
404 | std::stop_callback __cb(__stoken, [this] { notify_all(); }); |
405 | shared_ptr<mutex> __mutex = _M_mutex; |
406 | while (!__p()) |
407 | { |
408 | bool __stop; |
409 | { |
410 | unique_lock<mutex> __my_lock(*__mutex); |
411 | if (__stoken.stop_requested()) |
412 | { |
413 | return false; |
414 | } |
415 | _Unlock<_Lock> __u(__lock); |
416 | unique_lock<mutex> __my_lock2(std::move(__my_lock)); |
417 | const auto __status = _M_cond.wait_until(__my_lock2, __abs_time); |
418 | __stop = (__status == std::cv_status::timeout) || __stoken.stop_requested(); |
419 | } |
420 | if (__stop) |
421 | { |
422 | return __p(); |
423 | } |
424 | } |
425 | return true; |
426 | } |
427 | |
428 | template <class _Lock, class _Rep, class _Period, class _Predicate> |
429 | bool wait_for(_Lock& __lock, |
430 | stop_token __stoken, |
431 | const chrono::duration<_Rep, _Period>& __rel_time, |
432 | _Predicate __p) |
433 | { |
434 | auto __abst = std::chrono::steady_clock::now() + __rel_time; |
435 | return wait_until(__lock, |
436 | std::move(__stoken), |
437 | __abst, |
438 | std::move(__p)); |
439 | } |
440 | #endif |
441 | }; |
442 | |
443 | } // end inline namespace |
444 | |
445 | /// @} group condition_variables |
446 | _GLIBCXX_END_NAMESPACE_VERSION |
447 | } // namespace |
448 | |
449 | #endif // _GLIBCXX_HAS_GTHREADS |
450 | #endif // C++11 |
451 | #endif // _GLIBCXX_CONDITION_VARIABLE |
452 | |