Merge branch '21_choose_monotonic'

This commit is contained in:
Nick Mathewson 2012-04-17 17:54:26 -04:00
commit 5595a79ff1
3 changed files with 73 additions and 30 deletions

View File

@ -36,6 +36,10 @@ extern "C" {
#include <time.h> #include <time.h>
#include <sys/queue.h> #include <sys/queue.h>
#ifdef EVENT__HAVE_MACH_MACH_TIME_H
/* For mach_timebase_info */
#include <mach/mach_time.h>
#endif
#include "event2/event_struct.h" #include "event2/event_struct.h"
#include "minheap-internal.h" #include "minheap-internal.h"
#include "evsignal-internal.h" #include "evsignal-internal.h"
@ -64,6 +68,8 @@ extern "C" {
#define HAVE_ANY_MONOTONIC 1 #define HAVE_ANY_MONOTONIC 1
#elif defined(EVENT__HAVE_MACH_ABSOLUTE_TIME) #elif defined(EVENT__HAVE_MACH_ABSOLUTE_TIME)
#define HAVE_ANY_MONOTONIC 1 #define HAVE_ANY_MONOTONIC 1
#elif defined(_WIN32)
#define HAVE_ANY_MONOTONIC 1
#endif #endif
/** Structure to define the backend of a given event_base. */ /** Structure to define the backend of a given event_base. */
@ -251,7 +257,21 @@ struct event_base {
* too often. */ * too often. */
struct timeval tv_cache; struct timeval tv_cache;
#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
#ifdef _WIN32
DWORD last_tick_count;
struct timeval adjust_tick_count;
#endif
#if defined(HAVE_ANY_MONOTONIC) #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 /** Difference between internal time (maybe from clock_gettime) and
* gettimeofday. */ * gettimeofday. */
struct timeval tv_clock_diff; struct timeval tv_clock_diff;

74
event.c
View File

@ -46,9 +46,6 @@
#ifdef EVENT__HAVE_UNISTD_H #ifdef EVENT__HAVE_UNISTD_H
#include <unistd.h> #include <unistd.h>
#endif #endif
#ifdef EVENT__HAVE_MACH_MACH_TIME_H
#include <mach/mach_time.h>
#endif
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <signal.h> #include <signal.h>
@ -131,10 +128,6 @@ struct event_base *event_global_current_base_ = NULL;
/* Global state */ /* Global state */
#ifdef HAVE_ANY_MONOTONIC
static int use_monotonic;
#endif
static void *event_self_cbarg_ptr_ = NULL; static void *event_self_cbarg_ptr_ = NULL;
/* Prototypes */ /* Prototypes */
@ -345,19 +338,11 @@ HT_GENERATE(event_debug_map, event_debug_entry, node, hash_debug_entry,
#define EVENT_BASE_ASSERT_LOCKED(base) \ #define EVENT_BASE_ASSERT_LOCKED(base) \
EVLOCK_ASSERT_LOCKED((base)->th_base_lock) EVLOCK_ASSERT_LOCKED((base)->th_base_lock)
#if defined(EVENT__HAVE_MACH_ABSOLUTE_TIME) /* Set base->use_monotonic to 1 if we have a clock function that supports
struct mach_timebase_info mach_timebase_units; * monotonic time */
#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 static void
detect_monotonic(void) detect_monotonic(struct event_base *base, const struct event_config *cfg)
{ {
static int use_monotonic_initialized = 0;
if (use_monotonic_initialized)
return;
#if defined(EVENT__HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) #if defined(EVENT__HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
{ {
/* CLOCK_MONOTONIC exists on FreeBSD, Linux, and Solaris. /* CLOCK_MONOTONIC exists on FreeBSD, Linux, and Solaris.
@ -365,25 +350,34 @@ detect_monotonic(void)
* versions won't have it working. */ * versions won't have it working. */
struct timespec ts; struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
use_monotonic = 1; 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) #elif defined(EVENT__HAVE_MACH_ABSOLUTE_TIME)
{ {
struct mach_timebase_info mi; struct mach_timebase_info mi;
/* OSX has mach_absolute_time() */ /* OSX has mach_absolute_time() */
if (mach_timebase_info(&mi) == 0 && mach_absolute_time() != 0) { 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_timebase_info tells us how to convert
* mach_absolute_time() into nanoseconds, but we * mach_absolute_time() into nanoseconds, but we
* want to use microseconds instead. */ * want to use microseconds instead. */
mi.denom *= 1000; mi.denom *= 1000;
memcpy(&mach_timebase_units, &mi, sizeof(mi)); memcpy(&base->mach_timebase_units, &mi, sizeof(mi));
} }
} }
#elif defined(_WIN32)
base->use_monotonic = 1;
#endif #endif
use_monotonic_initialized = 1;
} }
/* How often (in seconds) do we check for changes in wall clock time relative /* How often (in seconds) do we check for changes in wall clock time relative
@ -406,21 +400,43 @@ gettime(struct event_base *base, struct timeval *tp)
} }
#ifdef HAVE_ANY_MONOTONIC #ifdef HAVE_ANY_MONOTONIC
if (use_monotonic) { if (base->use_monotonic) {
#if defined(EVENT__HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) #if defined(EVENT__HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
struct timespec ts; 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) if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
return (-1); return (-1);
#endif
tp->tv_sec = ts.tv_sec; tp->tv_sec = ts.tv_sec;
tp->tv_usec = ts.tv_nsec / 1000; tp->tv_usec = ts.tv_nsec / 1000;
#elif defined(EVENT__HAVE_MACH_ABSOLUTE_TIME) #elif defined(EVENT__HAVE_MACH_ABSOLUTE_TIME)
uint64_t abstime = mach_absolute_time(); uint64_t abstime = mach_absolute_time();
uint64_t usec; uint64_t usec;
usec = (abstime * mach_timebase_units.numer) usec = (abstime * base->mach_timebase_units.numer)
/ (mach_timebase_units.denom); / (base->mach_timebase_units.denom);
tp->tv_sec = usec / 1000000; tp->tv_sec = usec / 1000000;
tp->tv_usec = 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 #else
#error "Missing monotonic time implementation." #error "Missing monotonic time implementation."
#endif #endif
@ -631,7 +647,7 @@ event_base_new_with_config(const struct event_config *cfg)
event_warn("%s: calloc", __func__); event_warn("%s: calloc", __func__);
return NULL; return NULL;
} }
detect_monotonic(); detect_monotonic(base, cfg);
gettime(base, &base->event_tv); gettime(base, &base->event_tv);
min_heap_ctor_(&base->timeheap); min_heap_ctor_(&base->timeheap);
@ -2621,7 +2637,7 @@ timeout_correct(struct event_base *base, struct timeval *tv)
int i; int i;
#ifdef HAVE_ANY_MONOTONIC #ifdef HAVE_ANY_MONOTONIC
if (use_monotonic) if (base->use_monotonic)
return; return;
#endif #endif

View File

@ -481,7 +481,14 @@ enum event_base_config_flag {
This flag has no effect if you wind up using a backend other than This flag has no effect if you wind up using a backend other than
epoll. 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
}; };
/** /**