$NetBSD: patch-aa,v 1.1 2010/12/07 22:26:10 abs Exp $

--- eloop.c.orig	2010-12-06 14:41:51.000000000 +0000
+++ eloop.c
@@ -25,6 +25,11 @@
  * SUCH DAMAGE.
  */
 
+#ifdef __APPLE__
+#  include <mach/mach_time.h>
+#  include <mach/kern_return.h>
+#endif
+
 #include <sys/time.h>
 
 #include <errno.h>
@@ -33,6 +38,7 @@
 #include <stdarg.h>
 #include <stdlib.h>
 #include <syslog.h>
+#include <unistd.h>
 
 #include "eloop.h"
 
@@ -65,16 +71,71 @@ static struct timeout *free_timeouts;
 static struct pollfd *fds;
 static size_t fds_len;
 
-static int get_monotonic(struct timeval *tp)
+/* Handy function to get the time.
+ * We only care about time advancements, not the actual time itself
+ * Which is why we use CLOCK_MONOTONIC, but it is not available on all
+ * platforms.
+ */
+#define NO_MONOTONIC "host does not support a monotonic clock - timing can skew"
+static int clock_monotonic;
+static int
+get_monotonic(struct timeval *tp)
 {
+	static int posix_clock_set = 0;
+#if defined(_POSIX_MONOTONIC_CLOCK) && defined(CLOCK_MONOTONIC)
 	struct timespec ts;
+	static clockid_t posix_clock;
+
+	if (!posix_clock_set) {
+		if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
+			posix_clock = CLOCK_MONOTONIC;
+			clock_monotonic = posix_clock_set = 1;
+		}
+	}
 
-	if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
-		tp->tv_sec = ts.tv_sec;
-		tp->tv_usec = ts.tv_nsec / 1000;
+	if (clock_monotonic) {
+		if (clock_gettime(posix_clock, &ts) == 0) {
+			tp->tv_sec = ts.tv_sec;
+			tp->tv_usec = ts.tv_nsec / 1000;
+			return 0;
+		}
+	}
+#elif defined(__APPLE__)
+#define NSEC_PER_SEC 1000000000
+	/* We can use mach kernel functions here.
+	 * This is crap though - why can't they implement clock_gettime?*/
+	static struct mach_timebase_info info = { 0, 0 };
+	static double factor = 0.0;
+	uint64_t nano;
+	long rem;
+
+	if (!posix_clock_set) {
+		if (mach_timebase_info(&info) == KERN_SUCCESS) {
+			factor = (double)info.numer / (double)info.denom;
+			clock_monotonic = posix_clock_set = 1;
+		}
+	}
+	if (clock_monotonic) {
+		nano = mach_absolute_time();
+		if ((info.denom != 1 || info.numer != 1) && factor != 0.0)
+			nano *= factor;
+		tp->tv_sec = nano / NSEC_PER_SEC;
+		rem = nano % NSEC_PER_SEC;
+		if (rem < 0) {
+			tp->tv_sec--;
+			rem += NSEC_PER_SEC;
+		}
+		tp->tv_usec = rem / 1000;
 		return 0;
 	}
-	return -1;
+#endif
+
+	/* Something above failed, so fall back to gettimeofday */
+	if (!posix_clock_set) {
+		syslog(LOG_WARNING, NO_MONOTONIC);
+		posix_clock_set = 1;
+	}
+	return gettimeofday(tp, NULL);
 }
 
 static int
