This has the benefit of consistent placement for these macros (outside the namespace, not inside it). This, in turn, makes it more obvious that the condition applies to the entire namespace scope. Compile times are also reduced a bit, since the compiler doesn't have to open a namespace just to close it again. This is enforced with a clang-tidy check. As a drive-by this also fixes a few macro comments.
234 lines
8.1 KiB
C++
234 lines
8.1 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef _LIBCPP___CONDITION_VARIABLE_CONDITION_VARIABLE_H
|
|
#define _LIBCPP___CONDITION_VARIABLE_CONDITION_VARIABLE_H
|
|
|
|
#include <__chrono/duration.h>
|
|
#include <__chrono/steady_clock.h>
|
|
#include <__chrono/system_clock.h>
|
|
#include <__chrono/time_point.h>
|
|
#include <__config>
|
|
#include <__mutex/mutex.h>
|
|
#include <__mutex/unique_lock.h>
|
|
#include <__system_error/throw_system_error.h>
|
|
#include <__thread/support.h>
|
|
#include <__type_traits/enable_if.h>
|
|
#include <__type_traits/is_floating_point.h>
|
|
#include <__utility/move.h>
|
|
#include <limits>
|
|
#include <ratio>
|
|
|
|
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
|
# pragma GCC system_header
|
|
#endif
|
|
|
|
_LIBCPP_PUSH_MACROS
|
|
#include <__undef_macros>
|
|
|
|
#if _LIBCPP_HAS_THREADS
|
|
|
|
_LIBCPP_BEGIN_NAMESPACE_STD
|
|
_LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
|
|
|
|
// enum class cv_status
|
|
_LIBCPP_DECLARE_STRONG_ENUM(cv_status){no_timeout, timeout};
|
|
_LIBCPP_DECLARE_STRONG_ENUM_EPILOG(cv_status)
|
|
|
|
template <class _Rep, class _Period, __enable_if_t<is_floating_point<_Rep>::value, int> = 0>
|
|
inline _LIBCPP_HIDE_FROM_ABI chrono::nanoseconds __safe_nanosecond_cast(chrono::duration<_Rep, _Period> __d) {
|
|
using namespace chrono;
|
|
using __ratio = ratio_divide<_Period, nano>;
|
|
using __ns_rep = nanoseconds::rep;
|
|
_Rep __result_float = __d.count() * __ratio::num / __ratio::den;
|
|
|
|
_Rep __result_max = numeric_limits<__ns_rep>::max();
|
|
if (__result_float >= __result_max) {
|
|
return nanoseconds::max();
|
|
}
|
|
|
|
_Rep __result_min = numeric_limits<__ns_rep>::min();
|
|
if (__result_float <= __result_min) {
|
|
return nanoseconds::min();
|
|
}
|
|
|
|
return nanoseconds(static_cast<__ns_rep>(__result_float));
|
|
}
|
|
|
|
template <class _Rep, class _Period, __enable_if_t<!is_floating_point<_Rep>::value, int> = 0>
|
|
inline _LIBCPP_HIDE_FROM_ABI chrono::nanoseconds __safe_nanosecond_cast(chrono::duration<_Rep, _Period> __d) {
|
|
using namespace chrono;
|
|
if (__d.count() == 0) {
|
|
return nanoseconds(0);
|
|
}
|
|
|
|
using __ratio = ratio_divide<_Period, nano>;
|
|
using __ns_rep = nanoseconds::rep;
|
|
__ns_rep __result_max = numeric_limits<__ns_rep>::max();
|
|
if (__d.count() > 0 && __d.count() > __result_max / __ratio::num) {
|
|
return nanoseconds::max();
|
|
}
|
|
|
|
__ns_rep __result_min = numeric_limits<__ns_rep>::min();
|
|
if (__d.count() < 0 && __d.count() < __result_min / __ratio::num) {
|
|
return nanoseconds::min();
|
|
}
|
|
|
|
__ns_rep __result = __d.count() * __ratio::num / __ratio::den;
|
|
if (__result == 0) {
|
|
return nanoseconds(1);
|
|
}
|
|
|
|
return nanoseconds(__result);
|
|
}
|
|
|
|
class _LIBCPP_EXPORTED_FROM_ABI condition_variable {
|
|
__libcpp_condvar_t __cv_ = _LIBCPP_CONDVAR_INITIALIZER;
|
|
|
|
public:
|
|
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR condition_variable() _NOEXCEPT = default;
|
|
|
|
# if _LIBCPP_HAS_TRIVIAL_CONDVAR_DESTRUCTION
|
|
~condition_variable() = default;
|
|
# else
|
|
~condition_variable();
|
|
# endif
|
|
|
|
condition_variable(const condition_variable&) = delete;
|
|
condition_variable& operator=(const condition_variable&) = delete;
|
|
|
|
void notify_one() _NOEXCEPT;
|
|
void notify_all() _NOEXCEPT;
|
|
|
|
void wait(unique_lock<mutex>& __lk) _NOEXCEPT;
|
|
|
|
template <class _Predicate>
|
|
_LIBCPP_HIDE_FROM_ABI void wait(unique_lock<mutex>& __lk, _Predicate __pred) {
|
|
while (!__pred())
|
|
wait(__lk);
|
|
}
|
|
|
|
template <class _Clock, class _Duration>
|
|
_LIBCPP_HIDE_FROM_ABI cv_status
|
|
wait_until(unique_lock<mutex>& __lk, const chrono::time_point<_Clock, _Duration>& __t) {
|
|
using namespace chrono;
|
|
using __clock_tp_ns = time_point<_Clock, nanoseconds>;
|
|
|
|
typename _Clock::time_point __now = _Clock::now();
|
|
if (__t <= __now)
|
|
return cv_status::timeout;
|
|
|
|
__clock_tp_ns __t_ns = __clock_tp_ns(std::__safe_nanosecond_cast(__t.time_since_epoch()));
|
|
|
|
__do_timed_wait(__lk, __t_ns);
|
|
return _Clock::now() < __t ? cv_status::no_timeout : cv_status::timeout;
|
|
}
|
|
|
|
template <class _Clock, class _Duration, class _Predicate>
|
|
_LIBCPP_HIDE_FROM_ABI bool
|
|
wait_until(unique_lock<mutex>& __lk, const chrono::time_point<_Clock, _Duration>& __t, _Predicate __pred) {
|
|
while (!__pred()) {
|
|
if (wait_until(__lk, __t) == cv_status::timeout)
|
|
return __pred();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template <class _Rep, class _Period>
|
|
_LIBCPP_HIDE_FROM_ABI cv_status wait_for(unique_lock<mutex>& __lk, const chrono::duration<_Rep, _Period>& __d) {
|
|
using namespace chrono;
|
|
if (__d <= __d.zero())
|
|
return cv_status::timeout;
|
|
using __ns_rep = nanoseconds::rep;
|
|
steady_clock::time_point __c_now = steady_clock::now();
|
|
|
|
# if _LIBCPP_HAS_COND_CLOCKWAIT
|
|
using __clock_tp_ns = time_point<steady_clock, nanoseconds>;
|
|
__ns_rep __now_count_ns = std::__safe_nanosecond_cast(__c_now.time_since_epoch()).count();
|
|
# else
|
|
using __clock_tp_ns = time_point<system_clock, nanoseconds>;
|
|
__ns_rep __now_count_ns = std::__safe_nanosecond_cast(system_clock::now().time_since_epoch()).count();
|
|
# endif
|
|
|
|
__ns_rep __d_ns_count = std::__safe_nanosecond_cast(__d).count();
|
|
|
|
if (__now_count_ns > numeric_limits<__ns_rep>::max() - __d_ns_count) {
|
|
__do_timed_wait(__lk, __clock_tp_ns::max());
|
|
} else {
|
|
__do_timed_wait(__lk, __clock_tp_ns(nanoseconds(__now_count_ns + __d_ns_count)));
|
|
}
|
|
|
|
return steady_clock::now() - __c_now < __d ? cv_status::no_timeout : cv_status::timeout;
|
|
}
|
|
|
|
template <class _Rep, class _Period, class _Predicate>
|
|
bool _LIBCPP_HIDE_FROM_ABI
|
|
wait_for(unique_lock<mutex>& __lk, const chrono::duration<_Rep, _Period>& __d, _Predicate __pred);
|
|
|
|
typedef __libcpp_condvar_t* native_handle_type;
|
|
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI native_handle_type native_handle() { return &__cv_; }
|
|
|
|
private:
|
|
void
|
|
__do_timed_wait(unique_lock<mutex>& __lk, chrono::time_point<chrono::system_clock, chrono::nanoseconds>) _NOEXCEPT;
|
|
# if _LIBCPP_HAS_COND_CLOCKWAIT
|
|
_LIBCPP_HIDE_FROM_ABI void
|
|
__do_timed_wait(unique_lock<mutex>& __lk, chrono::time_point<chrono::steady_clock, chrono::nanoseconds>) _NOEXCEPT;
|
|
# endif
|
|
template <class _Clock>
|
|
_LIBCPP_HIDE_FROM_ABI void
|
|
__do_timed_wait(unique_lock<mutex>& __lk, chrono::time_point<_Clock, chrono::nanoseconds>) _NOEXCEPT;
|
|
};
|
|
#endif // _LIBCPP_HAS_THREADS
|
|
|
|
#if _LIBCPP_HAS_THREADS
|
|
|
|
template <class _Rep, class _Period, class _Predicate>
|
|
inline bool
|
|
condition_variable::wait_for(unique_lock<mutex>& __lk, const chrono::duration<_Rep, _Period>& __d, _Predicate __pred) {
|
|
return wait_until(__lk, chrono::steady_clock::now() + __d, std::move(__pred));
|
|
}
|
|
|
|
# if _LIBCPP_HAS_COND_CLOCKWAIT
|
|
inline void condition_variable::__do_timed_wait(
|
|
unique_lock<mutex>& __lk, chrono::time_point<chrono::steady_clock, chrono::nanoseconds> __tp) _NOEXCEPT {
|
|
using namespace chrono;
|
|
if (!__lk.owns_lock())
|
|
std::__throw_system_error(EPERM, "condition_variable::timed wait: mutex not locked");
|
|
nanoseconds __d = __tp.time_since_epoch();
|
|
timespec __ts;
|
|
seconds __s = duration_cast<seconds>(__d);
|
|
using __ts_sec = decltype(__ts.tv_sec);
|
|
const __ts_sec __ts_sec_max = numeric_limits<__ts_sec>::max();
|
|
if (__s.count() < __ts_sec_max) {
|
|
__ts.tv_sec = static_cast<__ts_sec>(__s.count());
|
|
__ts.tv_nsec = (__d - __s).count();
|
|
} else {
|
|
__ts.tv_sec = __ts_sec_max;
|
|
__ts.tv_nsec = giga::num - 1;
|
|
}
|
|
int __ec = pthread_cond_clockwait(&__cv_, __lk.mutex()->native_handle(), CLOCK_MONOTONIC, &__ts);
|
|
if (__ec != 0 && __ec != ETIMEDOUT)
|
|
std::__throw_system_error(__ec, "condition_variable timed_wait failed");
|
|
}
|
|
# endif // _LIBCPP_HAS_COND_CLOCKWAIT
|
|
|
|
template <class _Clock>
|
|
inline void condition_variable::__do_timed_wait(unique_lock<mutex>& __lk,
|
|
chrono::time_point<_Clock, chrono::nanoseconds> __tp) _NOEXCEPT {
|
|
wait_for(__lk, __tp - _Clock::now());
|
|
}
|
|
|
|
_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
|
|
_LIBCPP_END_NAMESPACE_STD
|
|
|
|
#endif // _LIBCPP_HAS_THREADS
|
|
|
|
_LIBCPP_POP_MACROS
|
|
|
|
#endif // _LIBCPP___CONDITION_VARIABLE_CONDITION_VARIABLE_H
|