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