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