245 lines · c
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef _LIBCPP___CXX03___CONDITION_VARIABLE_CONDITION_VARIABLE_H10#define _LIBCPP___CXX03___CONDITION_VARIABLE_CONDITION_VARIABLE_H11 12#include <__cxx03/__chrono/duration.h>13#include <__cxx03/__chrono/steady_clock.h>14#include <__cxx03/__chrono/system_clock.h>15#include <__cxx03/__chrono/time_point.h>16#include <__cxx03/__config>17#include <__cxx03/__mutex/mutex.h>18#include <__cxx03/__mutex/unique_lock.h>19#include <__cxx03/__system_error/system_error.h>20#include <__cxx03/__thread/support.h>21#include <__cxx03/__type_traits/enable_if.h>22#include <__cxx03/__type_traits/is_floating_point.h>23#include <__cxx03/__utility/move.h>24#include <__cxx03/limits>25#include <__cxx03/ratio>26 27#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)28# pragma GCC system_header29#endif30 31_LIBCPP_PUSH_MACROS32#include <__cxx03/__undef_macros>33 34_LIBCPP_BEGIN_NAMESPACE_STD35 36#ifndef _LIBCPP_HAS_NO_THREADS37 38// enum class cv_status39_LIBCPP_DECLARE_STRONG_ENUM(cv_status){no_timeout, timeout};40_LIBCPP_DECLARE_STRONG_ENUM_EPILOG(cv_status)41 42class _LIBCPP_EXPORTED_FROM_ABI condition_variable {43 __libcpp_condvar_t __cv_ = _LIBCPP_CONDVAR_INITIALIZER;44 45public:46 _LIBCPP_HIDE_FROM_ABI condition_variable() _NOEXCEPT = default;47 48# ifdef _LIBCPP_HAS_TRIVIAL_CONDVAR_DESTRUCTION49 ~condition_variable() = default;50# else51 ~condition_variable();52# endif53 54 condition_variable(const condition_variable&) = delete;55 condition_variable& operator=(const condition_variable&) = delete;56 57 void notify_one() _NOEXCEPT;58 void notify_all() _NOEXCEPT;59 60 void wait(unique_lock<mutex>& __lk) _NOEXCEPT;61 template <class _Predicate>62 _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS void wait(unique_lock<mutex>& __lk, _Predicate __pred);63 64 template <class _Clock, class _Duration>65 _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS cv_status66 wait_until(unique_lock<mutex>& __lk, const chrono::time_point<_Clock, _Duration>& __t);67 68 template <class _Clock, class _Duration, class _Predicate>69 _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS bool70 wait_until(unique_lock<mutex>& __lk, const chrono::time_point<_Clock, _Duration>& __t, _Predicate __pred);71 72 template <class _Rep, class _Period>73 _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS cv_status74 wait_for(unique_lock<mutex>& __lk, const chrono::duration<_Rep, _Period>& __d);75 76 template <class _Rep, class _Period, class _Predicate>77 bool _LIBCPP_HIDE_FROM_ABI78 wait_for(unique_lock<mutex>& __lk, const chrono::duration<_Rep, _Period>& __d, _Predicate __pred);79 80 typedef __libcpp_condvar_t* native_handle_type;81 _LIBCPP_HIDE_FROM_ABI native_handle_type native_handle() { return &__cv_; }82 83private:84 void85 __do_timed_wait(unique_lock<mutex>& __lk, chrono::time_point<chrono::system_clock, chrono::nanoseconds>) _NOEXCEPT;86# if defined(_LIBCPP_HAS_COND_CLOCKWAIT)87 _LIBCPP_HIDE_FROM_ABI void88 __do_timed_wait(unique_lock<mutex>& __lk, chrono::time_point<chrono::steady_clock, chrono::nanoseconds>) _NOEXCEPT;89# endif90 template <class _Clock>91 _LIBCPP_HIDE_FROM_ABI void92 __do_timed_wait(unique_lock<mutex>& __lk, chrono::time_point<_Clock, chrono::nanoseconds>) _NOEXCEPT;93};94#endif // !_LIBCPP_HAS_NO_THREADS95 96template <class _Rep, class _Period, __enable_if_t<is_floating_point<_Rep>::value, int> = 0>97inline _LIBCPP_HIDE_FROM_ABI chrono::nanoseconds __safe_nanosecond_cast(chrono::duration<_Rep, _Period> __d) {98 using namespace chrono;99 using __ratio = ratio_divide<_Period, nano>;100 using __ns_rep = nanoseconds::rep;101 _Rep __result_float = __d.count() * __ratio::num / __ratio::den;102 103 _Rep __result_max = numeric_limits<__ns_rep>::max();104 if (__result_float >= __result_max) {105 return nanoseconds::max();106 }107 108 _Rep __result_min = numeric_limits<__ns_rep>::min();109 if (__result_float <= __result_min) {110 return nanoseconds::min();111 }112 113 return nanoseconds(static_cast<__ns_rep>(__result_float));114}115 116template <class _Rep, class _Period, __enable_if_t<!is_floating_point<_Rep>::value, int> = 0>117inline _LIBCPP_HIDE_FROM_ABI chrono::nanoseconds __safe_nanosecond_cast(chrono::duration<_Rep, _Period> __d) {118 using namespace chrono;119 if (__d.count() == 0) {120 return nanoseconds(0);121 }122 123 using __ratio = ratio_divide<_Period, nano>;124 using __ns_rep = nanoseconds::rep;125 __ns_rep __result_max = numeric_limits<__ns_rep>::max();126 if (__d.count() > 0 && __d.count() > __result_max / __ratio::num) {127 return nanoseconds::max();128 }129 130 __ns_rep __result_min = numeric_limits<__ns_rep>::min();131 if (__d.count() < 0 && __d.count() < __result_min / __ratio::num) {132 return nanoseconds::min();133 }134 135 __ns_rep __result = __d.count() * __ratio::num / __ratio::den;136 if (__result == 0) {137 return nanoseconds(1);138 }139 140 return nanoseconds(__result);141}142 143#ifndef _LIBCPP_HAS_NO_THREADS144template <class _Predicate>145void condition_variable::wait(unique_lock<mutex>& __lk, _Predicate __pred) {146 while (!__pred())147 wait(__lk);148}149 150template <class _Clock, class _Duration>151cv_status condition_variable::wait_until(unique_lock<mutex>& __lk, const chrono::time_point<_Clock, _Duration>& __t) {152 using namespace chrono;153 using __clock_tp_ns = time_point<_Clock, nanoseconds>;154 155 typename _Clock::time_point __now = _Clock::now();156 if (__t <= __now)157 return cv_status::timeout;158 159 __clock_tp_ns __t_ns = __clock_tp_ns(std::__safe_nanosecond_cast(__t.time_since_epoch()));160 161 __do_timed_wait(__lk, __t_ns);162 return _Clock::now() < __t ? cv_status::no_timeout : cv_status::timeout;163}164 165template <class _Clock, class _Duration, class _Predicate>166bool condition_variable::wait_until(167 unique_lock<mutex>& __lk, const chrono::time_point<_Clock, _Duration>& __t, _Predicate __pred) {168 while (!__pred()) {169 if (wait_until(__lk, __t) == cv_status::timeout)170 return __pred();171 }172 return true;173}174 175template <class _Rep, class _Period>176cv_status condition_variable::wait_for(unique_lock<mutex>& __lk, const chrono::duration<_Rep, _Period>& __d) {177 using namespace chrono;178 if (__d <= __d.zero())179 return cv_status::timeout;180 using __ns_rep = nanoseconds::rep;181 steady_clock::time_point __c_now = steady_clock::now();182 183# if defined(_LIBCPP_HAS_COND_CLOCKWAIT)184 using __clock_tp_ns = time_point<steady_clock, nanoseconds>;185 __ns_rep __now_count_ns = std::__safe_nanosecond_cast(__c_now.time_since_epoch()).count();186# else187 using __clock_tp_ns = time_point<system_clock, nanoseconds>;188 __ns_rep __now_count_ns = std::__safe_nanosecond_cast(system_clock::now().time_since_epoch()).count();189# endif190 191 __ns_rep __d_ns_count = std::__safe_nanosecond_cast(__d).count();192 193 if (__now_count_ns > numeric_limits<__ns_rep>::max() - __d_ns_count) {194 __do_timed_wait(__lk, __clock_tp_ns::max());195 } else {196 __do_timed_wait(__lk, __clock_tp_ns(nanoseconds(__now_count_ns + __d_ns_count)));197 }198 199 return steady_clock::now() - __c_now < __d ? cv_status::no_timeout : cv_status::timeout;200}201 202template <class _Rep, class _Period, class _Predicate>203inline bool204condition_variable::wait_for(unique_lock<mutex>& __lk, const chrono::duration<_Rep, _Period>& __d, _Predicate __pred) {205 return wait_until(__lk, chrono::steady_clock::now() + __d, std::move(__pred));206}207 208# if defined(_LIBCPP_HAS_COND_CLOCKWAIT)209inline void condition_variable::__do_timed_wait(210 unique_lock<mutex>& __lk, chrono::time_point<chrono::steady_clock, chrono::nanoseconds> __tp) _NOEXCEPT {211 using namespace chrono;212 if (!__lk.owns_lock())213 __throw_system_error(EPERM, "condition_variable::timed wait: mutex not locked");214 nanoseconds __d = __tp.time_since_epoch();215 timespec __ts;216 seconds __s = duration_cast<seconds>(__d);217 using __ts_sec = decltype(__ts.tv_sec);218 const __ts_sec __ts_sec_max = numeric_limits<__ts_sec>::max();219 if (__s.count() < __ts_sec_max) {220 __ts.tv_sec = static_cast<__ts_sec>(__s.count());221 __ts.tv_nsec = (__d - __s).count();222 } else {223 __ts.tv_sec = __ts_sec_max;224 __ts.tv_nsec = giga::num - 1;225 }226 int __ec = pthread_cond_clockwait(&__cv_, __lk.mutex()->native_handle(), CLOCK_MONOTONIC, &__ts);227 if (__ec != 0 && __ec != ETIMEDOUT)228 __throw_system_error(__ec, "condition_variable timed_wait failed");229}230# endif // _LIBCPP_HAS_COND_CLOCKWAIT231 232template <class _Clock>233inline void condition_variable::__do_timed_wait(unique_lock<mutex>& __lk,234 chrono::time_point<_Clock, chrono::nanoseconds> __tp) _NOEXCEPT {235 wait_for(__lk, __tp - _Clock::now());236}237 238#endif // _LIBCPP_HAS_NO_THREADS239 240_LIBCPP_END_NAMESPACE_STD241 242_LIBCPP_POP_MACROS243 244#endif // _LIBCPP___CXX03___CONDITION_VARIABLE_CONDITION_VARIABLE_H245