summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--event.c13
-rw-r--r--evutil_time.c28
-rw-r--r--test/regress_util.c63
-rw-r--r--time-internal.h5
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);