diff --git a/configure.in b/configure.in index bea4aaf3..9bd2a2da 100644 --- a/configure.in +++ b/configure.in @@ -198,6 +198,7 @@ AC_CHECK_HEADERS([ \ fcntl.h \ ifaddrs.h \ inttypes.h \ + mach/mach_time.h \ netdb.h \ netinet/in.h \ netinet/in6.h \ @@ -339,6 +340,7 @@ AC_CHECK_FUNCS([ \ inet_ntop \ inet_pton \ issetugid \ + mach_absolute_time \ mmap \ nanosleep \ pipe \ diff --git a/event-internal.h b/event-internal.h index 78b3fe63..d2533fa4 100644 --- a/event-internal.h +++ b/event-internal.h @@ -58,6 +58,14 @@ extern "C" { #define EV_CLOSURE_SIGNAL 1 #define EV_CLOSURE_PERSIST 2 +/* Define HAVE_ANY_MONOTONIC iff we *might* have a working monotonic + * clock implementation */ +#if defined(EVENT__HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) +#define HAVE_ANY_MONOTONIC 1 +#elif defined(EVENT__HAVE_MACH_ABSOLUTE_TIME) +#define HAVE_ANY_MONOTONIC 1 +#endif + /** Structure to define the backend of a given event_base. */ struct eventop { /** The name of this backend. */ @@ -243,7 +251,7 @@ struct event_base { * too often. */ struct timeval tv_cache; -#if defined(EVENT__HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) +#if defined(HAVE_ANY_MONOTONIC) /** Difference between internal time (maybe from clock_gettime) and * gettimeofday. */ struct timeval tv_clock_diff; diff --git a/event.c b/event.c index 0f10b569..a85d1f5a 100644 --- a/event.c +++ b/event.c @@ -46,6 +46,9 @@ #ifdef EVENT__HAVE_UNISTD_H #include #endif +#ifdef EVENT__HAVE_MACH_MACH_TIME_H +#include +#endif #include #include #include @@ -335,23 +338,45 @@ HT_GENERATE(event_debug_map, event_debug_entry, node, hash_debug_entry, #define EVENT_BASE_ASSERT_LOCKED(base) \ EVLOCK_ASSERT_LOCKED((base)->th_base_lock) +#if defined(EVENT__HAVE_MACH_ABSOLUTE_TIME) +struct mach_timebase_info mach_timebase_units; +#endif + /* The first time this function is called, it sets use_monotonic to 1 * if we have a clock function that supports monotonic time */ static void detect_monotonic(void) { -#if defined(EVENT__HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) - struct timespec ts; static int use_monotonic_initialized = 0; - if (use_monotonic_initialized) return; - if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) - use_monotonic = 1; +#if defined(EVENT__HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) + { + /* CLOCK_MONOTONIC exists on FreeBSD, Linux, and Solaris. + * You need to check for it at runtime, because some older + * versions won't have it working. */ + struct timespec ts; - use_monotonic_initialized = 1; + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + use_monotonic = 1; + } +#elif defined(EVENT__HAVE_MACH_ABSOLUTE_TIME) + { + struct mach_timebase_info mi; + /* OSX has mach_absolute_time() */ + if (mach_timebase_info(&mi) == 0 && mach_absolute_time() != 0) { + use_monotonic = 1; + /* mach_timebase_info tells us how to convert + * mach_absolute_time() into nanoseconds, but we + * want to use microseconds instead. */ + mi.denom *= 1000; + memcpy(&mach_timebase_units, &mi, sizeof(mi)); + } + } #endif + use_monotonic_initialized = 1; + } /* How often (in seconds) do we check for changes in wall clock time relative @@ -373,26 +398,34 @@ gettime(struct event_base *base, struct timeval *tp) return (0); } -#if defined(EVENT__HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) if (use_monotonic) { +#if defined(EVENT__HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) struct timespec ts; - if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) return (-1); tp->tv_sec = ts.tv_sec; tp->tv_usec = ts.tv_nsec / 1000; +#elif defined(EVENT__HAVE_MACH_ABSOLUTE_TIME) + uint64_t abstime = mach_absolute_time(); + uint64_t usec; + usec = (abstime * mach_timebase_units.numer) + / (mach_timebase_units.denom); + tp->tv_sec = usec / 1000000; + tp->tv_usec = usec % 1000000; +#else +#error "Missing monotonic time implementation." +#endif if (base->last_updated_clock_diff + CLOCK_SYNC_INTERVAL - < ts.tv_sec) { + < tp->tv_sec) { struct timeval tv; evutil_gettimeofday(&tv,NULL); evutil_timersub(&tv, tp, &base->tv_clock_diff); - base->last_updated_clock_diff = ts.tv_sec; + base->last_updated_clock_diff = tp->tv_sec; } return (0); } -#endif return (evutil_gettimeofday(tp, NULL)); } @@ -411,7 +444,7 @@ event_base_gettimeofday_cached(struct event_base *base, struct timeval *tv) if (base->tv_cache.tv_sec == 0) { r = evutil_gettimeofday(tv, NULL); } else { -#if defined(EVENT__HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) +#ifdef HAVE_ANY_MONOTONIC evutil_timeradd(&base->tv_cache, &base->tv_clock_diff, tv); #else *tv = base->tv_cache; @@ -2014,7 +2047,7 @@ event_pending(const struct event *ev, short event, struct timeval *tv) if (tv != NULL && (flags & event & EV_TIMEOUT)) { struct timeval tmp = ev->ev_timeout; tmp.tv_usec &= MICROSECONDS_MASK; -#if defined(EVENT__HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) +#ifdef HAVE_ANY_MONOTONIC /* correctly remamp to real time */ evutil_timeradd(&ev->ev_base->tv_clock_diff, &tmp, tv); #else @@ -3125,7 +3158,7 @@ dump_inserted_event_fn(struct event_base *base, struct event *e, void *arg) struct timeval tv; tv.tv_sec = e->ev_timeout.tv_sec; tv.tv_usec = e->ev_timeout.tv_usec & MICROSECONDS_MASK; -#if defined(EVENT__HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) +#if defined(HAVE_ANY_MONOTONIC) evutil_timeradd(&tv, &base->tv_clock_diff, &tv); #endif fprintf(output, " Timeout=%ld.%06d",