Index: include/__config =================================================================== --- include/__config +++ include/__config @@ -1082,6 +1082,12 @@ # endif // _LIBCPP_HAS_THREAD_API #endif // _LIBCPP_HAS_NO_THREADS +#if defined(_LIBCPP_HAS_THREAD_API_PTHREAD) +# if (defined(__ANDROID__) && __ANDROID_API__ >= 30) || _LIBCPP_GLIBC_PREREQ(2, 30) +# define _LIBCPP_HAS_COND_CLOCKWAIT +# endif +#endif + #if defined(_LIBCPP_HAS_NO_THREADS) && defined(_LIBCPP_HAS_THREAD_API_PTHREAD) #error _LIBCPP_HAS_THREAD_API_PTHREAD may only be defined when \ _LIBCPP_HAS_NO_THREADS is not defined. Index: include/__mutex_base =================================================================== --- include/__mutex_base +++ include/__mutex_base @@ -15,6 +15,10 @@ #include #include <__threading_support> +#if defined(_LIBCPP_HAS_COND_CLOCKWAIT) +#include +#endif + #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) #pragma GCC system_header @@ -304,6 +308,20 @@ _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS void wait(unique_lock& __lk, _Predicate __pred); + template + _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS + cv_status + wait_until(unique_lock& __lk, + const chrono::time_point& __t); + +#if defined(_LIBCPP_HAS_COND_CLOCKWAIT) + template + _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS + cv_status + wait_until(unique_lock& __lk, + const chrono::time_point& __t); +#endif + template _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS cv_status @@ -336,6 +354,10 @@ private: void __do_timed_wait(unique_lock& __lk, chrono::time_point) _NOEXCEPT; +#if defined(_LIBCPP_HAS_COND_CLOCKWAIT) + void __do_timed_wait(unique_lock& __lk, + chrono::time_point) _NOEXCEPT; +#endif }; #endif // !_LIBCPP_HAS_NO_THREADS @@ -364,6 +386,58 @@ wait(__lk); } +template +cv_status +condition_variable::wait_until(unique_lock& __lk, + const chrono::time_point& __t) +{ + using namespace chrono; + typedef time_point > __sys_tpf; + typedef time_point __sys_tpi; + __sys_tpf _Max = __sys_tpi::max(); + + if (__sys_tpf(__t) > _Max) { + __do_timed_wait(__lk, __sys_tpi::max()); + return cv_status::no_timeout; + } + + system_clock::time_point __now = system_clock::now(); + if (__t < __now) + return cv_status::timeout; + + _Duration __d = __t.time_since_epoch(); + __do_timed_wait(__lk, __sys_tpi(__ceil(__d))); + + return system_clock::now() < __t ? cv_status::no_timeout : cv_status::timeout; +} + +#if defined(_LIBCPP_HAS_COND_CLOCKWAIT) +template +cv_status +condition_variable::wait_until(unique_lock& __lk, + const chrono::time_point& __t) +{ + using namespace chrono; + typedef time_point > __std_tpf; + typedef time_point __std_tpi; + __std_tpf _Max = __std_tpi::max(); + + if (__std_tpf(__t) > _Max) { + __do_timed_wait(__lk, __std_tpi::max()); + return cv_status::no_timeout; + } + + steady_clock::time_point __now = steady_clock::now(); + if (__t < __now) + return cv_status::timeout; + + _Duration __d = __t.time_since_epoch(); + __do_timed_wait(__lk, __std_tpi(__ceil(__d))); + + return steady_clock::now() < __t ? cv_status::no_timeout : cv_status::timeout; +} +#endif + template cv_status condition_variable::wait_until(unique_lock& __lk, @@ -396,15 +470,25 @@ using namespace chrono; if (__d <= __d.zero()) return cv_status::timeout; + steady_clock::time_point __c_now = steady_clock::now(); +#if defined(_LIBCPP_HAS_COND_CLOCKWAIT) + typedef time_point > __c_tpf; + typedef time_point __c_tpi; + __c_tpf _Max = __c_tpi::max(); + if (_Max - __d > __c_now) + __do_timed_wait(__lk, __c_now + __ceil(__d)); + else + __do_timed_wait(__lk, __c_tpi::max()); +#else typedef time_point > __sys_tpf; typedef time_point __sys_tpi; __sys_tpf _Max = __sys_tpi::max(); - steady_clock::time_point __c_now = steady_clock::now(); system_clock::time_point __s_now = system_clock::now(); if (_Max - __d > __s_now) __do_timed_wait(__lk, __s_now + __ceil(__d)); else __do_timed_wait(__lk, __sys_tpi::max()); +#endif return steady_clock::now() - __c_now < __d ? cv_status::no_timeout : cv_status::timeout; } @@ -420,6 +504,39 @@ _VSTD::move(__pred)); } +#if defined(_LIBCPP_HAS_COND_CLOCKWAIT) +inline +void +condition_variable::__do_timed_wait(unique_lock& lk, + chrono::time_point tp) _NOEXCEPT +{ + using namespace chrono; + if (!lk.owns_lock()) + __throw_system_error(EPERM, + "condition_variable::timed wait: mutex not locked"); + nanoseconds d = tp.time_since_epoch(); + if (d > nanoseconds(0x59682F000000E941)) + d = nanoseconds(0x59682F000000E941); + timespec ts; + seconds s = duration_cast(d); + typedef decltype(ts.tv_sec) ts_sec; + _LIBCPP_CONSTEXPR ts_sec ts_sec_max = numeric_limits::max(); + if (s.count() < ts_sec_max) + { + ts.tv_sec = static_cast(s.count()); + ts.tv_nsec = static_cast((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) + __throw_system_error(ec, "condition_variable timed_wait failed"); +} +#endif // _LIBCPP_HAS_COND_CLOCKWAIT + #endif // !_LIBCPP_HAS_NO_THREADS _LIBCPP_END_NAMESPACE_STD Index: test/std/thread/thread.condition/thread.condition.condvar/wait_until.pass.cpp =================================================================== --- test/std/thread/thread.condition/thread.condition.condvar/wait_until.pass.cpp +++ test/std/thread/thread.condition/thread.condition.condvar/wait_until.pass.cpp @@ -25,12 +25,12 @@ #include "test_macros.h" -struct Clock +struct TestClock { typedef std::chrono::milliseconds duration; typedef duration::rep rep; typedef duration::period period; - typedef std::chrono::time_point time_point; + typedef std::chrono::time_point time_point; static const bool is_steady = true; static time_point now() @@ -50,35 +50,40 @@ int runs = 0; +template void f() { std::unique_lock lk(mut); assert(test2 == 0); test1 = 1; cv.notify_one(); - Clock::time_point t0 = Clock::now(); - Clock::time_point t = t0 + Clock::duration(250); + typename Clock::time_point t0 = Clock::now(); + typename Clock::time_point t = t0 + std::chrono::milliseconds(250); while (test2 == 0 && cv.wait_until(lk, t) == std::cv_status::no_timeout) ; - Clock::time_point t1 = Clock::now(); + typename Clock::time_point t1 = Clock::now(); if (runs == 0) { - assert(t1 - t0 < Clock::duration(250)); + assert(t1 - t0 < std::chrono::milliseconds(250)); assert(test2 != 0); } else { - assert(t1 - t0 - Clock::duration(250) < Clock::duration(50)); + assert(t1 - t0 - std::chrono::milliseconds(250) < std::chrono::milliseconds(50)); assert(test2 == 0); } ++runs; } -int main(int, char**) +template +void run_test() { + runs = 0; + test1 = 0; + test2 = 0; { std::unique_locklk(mut); - std::thread t(f); + std::thread t(f); assert(test1 == 0); while (test1 == 0) cv.wait(lk); @@ -92,7 +97,7 @@ test2 = 0; { std::unique_locklk(mut); - std::thread t(f); + std::thread t(f); assert(test1 == 0); while (test1 == 0) cv.wait(lk); @@ -103,3 +108,10 @@ return 0; } + +int main(int, char**) +{ + run_test(); + run_test(); + run_test(); +}