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