Bug 18121 - Use a monotonic clock for pthread timeouts
authorColin Walters <walters@verbum.org>
Sat, 11 Jul 2009 02:27:55 +0000 (22:27 -0400)
committerColin Walters <walters@verbum.org>
Sat, 11 Jul 2009 02:57:08 +0000 (22:57 -0400)
Patch based on one from Keith Mok <ek9852@gmail.com>, some
followup work from Janne Karhunen <Janne.Karhunen@gmail.com>.

We don't want condition variable timeouts to be affected by the system clock.
Use the POSIX CLOCK_MONOTONIC if available.

configure.in
dbus/dbus-sysdeps-pthread.c

index 3af9cf7..bd21a8c 100644 (file)
@@ -696,8 +696,31 @@ if $dbus_use_libxml; then
 fi
 
 # Thread lib detection
-AC_CHECK_FUNC(pthread_cond_timedwait,,[AC_CHECK_LIB(pthread,pthread_cond_timedwait,
+AC_CHECK_FUNC(pthread_cond_timedwait,[AC_CHECK_LIB(pthread,pthread_cond_timedwait,
                                                     [THREAD_LIBS="-lpthread"])])
+save_libs="$LIBS"
+LIBS="$LIBS $THREAD_LIBS"
+AC_CHECK_FUNC(pthread_condattr_setclock,have_pthread_condattr_setclock=true,have_pthread_condattr_setclock=false)
+if test x$have_pthread_condattr_setclock = xtrue; then
+    AC_SEARCH_LIBS([clock_getres],[rt],[THREAD_LIBS="$THREAD_LIBS -lrt"])
+    AC_MSG_CHECKING([for CLOCK_MONOTONIC])
+    AC_TRY_COMPILE([#include <time.h>
+#include <pthread.h>
+], [
+struct timespec monotonic_timer;
+pthread_condattr_t attr;
+pthread_condattr_init (&attr);
+pthread_condattr_setclock (&attr, CLOCK_MONOTONIC);
+clock_getres (CLOCK_MONOTONIC,&monotonic_timer);
+], have_clock_monotonic=true, have_clock_monotonic=false)
+if test x$have_clock_monotonic = xtrue; then
+    AC_MSG_RESULT([found])
+    AC_DEFINE(HAVE_MONOTONIC_CLOCK, 1, [Define if we have CLOCK_MONOTONIC])
+else
+    AC_MSG_RESULT([not found])
+fi
+fi
+LIBS="$save_libs"
 
 # SELinux detection
 if test x$enable_selinux = xno ; then
index b7c3706..46e4204 100644 (file)
 #include <errno.h>
 #endif
 
+#include <config.h>
+
+/* Whether we have a "monotonic" clock; i.e. a clock not affected by
+ * changes in system time.
+ * This is initialized once in check_monotonic_clock below.
+ * https://bugs.freedesktop.org/show_bug.cgi?id=18121
+ */
+static dbus_bool_t have_monotonic_clock = 0;
+
 typedef struct {
   pthread_mutex_t lock; /**< lock protecting count field */
   volatile int count;   /**< count of how many times lock holder has recursively locked */
@@ -184,13 +193,21 @@ static DBusCondVar *
 _dbus_pthread_condvar_new (void)
 {
   DBusCondVarPThread *pcond;
+  pthread_condattr_t attr;
   int result;
   
   pcond = dbus_new (DBusCondVarPThread, 1);
   if (pcond == NULL)
     return NULL;
 
-  result = pthread_cond_init (&pcond->cond, NULL);
+  pthread_condattr_init (&attr);
+#ifdef HAVE_MONOTONIC_CLOCK
+  if (have_monotonic_clock)
+    pthread_condattr_setclock (&attr, CLOCK_MONOTONIC);
+#endif
+
+  result = pthread_cond_init (&pcond->cond, &attr);
+  pthread_condattr_destroy (&attr);
 
   if (result == EAGAIN || result == ENOMEM)
     {
@@ -248,7 +265,18 @@ _dbus_pthread_condvar_wait_timeout (DBusCondVar               *cond,
   
   _dbus_assert (pmutex->count > 0);
   _dbus_assert (pthread_equal (pmutex->holder, pthread_self ()));  
-  
+
+#ifdef HAVE_MONOTONIC_CLOCK
+  if (have_monotonic_clock)
+    {
+      struct timespec monotonic_timer;
+      clock_gettime (CLOCK_MONOTONIC,&monotonic_timer);
+      time_now.tv_sec = monotonic_timer.tv_sec;
+      time_now.tv_usec = monotonic_timer.tv_nsec / 1000;
+    }
+  else
+    /* This else falls through to gettimeofday */
+#endif
   gettimeofday (&time_now, NULL);
   
   end_time.tv_sec = time_now.tv_sec + timeout_milliseconds / 1000;
@@ -317,8 +345,19 @@ static const DBusThreadFunctions pthread_functions =
   _dbus_pthread_mutex_unlock
 };
 
+static void
+check_monotonic_clock (void)
+{
+#ifdef HAVE_MONOTONIC_CLOCK
+  struct timespec dummy;
+  if (clock_getres (CLOCK_MONOTONIC, &dummy) == 0)
+    have_monotonic_clock = TRUE;
+#endif
+}
+
 dbus_bool_t
 _dbus_threads_init_platform_specific (void)
 {
+  check_monotonic_clock ();
   return dbus_threads_init (&pthread_functions);
 }