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 
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
69  {
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,
111  { return __wait_until_impl(__lock, __atime); }
112 #endif
113 
114  template<typename _Duration>
115  cv_status
116  wait_until(unique_lock<mutex>& __lock,
118  { return __wait_until_impl(__lock, __atime); }
119 
120  template<typename _Clock, typename _Duration>
121  cv_status
122  wait_until(unique_lock<mutex>& __lock,
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,
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,
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,
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.
243  {
244 #ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT
246 #else
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 
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,
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,
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
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.
time_point
Definition: chrono:852
System clock.
Definition: chrono:1102
Monotonic clock.
Definition: chrono:1142
condition_variable
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:224
A movable scoped lock type.
Definition: unique_lock.h:58