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 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 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
98template <class _Duration>
99chrono::steady_clock::time_point __rel_to_abs(const _Duration& __rel_time) {
100 return chrono::steady_clock::now() + chrono::__ceil<chrono::steady_clock::duration>(__rel_time);
101}
102
103class _LIBCPP_EXPORTED_FROM_ABI _LIBCPP_WARN_UNUSED condition_variable {
104 __libcpp_condvar_t __cv_ = _LIBCPP_CONDVAR_INITIALIZER;
105
106public:
107 _LIBCPP_CONSTEXPR condition_variable() _NOEXCEPT = default;
108
109# if _LIBCPP_HAS_TRIVIAL_CONDVAR_DESTRUCTION
110 ~condition_variable() = default;
111# else
112 ~condition_variable();
113# endif
114
115 condition_variable(const condition_variable&) = delete;
116 condition_variable& operator=(const condition_variable&) = delete;
117
118 void notify_one() _NOEXCEPT;
119 void notify_all() _NOEXCEPT;
120
121 void wait(unique_lock<mutex>& __lk) _NOEXCEPT;
122
123 template <class _Predicate>
124 void wait(unique_lock<mutex>& __lk, _Predicate __pred) {
125 while (!__pred())
126 wait(__lk);
127 }
128
129 template <class _Clock, class _Duration>
130 cv_status wait_until(unique_lock<mutex>& __lk, const chrono::time_point<_Clock, _Duration>& __t) {
131 using namespace chrono;
132 using __clock_tp_ns = time_point<_Clock, nanoseconds>;
133
134 typename _Clock::time_point __now = _Clock::now();
135 if (__t <= __now)
136 return cv_status::timeout;
137
138 __clock_tp_ns __t_ns = __clock_tp_ns(std::__safe_nanosecond_cast(__t.time_since_epoch()));
139
140 __do_timed_wait(__lk, __t_ns);
141 return _Clock::now() < __t ? cv_status::no_timeout : cv_status::timeout;
142 }
143
144 template <class _Clock, class _Duration, class _Predicate>
145 bool wait_until(unique_lock<mutex>& __lk, const chrono::time_point<_Clock, _Duration>& __t, _Predicate __pred) {
146 while (!__pred()) {
147 if (wait_until(__lk, __t) == cv_status::timeout)
148 return __pred();
149 }
150 return true;
151 }
152
153 template <class _Rep, class _Period>
154 cv_status wait_for(unique_lock<mutex>& __lk, const chrono::duration<_Rep, _Period>& __d) {
155 return wait_until(__lk, std::__rel_to_abs(__d));
156 }
157
158 template <class _Rep, class _Period, class _Predicate>
159 bool wait_for(unique_lock<mutex>& __lk, const chrono::duration<_Rep, _Period>& __d, _Predicate __pred);
160
161 typedef __libcpp_condvar_t* native_handle_type;
162 [[__nodiscard__]] native_handle_type native_handle() { return &__cv_; }
163
164private:
165 void
166 __do_timed_wait(unique_lock<mutex>& __lk, chrono::time_point<chrono::system_clock, chrono::nanoseconds>) _NOEXCEPT;
167# if _LIBCPP_HAS_COND_CLOCKWAIT
168 void
169 __do_timed_wait(unique_lock<mutex>& __lk, chrono::time_point<chrono::steady_clock, chrono::nanoseconds>) _NOEXCEPT;
170# endif
171 template <class _Clock>
172 void __do_timed_wait(unique_lock<mutex>& __lk, chrono::time_point<_Clock, chrono::nanoseconds>) _NOEXCEPT;
173};
174#endif // _LIBCPP_HAS_THREADS
175
176#if _LIBCPP_HAS_THREADS
177
178template <class _Rep, class _Period, class _Predicate>
179inline bool
180condition_variable::wait_for(unique_lock<mutex>& __lk, const chrono::duration<_Rep, _Period>& __d, _Predicate __pred) {
181 return wait_until(__lk, std::__rel_to_abs(__d), std::move(__pred));
182}
183
184# if _LIBCPP_HAS_COND_CLOCKWAIT
185inline void condition_variable::__do_timed_wait(
186 unique_lock<mutex>& __lk, chrono::time_point<chrono::steady_clock, chrono::nanoseconds> __tp) _NOEXCEPT {
187 using namespace chrono;
188 if (!__lk.owns_lock())
189 std::__throw_system_error(EPERM, what_arg: "condition_variable::timed wait: mutex not locked");
190 nanoseconds __d = __tp.time_since_epoch();
191 timespec __ts;
192 seconds __s = duration_cast<seconds>(fd: __d);
193 using __ts_sec = decltype(__ts.tv_sec);
194 const __ts_sec __ts_sec_max = numeric_limits<__ts_sec>::max();
195 if (__s.count() < __ts_sec_max) {
196 __ts.tv_sec = static_cast<__ts_sec>(__s.count());
197 __ts.tv_nsec = (__d - __s).count();
198 } else {
199 __ts.tv_sec = __ts_sec_max;
200 __ts.tv_nsec = giga::num - 1;
201 }
202 int __ec = pthread_cond_clockwait(cond: &__cv_, mutex: __lk.mutex()->native_handle(), CLOCK_MONOTONIC, abstime: &__ts);
203 if (__ec != 0 && __ec != ETIMEDOUT)
204 std::__throw_system_error(ev: __ec, what_arg: "condition_variable timed_wait failed");
205}
206# endif // _LIBCPP_HAS_COND_CLOCKWAIT
207
208template <class _Clock>
209inline void condition_variable::__do_timed_wait(unique_lock<mutex>& __lk,
210 chrono::time_point<_Clock, chrono::nanoseconds> __tp) _NOEXCEPT {
211 using namespace chrono;
212 nanoseconds __d = __tp - _Clock::now();
213 if (__d <= __d.zero())
214 return;
215 using __ns_rep = nanoseconds::rep;
216
217# if _LIBCPP_HAS_COND_CLOCKWAIT
218 steady_clock::time_point __c_now = steady_clock::now();
219 using __clock_tp_ns = time_point<steady_clock, nanoseconds>;
220 __ns_rep __now_count_ns = std::__safe_nanosecond_cast(d: __c_now.time_since_epoch()).count();
221# else
222 using __clock_tp_ns = time_point<system_clock, nanoseconds>;
223 __ns_rep __now_count_ns = std::__safe_nanosecond_cast(system_clock::now().time_since_epoch()).count();
224# endif
225
226 __ns_rep __d_ns_count = std::__safe_nanosecond_cast(__d).count();
227
228 if (__now_count_ns > numeric_limits<__ns_rep>::max() - __d_ns_count) {
229 __do_timed_wait(__lk, tp: __clock_tp_ns::max());
230 } else {
231 __do_timed_wait(__lk, tp: __clock_tp_ns(nanoseconds(__now_count_ns + __d_ns_count)));
232 }
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