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
54namespace 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