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/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 | #ifndef _LIBCPP_HAS_NO_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 | class _LIBCPP_EXPORTED_FROM_ABI condition_variable { |
43 | __libcpp_condvar_t __cv_ = _LIBCPP_CONDVAR_INITIALIZER; |
44 | |
45 | public: |
46 | _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR condition_variable() _NOEXCEPT = default; |
47 | |
48 | # ifdef _LIBCPP_HAS_TRIVIAL_CONDVAR_DESTRUCTION |
49 | ~condition_variable() = default; |
50 | # else |
51 | ~condition_variable(); |
52 | # endif |
53 | |
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_status |
66 | 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 bool |
70 | 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_status |
74 | 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_ABI |
78 | 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 | |
83 | private: |
84 | void |
85 | __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 void |
88 | __do_timed_wait(unique_lock<mutex>& __lk, chrono::time_point<chrono::steady_clock, chrono::nanoseconds>) _NOEXCEPT; |
89 | # endif |
90 | template <class _Clock> |
91 | _LIBCPP_HIDE_FROM_ABI void |
92 | __do_timed_wait(unique_lock<mutex>& __lk, chrono::time_point<_Clock, chrono::nanoseconds>) _NOEXCEPT; |
93 | }; |
94 | #endif // !_LIBCPP_HAS_NO_THREADS |
95 | |
96 | template <class _Rep, class _Period, __enable_if_t<is_floating_point<_Rep>::value, int> = 0> |
97 | inline _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 | |
116 | template <class _Rep, class _Period, __enable_if_t<!is_floating_point<_Rep>::value, int> = 0> |
117 | inline _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_THREADS |
144 | template <class _Predicate> |
145 | void condition_variable::wait(unique_lock<mutex>& __lk, _Predicate __pred) { |
146 | while (!__pred()) |
147 | wait(__lk); |
148 | } |
149 | |
150 | template <class _Clock, class _Duration> |
151 | cv_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 | |
165 | template <class _Clock, class _Duration, class _Predicate> |
166 | bool 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 | |
175 | template <class _Rep, class _Period> |
176 | cv_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 | # else |
187 | 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 | # endif |
190 | |
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 | |
202 | template <class _Rep, class _Period, class _Predicate> |
203 | inline bool |
204 | condition_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) |
209 | inline 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_CLOCKWAIT |
231 | |
232 | template <class _Clock> |
233 | inline 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_THREADS |
239 | |
240 | _LIBCPP_END_NAMESPACE_STD |
241 | |
242 | _LIBCPP_POP_MACROS |
243 | |
244 | #endif // _LIBCPP___CONDITION_VARIABLE_CONDITION_VARIABLE_H |
245 | |