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