Skip to content

Commit

Permalink
Simple unit tests for monotonic timers
Browse files Browse the repository at this point in the history
  • Loading branch information
nmathewson committed Apr 26, 2012
1 parent 2c47045 commit 630f077
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 13 deletions.
13 changes: 9 additions & 4 deletions event.c
Expand Up @@ -560,16 +560,23 @@ event_base_new_with_config(const struct event_config *cfg)
return NULL;
}

if (cfg)
base->flags = cfg->flags;

should_check_environment =
!(cfg && (cfg->flags & EVENT_BASE_FLAG_IGNORE_ENV));

{
struct timeval tmp;
int precise_time =
cfg && (cfg->flags & EVENT_BASE_FLAG_PRECISE_TIMER);
if (should_check_environment && !precise_time)
int flags;
if (should_check_environment && !precise_time) {
precise_time = evutil_getenv_("EVENT_PRECISE_TIMER") != NULL;
evutil_configure_monotonic_time_(&base->monotonic_timer, precise_time);
base->flags |= EVENT_BASE_FLAG_PRECISE_TIMER;
}
flags = precise_time ? EV_MONOT_PRECISE : 0;
evutil_configure_monotonic_time_(&base->monotonic_timer, flags);

gettime(base, &tmp);
}
Expand All @@ -585,8 +592,6 @@ event_base_new_with_config(const struct event_config *cfg)
base->defer_queue.base = base;
base->defer_queue.notify_fn = notify_base_cbq_callback;
base->defer_queue.notify_arg = base;
if (cfg)
base->flags = cfg->flags;

evmap_io_initmap_(&base->io);
evmap_signal_initmap_(&base->sigmap);
Expand Down
28 changes: 20 additions & 8 deletions evutil_time.c
Expand Up @@ -107,6 +107,10 @@ evutil_tv_to_msec_(const struct timeval *tv)
return (tv->tv_sec * 1000) + ((tv->tv_usec + 999) / 1000);
}

/*
Replacement for usleep on platforms that don't have one. Not guaranteed to
be any more finegrained than 1 msec.
*/
void
evutil_usleep_(const struct timeval *tv)
{
Expand Down Expand Up @@ -168,27 +172,30 @@ adjust_monotonic_time(struct evutil_monotonic_timer *base,

int
evutil_configure_monotonic_time_(struct evutil_monotonic_timer *base,
int precise)
int flags)
{
/* CLOCK_MONOTONIC exists on FreeBSD, Linux, and Solaris. You need to
* check for it at runtime, because some older kernel versions won't
* have it working. */
const int precise = flags & EV_MONOT_PRECISE;
const int fallback = flags & EV_MONOT_FALLBACK;
struct timespec ts;

#ifdef CLOCK_MONOTONIC_COARSE
#if CLOCK_MONOTONIC_COARSE < 0
/* Technically speaking, nothing keeps CLOCK_* from being negative (as
* far as I know). This check and the one below make sure that it's
* safe for us to use -1 as an "unset" value. */
#error "I didn't expect CLOCK_MONOTONIC_COARSE to be < 0"
#endif
if (! precise) {
if (! precise && ! fallback) {
if (clock_gettime(CLOCK_MONOTONIC_COARSE, &ts) == 0) {
base->monotonic_clock = CLOCK_MONOTONIC_COARSE;
return 0;
}
}
#endif
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
if (!fallback && clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
base->monotonic_clock = CLOCK_MONOTONIC;
return 0;
}
Expand Down Expand Up @@ -237,12 +244,15 @@ evutil_gettime_monotonic_(struct evutil_monotonic_timer *base,

int
evutil_configure_monotonic_time_(struct evutil_monotonic_timer *base,
int precise)
int flags)
{
const int fallback = flags & EV_MONOT_FALLBACK;
struct mach_timebase_info mi;
memset(base, 0, sizeof(*base));
/* OSX has mach_absolute_time() */
if (mach_timebase_info(&mi) == 0 && mach_absolute_time() != 0) {
if (!fallback &&
mach_timebase_info(&mi) == 0 &&
mach_absolute_time() != 0) {
/* mach_timebase_info tells us how to convert
* mach_absolute_time() into nanoseconds, but we
* want to use microseconds instead. */
Expand Down Expand Up @@ -367,19 +377,21 @@ evutil_GetTickCount_(struct evutil_monotonic_timer *base)

int
evutil_configure_monotonic_time_(struct evutil_monotonic_timer *base,
int precise)
int flags)
{
const int precise = flags & EV_MONOT_PRECISE;
const int fallback = flags & EV_MONOT_FALLBACK;
HANDLE h;
memset(base, 0, sizeof(*base));

h = evutil_load_windows_system_library_(TEXT("kernel32.dll"));
if (h != NULL) {
if (h != NULL && !fallback) {
base->GetTickCount64_fn = (ev_GetTickCount_func)GetProcAddress(h, "GetTickCount64");
base->GetTickCount_fn = (ev_GetTickCount_func)GetProcAddress(h, "GetTickCount");
}

base->first_tick = base->last_tick_count = evutil_GetTickCount_(base);
if (precise) {
if (precise && !fallback) {
LARGE_INTEGER freq;
if (QueryPerformanceFrequency(&freq)) {
LARGE_INTEGER counter;
Expand Down
63 changes: 63 additions & 0 deletions test/regress_util.c
Expand Up @@ -1218,6 +1218,66 @@ test_evutil_usleep(void *arg)
;
}

static void
test_evutil_monotonic(void *data_)
{
/* Basic santity-test for monotonic timers. What we'd really like
* to do is make sure that they can't go backwards even when the
* system clock goes backwards. But we haven't got a good way to
* move the system clock backwards.
*/
struct basic_test_data *data = data_;
struct evutil_monotonic_timer timer;
const int precise = strstr(data->setup_data, "precise") != NULL;
const int fallback = strstr(data->setup_data, "fallback") != NULL;
struct timeval tv[10], delay;
int total_diff = 0;

int flags = 0, wantres, acceptdiff, i;
if (precise)
flags |= EV_MONOT_PRECISE;
if (fallback)
flags |= EV_MONOT_FALLBACK;
if (precise || fallback) {
#ifdef _WIN32
wantres = 10*1000;
acceptdiff = 1000;
#else
wantres = 300;
acceptdiff = 100;
#endif
} else {
wantres = 40*1000;
acceptdiff = 20*1000;
}

TT_BLATHER(("Precise = %d", precise));
TT_BLATHER(("Fallback = %d", fallback));

delay.tv_sec = 0;
delay.tv_usec = wantres;

tt_int_op(evutil_configure_monotonic_time_(&timer, flags), ==, 0);

for (i = 0; i < 10; ++i) {
evutil_gettime_monotonic_(&timer, &tv[i]);
evutil_usleep_(&delay);
}

for (i = 0; i < 9; ++i) {
struct timeval diff;
tt_assert(evutil_timercmp(&tv[i], &tv[i+1], <));
evutil_timersub(&tv[i+1], &tv[i], &diff);
tt_int_op(diff.tv_sec, ==, 0);
total_diff += diff.tv_usec;
TT_BLATHER(("Difference = %d", (int)diff.tv_usec));
}
tt_int_op(abs(total_diff/10 - wantres), <, acceptdiff);

end:
;
}

struct testcase_t util_testcases[] = {
{ "ipv4_parse", regress_ipv4_parse, 0, NULL, NULL },
{ "ipv6_parse", regress_ipv6_parse, 0, NULL, NULL },
Expand All @@ -1240,6 +1300,9 @@ struct testcase_t util_testcases[] = {
{ "mm_calloc", test_event_calloc, 0, NULL, NULL },
{ "mm_strdup", test_event_strdup, 0, NULL, NULL },
{ "usleep", test_evutil_usleep, 0, NULL, NULL },
{ "monotonic", test_evutil_monotonic, 0, &basic_setup, (void*)"" },
{ "monotonic_precise", test_evutil_monotonic, 0, &basic_setup, (void*)"precise" },
{ "monotonic_fallback", test_evutil_monotonic, 0, &basic_setup, (void*)"fallback" },
END_OF_TESTCASES,
};

5 changes: 4 additions & 1 deletion time-internal.h
Expand Up @@ -86,8 +86,11 @@ struct evutil_monotonic_timer {
struct timeval last_time;
};

#define EV_MONOT_PRECISE 1
#define EV_MONOT_FALLBACK 2

int evutil_configure_monotonic_time_(struct evutil_monotonic_timer *mt,
int precise);
int flags);
int evutil_gettime_monotonic_(struct evutil_monotonic_timer *mt, struct timeval *tv);


Expand Down

0 comments on commit 630f077

Please sign in to comment.