From 1fbef7d538928a8d3b9c89a07004996e4b4062f9 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 17 Apr 2012 12:44:39 -0400 Subject: [PATCH 1/4] Move use_monotonic and friends into event_base The use_monotonic field used to be a static field set up at library setup. Unfortunately, this makes it hard to give the user a way to make speed/accuracy tradeoffs about time. Moving it into event_base should let the clock implementation become configurable. --- event-internal.h | 9 +++++++++ event.c | 39 +++++++++++---------------------------- 2 files changed, 20 insertions(+), 28 deletions(-) diff --git a/event-internal.h b/event-internal.h index d2533fa4..e7a2afad 100644 --- a/event-internal.h +++ b/event-internal.h @@ -36,6 +36,10 @@ extern "C" { #include #include +#ifdef EVENT__HAVE_MACH_MACH_TIME_H +/* For mach_timebase_info */ +#include +#endif #include "event2/event_struct.h" #include "minheap-internal.h" #include "evsignal-internal.h" @@ -251,7 +255,12 @@ struct event_base { * too often. */ struct timeval tv_cache; +#if defined(EVENT__HAVE_MACH_ABSOLUTE_TIME) + struct mach_timebase_info mach_timebase_units; +#endif #if defined(HAVE_ANY_MONOTONIC) + /** True iff we should use our system's monotonic time implementation */ + int use_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 a8867112..61899932 100644 --- a/event.c +++ b/event.c @@ -46,9 +46,6 @@ #ifdef EVENT__HAVE_UNISTD_H #include #endif -#ifdef EVENT__HAVE_MACH_MACH_TIME_H -#include -#endif #include #include #include @@ -131,10 +128,6 @@ struct event_base *event_global_current_base_ = NULL; /* Global state */ -#ifdef HAVE_ANY_MONOTONIC -static int use_monotonic; -#endif - static void *event_self_cbarg_ptr_ = NULL; /* Prototypes */ @@ -345,19 +338,11 @@ 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 */ +/* Set base->use_monotonic to 1 if we have a clock function that supports + * monotonic time */ static void -detect_monotonic(void) +detect_monotonic(struct event_base *base) { - static int use_monotonic_initialized = 0; - if (use_monotonic_initialized) - return; - #if defined(EVENT__HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) { /* CLOCK_MONOTONIC exists on FreeBSD, Linux, and Solaris. @@ -366,24 +351,22 @@ detect_monotonic(void) struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) - use_monotonic = 1; + base->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; + base->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)); + memcpy(&base->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 @@ -406,7 +389,7 @@ gettime(struct event_base *base, struct timeval *tp) } #ifdef HAVE_ANY_MONOTONIC - if (use_monotonic) { + if (base->use_monotonic) { #if defined(EVENT__HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) @@ -417,8 +400,8 @@ gettime(struct event_base *base, struct timeval *tp) #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); + usec = (abstime * base->mach_timebase_units.numer) + / (base->mach_timebase_units.denom); tp->tv_sec = usec / 1000000; tp->tv_usec = usec % 1000000; #else @@ -631,7 +614,7 @@ event_base_new_with_config(const struct event_config *cfg) event_warn("%s: calloc", __func__); return NULL; } - detect_monotonic(); + detect_monotonic(base); gettime(base, &base->event_tv); min_heap_ctor_(&base->timeheap); @@ -2621,7 +2604,7 @@ timeout_correct(struct event_base *base, struct timeval *tv) int i; #ifdef HAVE_ANY_MONOTONIC - if (use_monotonic) + if (base->use_monotonic) return; #endif From ddd69d391e05356113aa62c26c9090f540c85b9d Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 17 Apr 2012 13:04:02 -0400 Subject: [PATCH 2/4] EVENT_BASE_FLAG_PRECISE_TIMER indicates we want fine timer precision There are a bunch of backends that can give us a reasonably good monotonic timer quickly, or a very precise monotonic timer less quickly. For example, Linux has CLOCK_MONOTONIC_COARSE (1msec precision), which is over twice as fast as CLOCK_MONOTONIC. Since epoll only lets you wait with 1msec precision, CLOCK_MONOTONIC_COARSE is a clear win. On Windows, you've got GetTickCount{,64}() which is fast, and QueryPerformanceCounter, which is precise but slow. Note that even in the cases where the underlying timer claims to have nanosecond resolution, we're still not exposing that via Libevent. Note also that "Precision" isn't the same as "Resolution" or "Accuracy". A timer's precision is the smallest change that the clock will register; a timer's resolution is the fineness of its underlying representation; a timer's accuracy is how much it drifts with respect to "Real Time", whatever that means. (Terms and definitions from PEP 418.) --- include/event2/event.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/include/event2/event.h b/include/event2/event.h index 9685e0a2..f1331f62 100644 --- a/include/event2/event.h +++ b/include/event2/event.h @@ -481,7 +481,14 @@ enum event_base_config_flag { This flag has no effect if you wind up using a backend other than epoll. */ - EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST = 0x10 + EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST = 0x10, + + /** Ordinarily, Libevent implements its time and timeout code using + the fastest monotonic timer that we have. If this flag is set, + however, we use less efficient more precise timer, assuming one is + present. + */ + EVENT_BASE_FLAG_PRECISE_TIMER = 0x20 }; /** From 55780a70e294ecb2d0bf674b8db3fffe9466e6fc Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 17 Apr 2012 13:09:49 -0400 Subject: [PATCH 3/4] On Linux, use CLOCK_MONOTONIC_COARSE by default You can make it use CLOCK_MONOTONIC again by setting the EVENT_BASE_FLAG_PRECISE_TIMER flag in the event_config. --- event-internal.h | 4 ++++ event.c | 20 +++++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/event-internal.h b/event-internal.h index e7a2afad..6910469c 100644 --- a/event-internal.h +++ b/event-internal.h @@ -258,6 +258,10 @@ struct event_base { #if defined(EVENT__HAVE_MACH_ABSOLUTE_TIME) struct mach_timebase_info mach_timebase_units; #endif +#if defined(EVENT__HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) && defined(CLOCK_MONOTONIC_COARSE) +#define CLOCK_IS_SELECTED + int monotonic_clock; +#endif #if defined(HAVE_ANY_MONOTONIC) /** True iff we should use our system's monotonic time implementation */ int use_monotonic; diff --git a/event.c b/event.c index 61899932..fe55e216 100644 --- a/event.c +++ b/event.c @@ -341,7 +341,7 @@ HT_GENERATE(event_debug_map, event_debug_entry, node, hash_debug_entry, /* Set base->use_monotonic to 1 if we have a clock function that supports * monotonic time */ static void -detect_monotonic(struct event_base *base) +detect_monotonic(struct event_base *base, const struct event_config *cfg) { #if defined(EVENT__HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) { @@ -350,8 +350,17 @@ detect_monotonic(struct event_base *base) * versions won't have it working. */ struct timespec ts; - if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { base->use_monotonic = 1; +#ifdef CLOCK_IS_SELECTED + base->monotonic_clock = CLOCK_MONOTONIC; + if (cfg == NULL || + !(cfg->flags & EVENT_BASE_FLAG_PRECISE_TIMER)) { + if (clock_gettime(CLOCK_MONOTONIC_COARSE, &ts) == 0) + base->monotonic_clock = CLOCK_MONOTONIC_COARSE; + } +#endif + } } #elif defined(EVENT__HAVE_MACH_ABSOLUTE_TIME) { @@ -392,8 +401,13 @@ gettime(struct event_base *base, struct timeval *tp) if (base->use_monotonic) { #if defined(EVENT__HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) struct timespec ts; +#ifdef CLOCK_IS_SELECTED + if (clock_gettime(base->monotonic_clock, &ts) == -1) + return (-1); +#else if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) return (-1); +#endif tp->tv_sec = ts.tv_sec; tp->tv_usec = ts.tv_nsec / 1000; @@ -614,7 +628,7 @@ event_base_new_with_config(const struct event_config *cfg) event_warn("%s: calloc", __func__); return NULL; } - detect_monotonic(base); + detect_monotonic(base, cfg); gettime(base, &base->event_tv); min_heap_ctor_(&base->timeheap); From d5e1d5ad99d4a1dd05f3c72cbbd9a985ef43fcf3 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 17 Apr 2012 15:16:08 -0400 Subject: [PATCH 4/4] Implement a GetTickCount-based monotonic timer for Windows --- event-internal.h | 7 +++++++ event.c | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/event-internal.h b/event-internal.h index 6910469c..a2fc8e80 100644 --- a/event-internal.h +++ b/event-internal.h @@ -68,6 +68,8 @@ extern "C" { #define HAVE_ANY_MONOTONIC 1 #elif defined(EVENT__HAVE_MACH_ABSOLUTE_TIME) #define HAVE_ANY_MONOTONIC 1 +#elif defined(_WIN32) +#define HAVE_ANY_MONOTONIC 1 #endif /** Structure to define the backend of a given event_base. */ @@ -262,8 +264,13 @@ struct event_base { #define CLOCK_IS_SELECTED int monotonic_clock; #endif +#ifdef _WIN32 + DWORD last_tick_count; + struct timeval adjust_tick_count; +#endif #if defined(HAVE_ANY_MONOTONIC) /** True iff we should use our system's monotonic time implementation */ + /* TODO: Support systems where we don't need to detct monotonic time */ int use_monotonic; /** Difference between internal time (maybe from clock_gettime) and * gettimeofday. */ diff --git a/event.c b/event.c index fe55e216..c1d00eb3 100644 --- a/event.c +++ b/event.c @@ -375,6 +375,8 @@ detect_monotonic(struct event_base *base, const struct event_config *cfg) memcpy(&base->mach_timebase_units, &mi, sizeof(mi)); } } +#elif defined(_WIN32) + base->use_monotonic = 1; #endif } @@ -418,6 +420,23 @@ gettime(struct event_base *base, struct timeval *tp) / (base->mach_timebase_units.denom); tp->tv_sec = usec / 1000000; tp->tv_usec = usec % 1000000; +#elif defined(_WIN32) + /* TODO: Support GetTickCount64. */ + /* TODO: Support alternate timer backends if the user asked + * for a high-precision timer. QueryPerformanceCounter is + * possibly a good idea, but it is also supposed to have + * reliability issues under various circumstances. */ + DWORD ticks = GetTickCount(); + if (ticks < base->last_tick_count) { + /* The 32-bit timer rolled over. Let's assume it only + * happened once. Add 2**32 msec to adjust_tick_count. */ + const struct timeval tv_rollover = { 4294967, 296000 }; + evutil_timeradd(&tv_rollover, &base->adjust_tick_count, &base->adjust_tick_count); + } + base->last_tick_count = ticks; + tp->tv_sec = ticks / 1000; + tp->tv_usec = (ticks % 1000) * 1000; + evutil_timeradd(tp, &base->adjust_tick_count, tp); #else #error "Missing monotonic time implementation." #endif