[glib] gmain: rework g_get_monotonic_time() a bit



commit d61431254636b9ecc250884eb34757c2504e5685
Author: Ryan Lortie <desrt desrt ca>
Date:   Tue Feb 18 14:09:49 2014 -0500

    gmain: rework g_get_monotonic_time() a bit
    
    We now assume the existence of clock_gettime() and CLOCK_MONOTONIC as
    specified by POSIX.1-2001.  This means that we always return truly
    monotonic time, which will prevent problems in the case that the user
    changes the time.
    
    Mac OS doesn't have clock_gettime() but it does have
    mach_absolute_time(), so we can use that there.
    
    We keep our Windows case as well (although we should simplify it once XP
    hits EOL later this year).
    
    This patch removes the fallback to gettimeofday() in case of missing
    clock_gettime().  We no longer have any way to test this codepath and
    therefore it must go.
    
    This patch also restructures the #ifdef a bit so that we repeat the
    entire function definition inside of #ifdef instead of just the entire
    body of one function.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=724687

 configure.ac |    3 +-
 glib/gmain.c |  164 +++++++++++++++++++++++++++++++---------------------------
 2 files changed, 88 insertions(+), 79 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index dd80ea1..29c9f20 100644
--- a/configure.ac
+++ b/configure.ac
@@ -771,7 +771,7 @@ fi
 
 
 # check for header files
-AC_CHECK_HEADERS([sys/param.h sys/resource.h])
+AC_CHECK_HEADERS([sys/param.h sys/resource.h mach/mach_time.h])
 AC_CHECK_HEADERS([sys/select.h stdint.h inttypes.h sched.h malloc.h])
 AC_CHECK_HEADERS([sys/vfs.h sys/vmount.h sys/statfs.h sys/statvfs.h sys/filio.h])
 AC_CHECK_HEADERS([mntent.h sys/mnttab.h sys/vfstab.h sys/mntctl.h fstab.h])
@@ -2296,7 +2296,6 @@ AC_SUBST(G_THREAD_LIBS_EXTRA)
 
 AC_CHECK_FUNCS(clock_gettime, [], [
   AC_CHECK_LIB(rt, clock_gettime, [
-    AC_DEFINE(HAVE_CLOCK_GETTIME, 1)
     G_THREAD_LIBS="$G_THREAD_LIBS -lrt"
     G_THREAD_LIBS_FOR_GTHREAD="$G_THREAD_LIBS_FOR_GTHREAD -lrt"
   ])
diff --git a/glib/gmain.c b/glib/gmain.c
index f2d6178..c65d24f 100644
--- a/glib/gmain.c
+++ b/glib/gmain.c
@@ -71,6 +71,10 @@
 #include <windows.h>
 #endif /* G_OS_WIN32 */
 
+#ifdef HAVE_MACH_MACH_TIME_H
+#include <mach/mach_time.h>
+#endif
+
 #include "glib_trace.h"
 
 #include "gmain.h"
@@ -2510,7 +2514,25 @@ g_get_real_time (void)
   return (((gint64) tv.tv_sec) * 1000000) + tv.tv_usec;
 }
 
-#ifdef G_OS_WIN32
+/**
+ * g_get_monotonic_time:
+ *
+ * Queries the system monotonic time.
+ *
+ * The monotonic clock will always increase and doesn't suffer
+ * discontinuities when the user (or NTP) changes the system time.  It
+ * may or may not continue to tick during times where the machine is
+ * suspended.
+ *
+ * We try to use the clock that corresponds as closely as possible to
+ * the passage of time as measured by system calls such as poll() but it
+ * may not always be possible to do this.
+ *
+ * Returns: the monotonic time, in microseconds
+ *
+ * Since: 2.28
+ **/
+#if defined (G_OS_WIN32)
 static ULONGLONG (*g_GetTickCount64) (void) = NULL;
 static guint32 g_win32_tick_epoch = 0;
 
@@ -2525,70 +2547,10 @@ g_clock_win32_init (void)
     g_GetTickCount64 = (void *) GetProcAddress (kernel32, "GetTickCount64");
   g_win32_tick_epoch = ((guint32)GetTickCount()) >> 31;
 }
-#endif
 
-/**
- * g_get_monotonic_time:
- *
- * Queries the system monotonic time, if available.
- *
- * On POSIX systems with clock_gettime() and `CLOCK_MONOTONIC` this call
- * is a very shallow wrapper for that.  Otherwise, we make a best effort
- * that probably involves returning the wall clock time (with at least
- * microsecond accuracy, subject to the limitations of the OS kernel).
- *
- * It's important to note that POSIX `CLOCK_MONOTONIC` does
- * not count time spent while the machine is suspended.
- *
- * On Windows, "limitations of the OS kernel" is a rather substantial
- * statement.  Depending on the configuration of the system, the wall
- * clock time is updated as infrequently as 64 times a second (which
- * is approximately every 16ms). Also, on XP (but not on Vista or later)
- * the monotonic clock is locally monotonic, but may differ in exact
- * value between processes due to timer wrap handling.
- *
- * Returns: the monotonic time, in microseconds
- *
- * Since: 2.28
- **/
 gint64
 g_get_monotonic_time (void)
 {
-#ifdef HAVE_CLOCK_GETTIME
-  /* librt clock_gettime() is our first choice */
-  struct timespec ts;
-
-#ifdef CLOCK_MONOTONIC
-  clock_gettime (CLOCK_MONOTONIC, &ts);
-#else
-  clock_gettime (CLOCK_REALTIME, &ts);
-#endif
-
-  /* In theory monotonic time can have any epoch.
-   *
-   * glib presently assumes the following:
-   *
-   *   1) The epoch comes some time after the birth of Jesus of Nazareth, but
-   *      not more than 10000 years later.
-   *
-   *   2) The current time also falls sometime within this range.
-   *
-   * These two reasonable assumptions leave us with a maximum deviation from
-   * the epoch of 10000 years, or 315569520000000000 seconds.
-   *
-   * If we restrict ourselves to this range then the number of microseconds
-   * will always fit well inside the constraints of a int64 (by a factor of
-   * about 29).
-   *
-   * If you actually hit the following assertion, probably you should file a
-   * bug against your operating system for being excessively silly.
-   **/
-  g_assert (G_GINT64_CONSTANT (-315569520000000000) < ts.tv_sec &&
-            ts.tv_sec < G_GINT64_CONSTANT (315569520000000000));
-
-  return (((gint64) ts.tv_sec) * 1000000) + (ts.tv_nsec / 1000);
-
-#elif defined (G_OS_WIN32)
   guint64 ticks;
   guint32 ticks32;
 
@@ -2622,7 +2584,7 @@ g_get_monotonic_time (void)
    * is to use it to interpolate the lower precision clocks. Firefox does something like
    * this:
    *   https://bugzilla.mozilla.org/show_bug.cgi?id=363258
-   * 
+   *
    * However this seems quite complicated, so we're not doing this right now.
    *
    * The approach we take instead is to use the TGT timer, extending it to 64bit
@@ -2633,7 +2595,7 @@ g_get_monotonic_time (void)
    * This means that:
    *  - We have a globally consistent monotonic clock on Vista and later
    *  - We have a locally monotonic clock on XP
-   *  - Apps that need higher precision in timeouts and clock reads can call 
+   *  - Apps that need higher precision in timeouts and clock reads can call
    *    timeBeginPeriod() to increase it as much as they want
    */
 
@@ -2644,10 +2606,10 @@ g_get_monotonic_time (void)
       ticks = g_GetTickCount64 ();
       ticks32 = timeGetTime();
 
-      /* GTC64 and TGT are sampled at different times, however they 
-       * have the same base and source (msecs since system boot). 
+      /* GTC64 and TGT are sampled at different times, however they
+       * have the same base and source (msecs since system boot).
        * They can differ by as much as -16 to +16 msecs.
-       * We can't just inject the low bits into the 64bit counter 
+       * We can't just inject the low bits into the 64bit counter
        * as one of the counters can have wrapped in 32bit space and
        * the other not. Instead we calculate the signed difference
        * in 32bit space and apply that difference to the 64bit counter.
@@ -2656,9 +2618,9 @@ g_get_monotonic_time (void)
 
       /* We could do some 2's complement hack, but we play it safe */
       if (ticks32 - ticks_as_32bit <= G_MAXINT32)
-       ticks += ticks32 - ticks_as_32bit;
+        ticks += ticks32 - ticks_as_32bit;
       else
-       ticks -= ticks_as_32bit - ticks32;
+        ticks -= ticks_as_32bit - ticks32;
     }
   else
     {
@@ -2686,26 +2648,74 @@ g_get_monotonic_time (void)
        * processes.
        */
       if ((ticks32 >> 31) != (epoch & 1))
-       {
-         epoch++;
-         g_atomic_int_set (&g_win32_tick_epoch, epoch);
-       }
+        {
+          epoch++;
+          g_atomic_int_set (&g_win32_tick_epoch, epoch);
+        }
 
 
       ticks = (guint64)ticks32 | ((guint64)epoch) << 31;
     }
 
   return ticks * 1000;
+}
+#elif defined(HAVE_MACH_MACH_TIME_H) /* Mac OS */
+gint64
+g_get_monotonic_time (void)
+{
+  static mach_timebase_info_data_t timebase_info;
 
-#else /* !HAVE_CLOCK_GETTIME && ! G_OS_WIN32*/
+  if (timebase_info.denom == 0)
+    {
+      /* This is a fraction that we must use to scale
+       * mach_absolute_time() by in order to reach nanoseconds.
+       *
+       * We've only ever observed this to be 1/1, but maybe it could be
+       * 1000/1 if mach time is microseconds already, or 1/1000 if
+       * picoseconds.  Try to deal nicely with that.
+       */
+      mach_timebase_info (&timebase_info);
 
-  GTimeVal tv;
+      /* We actually want microseconds... */
+      if (timebase_info.numer % 1000 == 0)
+        timebase_info.numer /= 1000;
+      else
+        timebase_info.denom *= 1000;
 
-  g_get_current_time (&tv);
+      /* We want to make the numer 1 to avoid having to multiply... */
+      if (timebase_info.denom % timebase_info.numer == 0)
+        {
+          timebase_info.denom /= timebase_info.numer;
+          timebase_info.numer = 1;
+        }
+      else
+        {
+          /* We could just multiply by timebase_info.numer below, but why
+           * bother for a case that may never actually exist...
+           *
+           * Plus -- performing the multiplication would risk integer
+           * overflow.  If we ever actually end up in this situation, we
+           * should more carefully evaluate the correct course of action.
+           */
+          mach_timebase_info (&timebase_info); /* Get a fresh copy for a better message */
+          g_error ("Got weird mach timebase info of %d/%d.  Please file a bug against GLib.",
+                   timebase_info.numer, timebase_info.denom);
+        }
+    }
 
-  return (((gint64) tv.tv_sec) * 1000000) + tv.tv_usec;
-#endif
+  return mach_absolute_time () / timebase_info.denom;
+}
+#else
+gint64
+g_get_monotonic_time (void)
+{
+  struct timespec ts;
+
+  clock_gettime (CLOCK_MONOTONIC, &ts);
+
+  return (((gint64) ts.tv_sec) * 1000000) + (ts.tv_nsec / 1000);
 }
+#endif
 
 static void
 g_main_dispatch_free (gpointer dispatch)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]