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 452