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