Don't die from assert() in timer_stop() if time goes backwards.
[darkstat] / now.c
diff --git a/now.c b/now.c
index 6e003c4..ab72efa 100644 (file)
--- a/now.c
+++ b/now.c
  */
 #include "err.h"
 #include "now.h"
+#include "str.h"
 
 #include <assert.h>
 #include <string.h>
 #include <time.h>
 
+#ifdef __MACH__
+/* Fake up clock_gettime() on OS X. */
+# include <sys/time.h>
+# include <inttypes.h>
+# include <mach/mach.h>
+# include <mach/mach_time.h>
+
+typedef int clockid_t;
+#define CLOCK_REALTIME 0
+#define CLOCK_MONOTONIC 1
+
+static uint64_t mono_first = 0;
+
+int clock_gettime(clockid_t clk_id, struct timespec *tp) {
+   if (clk_id == CLOCK_REALTIME) {
+      struct timeval tv;
+      gettimeofday(&tv, NULL);
+      tp->tv_sec = tv.tv_sec;
+      tp->tv_nsec = tv.tv_usec * 1000;
+      return 0;
+   }
+   if (clk_id == CLOCK_MONOTONIC) {
+      uint64_t t = mach_absolute_time();
+      mach_timebase_info_data_t timebase;
+      mach_timebase_info(&timebase);
+      if (!mono_first) {
+         mono_first = t;
+      }
+      uint64_t tdiff = (t - mono_first) * timebase.numer / timebase.denom;
+      tp->tv_sec = tdiff / 1000000000;
+      tp->tv_nsec = tdiff % 1000000000;
+      return 0;
+   }
+   return -1;
+}
+#endif  /* __MACH__ */
+
 static struct timespec clock_real, clock_mono;
 static int now_initialized = 0;
 
-long now_real(void) {
+time_t now_real(void) {
    assert(now_initialized);
    return clock_real.tv_sec;
 }
 
-long now_mono(void) {
+time_t now_mono(void) {
    assert(now_initialized);
    return clock_mono.tv_sec;
 }
@@ -43,6 +81,17 @@ static int before(const struct timespec *a, const struct timespec *b) {
    return 0;
 }
 
+static void warn_backwards(const char *name,
+                           const struct timespec * const t0,
+                           const struct timespec * const t1) {
+   verbosef("%s clock went backwards from %lld.%09lld to %lld.%09lld",
+            name,
+            (lld)t0->tv_sec,
+            (lld)t0->tv_nsec,
+            (lld)t1->tv_sec,
+            (lld)t1->tv_nsec);
+}
+
 static void clock_update(const clockid_t clk_id,
                          struct timespec *dest,
                          const char *name) {
@@ -50,12 +99,7 @@ static void clock_update(const clockid_t clk_id,
 
    clock_gettime(clk_id, &t);
    if (now_initialized && before(&t, dest)) {
-      verbosef("%s clock went backwards from %ld.%09ld to %ld.%09ld",
-               name,
-               (long)dest->tv_sec,
-               (long)dest->tv_nsec,
-               (long)t.tv_sec,
-               (long)t.tv_nsec);
+      warn_backwards(name, &t, dest);
    }
    memcpy(dest, &t, sizeof(t));
 }
@@ -76,12 +120,12 @@ void now_update(void) {
    all_clocks_update();
 }
 
-long mono_to_real(const long t) {
+time_t mono_to_real(const time_t t) {
    assert(now_initialized);
    return t - clock_mono.tv_sec + clock_real.tv_sec;
 }
 
-long real_to_mono(const long t) {
+time_t real_to_mono(const time_t t) {
    assert(now_initialized);
    return t - clock_real.tv_sec + clock_mono.tv_sec;
 }
@@ -96,20 +140,24 @@ static int64_t ts_diff(const struct timespec * const a,
           a->tv_nsec - b->tv_nsec;
 }
 
-void timer_stop(const struct timespec * const t,
+void timer_stop(const struct timespec * const t0,
                 const int64_t nsec,
                 const char *warning) {
-   struct timespec t2;
+   struct timespec t1;
    int64_t diff;
 
-   clock_gettime(CLOCK_MONOTONIC, &t2);
-   diff = ts_diff(&t2, t);
-   assert(diff > 0);
-   if (diff > nsec)
+   clock_gettime(CLOCK_MONOTONIC, &t1);
+   if (before(&t1, t0)) {
+      warn_backwards("monotonic timer", t0, &t1);
+      return;
+   }
+   diff = ts_diff(&t1, t0);
+   if (diff > nsec) {
       warnx("%s (took %lld nsec, over threshold of %lld nsec)",
             warning,
-            (long long)diff,
-            (long long)nsec);
+            (lld)diff,
+            (lld)nsec);
+   }
 }
 
 /* vim:set ts=3 sw=3 tw=80 et: */