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-exception
6//
7//===----------------------------------------------------------------------===//
8
9#ifndef _LIBCPP___CONDITION_VARIABLE_CONDITION_VARIABLE_H
10#define _LIBCPP___CONDITION_VARIABLE_CONDITION_VARIABLE_H
11
12#include <__chrono/duration.h>
13#include <__chrono/steady_clock.h>
14#include <__chrono/system_clock.h>
15#include <__chrono/time_point.h>
16#include <__config>
17#include <__mutex/mutex.h>
18#include <__mutex/unique_lock.h>
19#include <__system_error/throw_system_error.h>
20#include <__thread/support.h>
21#include <__type_traits/enable_if.h>
22#include <__type_traits/is_floating_point.h>
23#include <__utility/move.h>
24#include <limits>
25#include <ratio>
26
27#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
28# pragma GCC system_header
29#endif
30
31_LIBCPP_PUSH_MACROS
32#include <__undef_macros>
33
34#if _LIBCPP_HAS_THREADS
35
36_LIBCPP_BEGIN_NAMESPACE_STD
37_LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
38
39# if !_LIBCPP_HAS_THREAD_API_PTHREAD
40# define _LIBCPP_HAS_COND_CLOCKWAIT 0
41# elif (defined(__ANDROID__) && __ANDROID_API__ >= 30) || _LIBCPP_GLIBC_PREREQ(2, 30)
42# define _LIBCPP_HAS_COND_CLOCKWAIT 1
43# else
44# define _LIBCPP_HAS_COND_CLOCKWAIT 0
45# endif
46
47// enum class cv_status
48_LIBCPP_DECLARE_STRONG_ENUM(cv_status){no_timeout, timeout};
49_LIBCPP_DECLARE_STRONG_ENUM_EPILOG(cv_status)
50
51template <class _Rep, class _Period, __enable_if_t<is_floating_point<_Rep>::value, int> = 0>
52inline _LIBCPP_HIDE_FROM_ABI chrono::nanoseconds __safe_nanosecond_cast(chrono::duration<_Rep, _Period> __d) {
53 using namespace chrono;
54 using __ratio = ratio_divide<_Period, nano>;
55 using __ns_rep = nanoseconds::rep;
56 _Rep __result_float = __d.count() * __ratio::num / __ratio::den;
57
58 _Rep __result_max = numeric_limits<__ns_rep>::max();
59 if (__result_float >= __result_max) {
60 return nanoseconds::max();
61 }
62
63 _Rep __result_min = numeric_limits<__ns_rep>::min();
64 if (__result_float <= __result_min) {
65 return nanoseconds::min();
66 }
67
68 return nanoseconds(static_cast<__ns_rep>(__result_float));
69}
70
71template <class _Rep, class _Period, __enable_if_t<!is_floating_point<_Rep>::value, int> = 0>
72inline _LIBCPP_HIDE_FROM_ABI chrono::nanoseconds __safe_nanosecond_cast(chrono::duration<_Rep, _Period> __d) {
73 using namespace chrono;
74 if (__d.count() == 0) {
75 return nanoseconds(0);
76 }
77
78 using __ratio = ratio_divide<_Period, nano>;
79 using __ns_rep = nanoseconds::rep;
80 __ns_rep __result_max = numeric_limits<__ns_rep>::max();
81 if (__d.count() > 0 && __d.count() > __result_max / __ratio::num) {
82 return nanoseconds::max();
83 }
84
85 __ns_rep __result_min = numeric_limits<__ns_rep>::min();
86 if (__d.count() < 0 && __d.count() < __result_min / __ratio::num) {
87 return nanoseconds::min();
88 }
89
90 __ns_rep __result = __d.count() * __ratio::num / __ratio::den;
91 if (__result == 0) {
92 return nanoseconds(1);
93 }
94
95 return nanoseconds(__result);
96}
97
98class _LIBCPP_EXPORTED_FROM_ABI _LIBCPP_WARN_UNUSED condition_variable {
99 __libcpp_condvar_t __cv_ = _LIBCPP_CONDVAR_INITIALIZER;
100
101public:
102 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR condition_variable() _NOEXCEPT = default;
103
104# if _LIBCPP_HAS_TRIVIAL_CONDVAR_DESTRUCTION
105 ~condition_variable() = default;
106# else
107 ~condition_variable();
108# endif
109
110 condition_variable(const condition_variable&) = delete;
111 condition_variable& operator=(const condition_variable&) = delete;
112
113 void notify_one() _NOEXCEPT;
114 void notify_all() _NOEXCEPT;
115
116 void wait(unique_lock<mutex>& __lk) _NOEXCEPT;
117
118 template <class _Predicate>
119 _LIBCPP_HIDE_FROM_ABI void wait(unique_lock<mutex>& __lk, _Predicate __pred) {
120 while (!__pred())
121 wait(__lk);
122 }
123
124 template <class _Clock, class _Duration>
125 _LIBCPP_HIDE_FROM_ABI cv_status
126 wait_until(unique_lock<mutex>& __lk, const chrono::time_point<_Clock, _Duration>& __t) {
127 using namespace chrono;
128 using __clock_tp_ns = time_point<_Clock, nanoseconds>;
129
130 typename _Clock::time_point __now = _Clock::now();
131 if (__t <= __now)
132 return cv_status::timeout;
133
134 __clock_tp_ns __t_ns = __clock_tp_ns(std::__safe_nanosecond_cast(__t.time_since_epoch()));
135
136 __do_timed_wait(__lk, __t_ns);
137 return _Clock::now() < __t ? cv_status::no_timeout : cv_status::timeout;
138 }
139
140 template <class _Clock, class _Duration, class _Predicate>
141 _LIBCPP_HIDE_FROM_ABI bool
142 wait_until(unique_lock<mutex>& __lk, const chrono::time_point<_Clock, _Duration>& __t, _Predicate __pred) {
143 while (!__pred()) {
144 if (wait_until(__lk, __t) == cv_status::timeout)
145 return __pred();
146 }
147 return true;
148 }
149
150 template <class _Rep, class _Period>
151 _LIBCPP_HIDE_FROM_ABI cv_status wait_for(unique_lock<mutex>& __lk, const chrono::duration<_Rep, _Period>& __d) {
152 using namespace chrono;
153 if (__d <= __d.zero())
154 return cv_status::timeout;
155 using __ns_rep = nanoseconds::rep;
156 steady_clock::time_point __c_now = steady_clock::now();
157
158# if _LIBCPP_HAS_COND_CLOCKWAIT
159 using __clock_tp_ns = time_point<steady_clock, nanoseconds>;
160 __ns_rep __now_count_ns = std::__safe_nanosecond_cast(d: __c_now.time_since_epoch()).count();
161# else
162 using __clock_tp_ns = time_point<system_clock, nanoseconds>;
163 __ns_rep __now_count_ns = std::__safe_nanosecond_cast(system_clock::now().time_since_epoch()).count();
164# endif
165
166 __ns_rep __d_ns_count = std::__safe_nanosecond_cast(__d).count();
167
168 if (__now_count_ns > numeric_limits<__ns_rep>::max() - __d_ns_count) {
169 __do_timed_wait(__lk, __clock_tp_ns::max());
170 } else {
171 __do_timed_wait(__lk, __clock_tp_ns(nanoseconds(__now_count_ns + __d_ns_count)));
172 }
173
174 return steady_clock::now() - __c_now < __d ? cv_status::no_timeout : cv_status::timeout;
175 }
176
177 template <class _Rep, class _Period, class _Predicate>
178 bool _LIBCPP_HIDE_FROM_ABI
179 wait_for(unique_lock<mutex>& __lk, const chrono::duration<_Rep, _Period>& __d, _Predicate __pred);
180
181 typedef __libcpp_condvar_t* native_handle_type;
182 [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI native_handle_type native_handle() { return &__cv_; }
183
184private:
185 void
186 __do_timed_wait(unique_lock<mutex>& __lk, chrono::time_point<chrono::system_clock, chrono::nanoseconds>) _NOEXCEPT;
187# if _LIBCPP_HAS_COND_CLOCKWAIT
188 _LIBCPP_HIDE_FROM_ABI void
189 __do_timed_wait(unique_lock<mutex>& __lk, chrono::time_point<chrono::steady_clock, chrono::nanoseconds>) _NOEXCEPT;
190# endif
191 template <class _Clock>
192 _LIBCPP_HIDE_FROM_ABI void
193 __do_timed_wait(unique_lock<mutex>& __lk, chrono::time_point<_Clock, chrono::nanoseconds>) _NOEXCEPT;
194};
195#endif // _LIBCPP_HAS_THREADS
196
197#if _LIBCPP_HAS_THREADS
198
199template <class _Rep, class _Period, class _Predicate>
200inline bool
201condition_variable::wait_for(unique_lock<mutex>& __lk, const chrono::duration<_Rep, _Period>& __d, _Predicate __pred) {
202 return wait_until(__lk, chrono::steady_clock::now() + __d, std::move(__pred));
203}
204
205# if _LIBCPP_HAS_COND_CLOCKWAIT
206inline void condition_variable::__do_timed_wait(
207 unique_lock<mutex>& __lk, chrono::time_point<chrono::steady_clock, chrono::nanoseconds> __tp) _NOEXCEPT {
208 using namespace chrono;
209 if (!__lk.owns_lock())
210 std::__throw_system_error(EPERM, what_arg: "condition_variable::timed wait: mutex not locked");
211 nanoseconds __d = __tp.time_since_epoch();
212 timespec __ts;
213 seconds __s = duration_cast<seconds>(fd: __d);
214 using __ts_sec = decltype(__ts.tv_sec);
215 const __ts_sec __ts_sec_max = numeric_limits<__ts_sec>::max();
216 if (__s.count() < __ts_sec_max) {
217 __ts.tv_sec = static_cast<__ts_sec>(__s.count());
218 __ts.tv_nsec = (__d - __s).count();
219 } else {
220 __ts.tv_sec = __ts_sec_max;
221 __ts.tv_nsec = giga::num - 1;
222 }
223 int __ec = pthread_cond_clockwait(cond: &__cv_, mutex: __lk.mutex()->native_handle(), CLOCK_MONOTONIC, abstime: &__ts);
224 if (__ec != 0 && __ec != ETIMEDOUT)
225 std::__throw_system_error(ev: __ec, what_arg: "condition_variable timed_wait failed");
226}
227# endif // _LIBCPP_HAS_COND_CLOCKWAIT
228
229template <class _Clock>
230inline void condition_variable::__do_timed_wait(unique_lock<mutex>& __lk,
231 chrono::time_point<_Clock, chrono::nanoseconds> __tp) _NOEXCEPT {
232 wait_for(__lk, __tp - _Clock::now());
233}
234
235_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
236_LIBCPP_END_NAMESPACE_STD
237
238#endif // _LIBCPP_HAS_THREADS
239
240_LIBCPP_POP_MACROS
241
242#endif // _LIBCPP___CONDITION_VARIABLE_CONDITION_VARIABLE_H
243