Line data Source code
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 :
52 : namespace 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 8312 : wait(unique_lock<mutex>& __lock, _Predicate __p)
101 : {
102 16110 : while (!__p())
103 7798 : wait(__lock);
104 8312 : }
105 :
106 : #ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT
107 : template<typename _Duration>
108 : cv_status
109 6914 : wait_until(unique_lock<mutex>& __lock,
110 : const chrono::time_point<steady_clock, _Duration>& __atime)
111 6914 : { return __wait_until_impl(__lock, __atime); }
112 : #endif
113 :
114 : template<typename _Duration>
115 : cv_status
116 0 : wait_until(unique_lock<mutex>& __lock,
117 : const chrono::time_point<system_clock, _Duration>& __atime)
118 0 : { 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 2267 : wait_until(unique_lock<mutex>& __lock,
148 : const chrono::time_point<_Clock, _Duration>& __atime,
149 : _Predicate __p)
150 : {
151 6423 : while (!__p())
152 4441 : if (wait_until(__lock, __atime) == cv_status::timeout)
153 285 : return __p();
154 1982 : return true;
155 : }
156 :
157 : template<typename _Rep, typename _Period>
158 : cv_status
159 10 : wait_for(unique_lock<mutex>& __lock,
160 : const chrono::duration<_Rep, _Period>& __rtime)
161 : {
162 : using __dur = typename steady_clock::duration;
163 10 : return wait_until(__lock,
164 10 : steady_clock::now() +
165 20 : chrono::__detail::ceil<__dur>(__rtime));
166 : }
167 :
168 : template<typename _Rep, typename _Period, typename _Predicate>
169 : bool
170 2267 : 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 2267 : return wait_until(__lock,
176 2267 : steady_clock::now() +
177 4534 : chrono::__detail::ceil<__dur>(__rtime),
178 6801 : 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 6915 : __wait_until_impl(unique_lock<mutex>& __lock,
190 : const chrono::time_point<steady_clock, _Dur>& __atime)
191 : {
192 6915 : auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
193 6913 : auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
194 :
195 13823 : __gthread_time_t __ts =
196 : {
197 6912 : static_cast<std::time_t>(__s.time_since_epoch().count()),
198 6912 : static_cast<long>(__ns.count())
199 : };
200 :
201 6911 : _M_cond.wait_until(*__lock.mutex(), CLOCK_MONOTONIC, __ts);
202 :
203 6916 : return (steady_clock::now() < __atime
204 6915 : ? cv_status::no_timeout : cv_status::timeout);
205 : }
206 : #endif
207 :
208 : template<typename _Dur>
209 : cv_status
210 0 : __wait_until_impl(unique_lock<mutex>& __lock,
211 : const chrono::time_point<system_clock, _Dur>& __atime)
212 : {
213 0 : auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
214 0 : auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
215 :
216 0 : __gthread_time_t __ts =
217 : {
218 0 : static_cast<std::time_t>(__s.time_since_epoch().count()),
219 0 : static_cast<long>(__ns.count())
220 : };
221 :
222 0 : _M_cond.wait_until(*__lock.mutex(), __ts);
223 :
224 0 : return (system_clock::now() < __atime
225 0 : ? 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
|