diff mbox

[RFC,2/2] Add steady_clock support to condition_variable

Message ID 1436187340-12164-2-git-send-email-mac@mcrowe.com
State New
Headers show

Commit Message

Mike Crowe July 6, 2015, 12:55 p.m. UTC
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 <mac@mcrowe.com>
---
 libstdc++-v3/include/std/condition_variable        | 56 ++++++++++++++++++----
 .../30_threads/condition_variable/members/2.cc     |  8 ++--
 2 files changed, 53 insertions(+), 11 deletions(-)

Comments

Jonathan Wakely July 6, 2015, 1:54 p.m. UTC | #1
On 06/07/15 13:55 +0100, Mike Crowe wrote:
>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.

From a quick glance this looks like a good change. As I said in my
other mail about the gthr-posix.h changes, it might be better to keep
this change to libstdc++ rather than splitting it across libstdc++ and
libgcc's gthr-posix.h

Do you have a copyright assignment in place for GCC contributions?
Mike Crowe July 6, 2015, 2:18 p.m. UTC | #2
On Monday 06 July 2015 at 14:54:06 +0100, Jonathan Wakely wrote:
> Do you have a copyright assignment in place for GCC contributions?

Not yet. From my reading of https://gcc.gnu.org/contribute.html I should
probably ask you for the forms. :-)

Mike.
Jonathan Wakely July 6, 2015, 2:38 p.m. UTC | #3
On 06/07/15 15:18 +0100, Mike Crowe wrote:
>On Monday 06 July 2015 at 14:54:06 +0100, Jonathan Wakely wrote:
>> Do you have a copyright assignment in place for GCC contributions?
>
>Not yet. From my reading of https://gcc.gnu.org/contribute.html I should
>probably ask you for the forms. :-)

Yep :-) I'll send them offlist.
diff mbox

Patch

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;
 }