libstdc++
condition_variable
Go to the documentation of this file.
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
52namespace 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 static_cast<std::time_t>(__s.time_since_epoch().count()),
198 static_cast<long>(__ns.count())
199 };
200
201 _M_cond.wait_until(*__lock.mutex(), CLOCK_MONOTONIC, __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 static_cast<std::time_t>(__s.time_since_epoch().count()),
219 static_cast<long>(__ns.count())
220 };
221
222 _M_cond.wait_until(*__lock.mutex(), __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(__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(__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