From ef401997d311c8ba84f5c36bf94a9239ecaf9a5f Mon Sep 17 00:00:00 2001
From: Richard Nyberg <rnyberg@murmeldjur.se>
Date: Thu, 29 Jan 2009 17:00:40 +0100
Subject: [PATCH] Make the timer code work on MacOS as well.

---
 btpd/main.c     |  5 ++---
 configure.ac    | 23 ++++++++++++++++++++---
 evloop/evloop.h |  1 +
 evloop/timer.c  | 39 +++++++++++++++++++++++++++++++++------
 4 files changed, 56 insertions(+), 12 deletions(-)

diff --git a/btpd/main.c b/btpd/main.c
index c31fa53..f735ec2 100644
--- a/btpd/main.c
+++ b/btpd/main.c
@@ -35,9 +35,8 @@ setup_daemon(int daemonize, const char *dir)
     if (snprintf(NULL, 0, "btpd") != 4)
         btpd_err("snprintf doesn't work.\n");
 
-    if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
-        btpd_err("clock_gettime(CLOCK_MONOTONIC, ...) failed (%s).\n",
-            strerror(errno));
+    if (evtimer_gettime(&ts) != 0)
+        btpd_err("evtimer_gettime failed (%s).\n", strerror(errno));
 
     if (dir == NULL) {
         if ((dir = find_btpd_dir()) == NULL)
diff --git a/configure.ac b/configure.ac
index 8fd64e4..e56a971 100644
--- a/configure.ac
+++ b/configure.ac
@@ -41,15 +41,32 @@ AC_ARG_WITH(evloop-method,
 
 old_LIBS=$LIBS
 LIBS=""
-AC_SEARCH_LIBS(clock_gettime, rt,,AC_MSG_FAILURE(btpd needs clock_gettime))
-AC_SUBST(CLOCKLIB,$LIBS)
-LIBS=""
 AC_SEARCH_LIBS(inet_ntop, nsl,,AC_MSG_FAILURE(btpd needs inet_ntop))
 AC_SEARCH_LIBS(bind, socket,,AC_MSG_FAILURE(btpd needs bind))
 AC_SUBST(INETLIBS,$LIBS)
 LIBS=$old_LIBS
 AC_CHECK_FUNCS(asprintf)
 
+AC_MSG_CHECKING(for CLOCK_MONOTONIC)
+AC_COMPILE_IFELSE([
+    #include <sys/time.h>
+    #include <time.h>
+    int main(void) { return clock_gettime(CLOCK_MONOTONIC, (void *)0); }
+],  clock_gettime=yes, clock_gettime=no)
+AC_MSG_RESULT($clock_gettime)
+if test $clock_gettime == yes; then
+    old_LIBS=$LIBS
+    LIBS=""
+    AC_SEARCH_LIBS(clock_gettime,rt,clock_gettime=yes,clock_gettime=no)
+    AC_SUBST(CLOCKLIB,$LIBS)
+    LIBS=$old_LIBS
+    AC_DEFINE(HAVE_CLOCK_MONOTONIC)
+fi
+if test $clock_gettime == no; then
+    AC_CHECK_FUNCS(mach_absolute_time,,
+        AC_MSG_FAILURE(no supported time mechanism found))
+fi
+
 AC_MSG_CHECKING(whether compiler accepts -Wno-pointer-sign)
 CC_ARGS_OK_IFELSE(-Wno-pointer-sign,
     AC_SUBST(WARNNPS,"-Wno-pointer-sign")
diff --git a/evloop/evloop.h b/evloop/evloop.h
index 55589aa..44f8803 100644
--- a/evloop/evloop.h
+++ b/evloop/evloop.h
@@ -53,5 +53,6 @@ void evtimer_del(struct timeout *);
 
 void evtimers_run(void);
 struct timespec evtimer_delay(void);
+int evtimer_gettime(struct timespec *);
 
 #endif
diff --git a/evloop/timer.c b/evloop/timer.c
index 3174c08..d115959 100644
--- a/evloop/timer.c
+++ b/evloop/timer.c
@@ -3,12 +3,39 @@
 #include "evloop.h"
 #include "timeheap.h"
 
-#if defined(CLOCK_MONOTONIC_FAST)
+#if defined(HAVE_CLOCK_MONOTONIC)
+
+#ifdef CLOCK_MONOTONIC_FAST
 #define TIMER_CLOCK CLOCK_MONOTONIC_FAST
-#elif defined(CLOCK_MONOTONIC)
+#else
 #define TIMER_CLOCK CLOCK_MONOTONIC
+#endif
+
+int
+evtimer_gettime(struct timespec *ts)
+{
+    return clock_gettime(TIMER_CLOCK, ts);
+}
+
+#elif defined(HAVE_MACH_ABSOLUTE_TIME)
+
+#include <mach/mach_time.h>
+
+int
+evtimer_gettime(struct timespec *ts)
+{
+    uint64_t nsecs;
+    static mach_timebase_info_data_t nsratio = { 0, 0 };
+    if (nsratio.denom == 0)
+        mach_timebase_info(&nsratio);
+    nsecs = mach_absolute_time() * nsratio.numer / nsratio.denom;
+    ts->tv_sec = nsecs / 1000000000;
+    ts->tv_nsec = nsecs - ts->tv_sec * 1000000000;
+    return 0;
+}
+
 #else
-#error CLOCK_MONOTONIC needed!
+#error No supported time mechanism
 #endif
 
 static struct timespec
@@ -50,7 +77,7 @@ int
 evtimer_add(struct timeout *h, struct timespec *t)
 {
     struct timespec now, sum;
-    clock_gettime(TIMER_CLOCK, &now);
+    evtimer_gettime(&now);
     sum = addtime(now, *t);
     if (h->th.i == -1)
         return timeheap_insert(&h->th, &sum);
@@ -73,7 +100,7 @@ void
 evtimers_run(void)
 {
     struct timespec now;
-    clock_gettime(TIMER_CLOCK, &now);
+    evtimer_gettime(&now);
     while (timeheap_size() > 0) {
         struct timespec diff = subtime(timeheap_top(), now);
         if (diff.tv_sec < 0) {
@@ -93,7 +120,7 @@ evtimer_delay(void)
         diff.tv_sec = -1;
         diff.tv_nsec = 0;
     } else {
-        clock_gettime(TIMER_CLOCK, &now);
+        evtimer_gettime(&now);
         diff = subtime(timeheap_top(), now);
         if (diff.tv_sec < 0) {
             diff.tv_sec = 0;