From 630f077c296de61c7b99ed83bf30de11e75e2740 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 26 Apr 2012 11:56:59 -0400 Subject: [PATCH] Simple unit tests for monotonic timers --- event.c | 13 +++++++--- evutil_time.c | 28 ++++++++++++++------ test/regress_util.c | 63 +++++++++++++++++++++++++++++++++++++++++++++ time-internal.h | 5 +++- 4 files changed, 96 insertions(+), 13 deletions(-) diff --git a/event.c b/event.c index 7ba39a7d..b41706cc 100644 --- a/event.c +++ b/event.c @@ -560,6 +560,9 @@ 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)); @@ -567,9 +570,13 @@ event_base_new_with_config(const struct event_config *cfg) 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); } @@ -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); diff --git a/evutil_time.c b/evutil_time.c index 5125f218..7b2043bd 100644 --- a/evutil_time.c +++ b/evutil_time.c @@ -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) { @@ -168,12 +172,15 @@ 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 @@ -181,14 +188,14 @@ evutil_configure_monotonic_time_(struct evutil_monotonic_timer *base, * 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; } @@ -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. */ @@ -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; diff --git a/test/regress_util.c b/test/regress_util.c index 38b91cf6..b36af1a5 100644 --- a/test/regress_util.c +++ b/test/regress_util.c @@ -1218,6 +1218,66 @@ end: ; } +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 }, @@ -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, }; diff --git a/time-internal.h b/time-internal.h index a889925c..daf20f47 100644 --- a/time-internal.h +++ b/time-internal.h @@ -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); -- 2.40.0