If __gthread_cond_timedwaitonclock is available it can be used it to fix part of https://gcc.gnu.org/bugzilla/show_bug.cgi?id=41861 by supporting std::chrono::steady_clock properly with std::condition_variable.
This means that code using std::condition_variable::wait_for or std::condition_variable::wait_until with std::chrono::steady_clock is no longer subject to timing out early or potentially waiting for much longer if the system clock is changed at an inopportune moment. If __gthread_cond_timedwaitonclock is available then std::chrono::steady_clock is deemed to be the "best" clock available which means that it is used for the relative wait_for calls and absolute wait_until calls that aren't choosing to use std::chrono::system_clock. Calls explicitly using std::chrono::system_clock continue to use CLOCK_REALTIME. If __gthread_cond_timedwaitonclock is not available then std::chrono::system_clock is deemed to be the "best" clock available which means that the previous suboptimal behaviour remains. Signed-off-by: Mike Crowe <m...@mcrowe.com> --- libstdc++-v3/include/std/condition_variable | 56 ++++++++++++++++++---- .../30_threads/condition_variable/members/2.cc | 8 ++-- 2 files changed, 53 insertions(+), 11 deletions(-) diff --git a/libstdc++-v3/include/std/condition_variable b/libstdc++-v3/include/std/condition_variable index f7da017..625ecfe 100644 --- a/libstdc++-v3/include/std/condition_variable +++ b/libstdc++-v3/include/std/condition_variable @@ -63,7 +63,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// condition_variable class condition_variable { - typedef chrono::system_clock __clock_t; +#ifdef _GTHREAD_USE_COND_TIMEDWAITONCLOCK + typedef chrono::steady_clock __steady_clock_t; + typedef chrono::steady_clock __best_clock_t; +#else + typedef chrono::system_clock __best_clock_t; +#endif + typedef chrono::system_clock __system_clock_t; typedef __gthread_cond_t __native_type; #ifdef __GTHREAD_COND_INIT @@ -98,10 +104,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION wait(__lock); } +#ifdef _GTHREAD_USE_COND_TIMEDWAITONCLOCK template<typename _Duration> cv_status wait_until(unique_lock<mutex>& __lock, - const chrono::time_point<__clock_t, _Duration>& __atime) + const chrono::time_point<__steady_clock_t, _Duration>& __atime) + { return __wait_until_impl(__lock, __atime); } +#endif + + template<typename _Duration> + cv_status + wait_until(unique_lock<mutex>& __lock, + const chrono::time_point<__system_clock_t, _Duration>& __atime) { return __wait_until_impl(__lock, __atime); } template<typename _Clock, typename _Duration> @@ -109,9 +123,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION wait_until(unique_lock<mutex>& __lock, const chrono::time_point<_Clock, _Duration>& __atime) { - // DR 887 - Sync unknown clock to known clock. const typename _Clock::time_point __c_entry = _Clock::now(); - const __clock_t::time_point __s_entry = __clock_t::now(); + const __best_clock_t::time_point __s_entry = __best_clock_t::now(); const auto __delta = __atime - __c_entry; const auto __s_atime = __s_entry + __delta; @@ -134,24 +147,47 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION cv_status wait_for(unique_lock<mutex>& __lock, const chrono::duration<_Rep, _Period>& __rtime) - { return wait_until(__lock, __clock_t::now() + __rtime); } + { return wait_until(__lock, __best_clock_t::now() + __rtime); } template<typename _Rep, typename _Period, typename _Predicate> bool wait_for(unique_lock<mutex>& __lock, const chrono::duration<_Rep, _Period>& __rtime, _Predicate __p) - { return wait_until(__lock, __clock_t::now() + __rtime, std::move(__p)); } + { return wait_until(__lock, __best_clock_t::now() + __rtime, std::move(__p)); } native_handle_type native_handle() { return &_M_cond; } private: +#ifdef _GTHREAD_USE_COND_TIMEDWAITONCLOCK + template<typename _Dur> + cv_status + __wait_until_impl(unique_lock<mutex>& __lock, + const chrono::time_point<__steady_clock_t, _Dur>& __atime) + { + auto __s = chrono::time_point_cast<chrono::seconds>(__atime); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); + + __gthread_time_t __ts = + { + static_cast<std::time_t>(__s.time_since_epoch().count()), + static_cast<long>(__ns.count()) + }; + + __gthread_cond_timedwaitonclock(&_M_cond, __lock.mutex()->native_handle(), + __GTHREAD_CLOCK_MONOTONIC, + &__ts); + + return (__steady_clock_t::now() < __atime + ? cv_status::no_timeout : cv_status::timeout); + } +#endif template<typename _Dur> cv_status __wait_until_impl(unique_lock<mutex>& __lock, - const chrono::time_point<__clock_t, _Dur>& __atime) + const chrono::time_point<__system_clock_t, _Dur>& __atime) { auto __s = chrono::time_point_cast<chrono::seconds>(__atime); auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); @@ -165,7 +201,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __gthread_cond_timedwait(&_M_cond, __lock.mutex()->native_handle(), &__ts); - return (__clock_t::now() < __atime + return (__system_clock_t::now() < __atime ? cv_status::no_timeout : cv_status::timeout); } }; @@ -185,7 +221,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // Like above, but mutex is not required to have try_lock. class condition_variable_any { +#ifdef _GTHREAD_USE_COND_TIMEDWAITONCLOCK + typedef chrono::steady_clock __clock_t; +#else typedef chrono::system_clock __clock_t; +#endif condition_variable _M_cond; shared_ptr<mutex> _M_mutex; diff --git a/libstdc++-v3/testsuite/30_threads/condition_variable/members/2.cc b/libstdc++-v3/testsuite/30_threads/condition_variable/members/2.cc index 04945f1..79d511e 100644 --- a/libstdc++-v3/testsuite/30_threads/condition_variable/members/2.cc +++ b/libstdc++-v3/testsuite/30_threads/condition_variable/members/2.cc @@ -27,6 +27,7 @@ #include <system_error> #include <testsuite_hooks.h> +template <typename CLOCK> void test01() { bool test __attribute__((unused)) = true; @@ -38,10 +39,10 @@ void test01() std::mutex m; std::unique_lock<std::mutex> l(m); - auto then = std::chrono::steady_clock::now(); + auto then = CLOCK::now(); std::cv_status result = c1.wait_until(l, then + ms); VERIFY( result == std::cv_status::timeout ); - VERIFY( (std::chrono::steady_clock::now() - then) >= ms ); + VERIFY( (CLOCK::now() - then) >= ms ); VERIFY( l.owns_lock() ); } catch (const std::system_error& e) @@ -56,6 +57,7 @@ void test01() int main() { - test01(); + test01<std::chrono::steady_clock>(); + test01<std::chrono::system_clock>(); return 0; } -- 2.1.4