From aa971c59246332391116caafd18855bccf2f99a5 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 8 Jul 2016 10:38:59 -0400 Subject: [PATCH 01/10] Move our "what time is it now" compat functions into a new module I'm not moving our "format and parse the time" functions, since those have been pretty volatile over the last couple of years. --- src/common/compat.c | 91 +++----------------------------- src/common/compat.h | 15 +----- src/common/compat_time.c | 110 +++++++++++++++++++++++++++++++++++++++ src/common/compat_time.h | 26 +++++++++ src/common/include.am | 1 + 5 files changed, 145 insertions(+), 98 deletions(-) create mode 100644 src/common/compat_time.c create mode 100644 src/common/compat_time.h diff --git a/src/common/compat.c b/src/common/compat.c index bae76f45db..081490d381 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -33,6 +33,12 @@ #ifdef HAVE_SYS_STAT_H #include #endif +#ifdef HAVE_UTIME_H +#include +#endif +#ifdef HAVE_SYS_UTIME_H +#include +#endif #ifdef HAVE_UNISTD_H #include #endif @@ -89,12 +95,6 @@ SecureZeroMemory(PVOID ptr, SIZE_T cnt) #include "tor_readpassphrase.h" #endif -#ifndef HAVE_GETTIMEOFDAY -#ifdef HAVE_FTIME -#include -#endif -#endif - /* Includes for the process attaching prevention */ #if defined(HAVE_SYS_PRCTL_H) && defined(__linux__) /* Only use the linux prctl; the IRIX prctl is totally different */ @@ -116,12 +116,6 @@ SecureZeroMemory(PVOID ptr, SIZE_T cnt) #ifdef HAVE_SIGNAL_H #include #endif -#ifdef HAVE_UTIME_H -#include -#endif -#ifdef HAVE_SYS_UTIME_H -#include -#endif #ifdef HAVE_SYS_MMAN_H #include #endif @@ -131,12 +125,6 @@ SecureZeroMemory(PVOID ptr, SIZE_T cnt) #ifdef HAVE_SYS_FILE_H #include #endif -#ifdef TOR_UNIT_TESTS -#if !defined(HAVE_USLEEP) && defined(HAVE_SYS_SELECT_H) -/* as fallback implementation for tor_sleep_msec */ -#include -#endif -#endif #include "torlog.h" #include "util.h" @@ -2836,53 +2824,6 @@ compute_num_cpus(void) return num_cpus; } -/** Set *timeval to the current time of day. On error, log and terminate. - * (Same as gettimeofday(timeval,NULL), but never returns -1.) - */ -void -tor_gettimeofday(struct timeval *timeval) -{ -#ifdef _WIN32 - /* Epoch bias copied from perl: number of units between windows epoch and - * Unix epoch. */ -#define EPOCH_BIAS U64_LITERAL(116444736000000000) -#define UNITS_PER_SEC U64_LITERAL(10000000) -#define USEC_PER_SEC U64_LITERAL(1000000) -#define UNITS_PER_USEC U64_LITERAL(10) - union { - uint64_t ft_64; - FILETIME ft_ft; - } ft; - /* number of 100-nsec units since Jan 1, 1601 */ - GetSystemTimeAsFileTime(&ft.ft_ft); - if (ft.ft_64 < EPOCH_BIAS) { - /* LCOV_EXCL_START */ - log_err(LD_GENERAL,"System time is before 1970; failing."); - exit(1); - /* LCOV_EXCL_STOP */ - } - ft.ft_64 -= EPOCH_BIAS; - timeval->tv_sec = (unsigned) (ft.ft_64 / UNITS_PER_SEC); - timeval->tv_usec = (unsigned) ((ft.ft_64 / UNITS_PER_USEC) % USEC_PER_SEC); -#elif defined(HAVE_GETTIMEOFDAY) - if (gettimeofday(timeval, NULL)) { - /* LCOV_EXCL_START */ - log_err(LD_GENERAL,"gettimeofday failed."); - /* If gettimeofday dies, we have either given a bad timezone (we didn't), - or segfaulted.*/ - exit(1); - /* LCOV_EXCL_STOP */ - } -#elif defined(HAVE_FTIME) - struct timeb tb; - ftime(&tb); - timeval->tv_sec = tb.time; - timeval->tv_usec = tb.millitm * 1000; -#else -#error "No way to get time." -#endif - return; -} #if !defined(_WIN32) /** Defined iff we need to add locks when defining fake versions of reentrant @@ -3441,26 +3382,6 @@ get_total_system_memory(size_t *mem_out) return 0; } -#ifdef TOR_UNIT_TESTS -/** Delay for msec milliseconds. Only used in tests. */ -void -tor_sleep_msec(int msec) -{ -#ifdef _WIN32 - Sleep(msec); -#elif defined(HAVE_USLEEP) - sleep(msec / 1000); - /* Some usleep()s hate sleeping more than 1 sec */ - usleep((msec % 1000) * 1000); -#elif defined(HAVE_SYS_SELECT_H) - struct timeval tv = { msec / 1000, (msec % 1000) * 1000}; - select(0, NULL, NULL, NULL, &tv); -#else - sleep(CEIL_DIV(msec, 1000)); -#endif -} -#endif - /** Emit the password prompt prompt, then read up to buflen * bytes of passphrase into output. Return the number of bytes in * the passphrase, excluding terminating NUL. diff --git a/src/common/compat.h b/src/common/compat.h index 6f102becc2..54ab8b08e8 100644 --- a/src/common/compat.h +++ b/src/common/compat.h @@ -42,6 +42,8 @@ #include #endif +#include "compat_time.h" + #if defined(__has_feature) # if __has_feature(address_sanitizer) /* Some of the fancy glibc strcmp() macros include references to memory that @@ -379,15 +381,6 @@ const char *tor_fix_source_file(const char *fname); #endif /* ===== Time compatibility */ -#if !defined(HAVE_GETTIMEOFDAY) && !defined(HAVE_STRUCT_TIMEVAL_TV_SEC) -/** Implementation of timeval for platforms that don't have it. */ -struct timeval { - time_t tv_sec; - unsigned int tv_usec; -}; -#endif - -void tor_gettimeofday(struct timeval *timeval); struct tm *tor_localtime_r(const time_t *timep, struct tm *result); struct tm *tor_gmtime_r(const time_t *timep, struct tm *result); @@ -737,10 +730,6 @@ char *format_win32_error(DWORD err); #endif -#ifdef TOR_UNIT_TESTS -void tor_sleep_msec(int msec); -#endif - #ifdef COMPAT_PRIVATE #if !defined(HAVE_SOCKETPAIR) || defined(_WIN32) || defined(TOR_UNIT_TESTS) #define NEED_ERSATZ_SOCKETPAIR diff --git a/src/common/compat_time.c b/src/common/compat_time.c new file mode 100644 index 0000000000..866c288fe6 --- /dev/null +++ b/src/common/compat_time.c @@ -0,0 +1,110 @@ +/* Copyright (c) 2003-2004, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file compat_time.c + * \brief Portable wrappers for finding out the current time, running + * timers, etc. + **/ + +#define COMPAT_PRIVATE +#include "compat.h" + +#ifdef _WIN32 +#include +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef TOR_UNIT_TESTS +#if !defined(HAVE_USLEEP) && defined(HAVE_SYS_SELECT_H) +/* as fallback implementation for tor_sleep_msec */ +#include +#endif +#endif + +#include "torlog.h" +#include "util.h" +#include "container.h" + +#ifndef HAVE_GETTIMEOFDAY +#ifdef HAVE_FTIME +#include +#endif +#endif + +#ifdef TOR_UNIT_TESTS +/** Delay for msec milliseconds. Only used in tests. */ +void +tor_sleep_msec(int msec) +{ +#ifdef _WIN32 + Sleep(msec); +#elif defined(HAVE_USLEEP) + sleep(msec / 1000); + /* Some usleep()s hate sleeping more than 1 sec */ + usleep((msec % 1000) * 1000); +#elif defined(HAVE_SYS_SELECT_H) + struct timeval tv = { msec / 1000, (msec % 1000) * 1000}; + select(0, NULL, NULL, NULL, &tv); +#else + sleep(CEIL_DIV(msec, 1000)); +#endif +} +#endif + +/** Set *timeval to the current time of day. On error, log and terminate. + * (Same as gettimeofday(timeval,NULL), but never returns -1.) + */ +void +tor_gettimeofday(struct timeval *timeval) +{ +#ifdef _WIN32 + /* Epoch bias copied from perl: number of units between windows epoch and + * Unix epoch. */ +#define EPOCH_BIAS U64_LITERAL(116444736000000000) +#define UNITS_PER_SEC U64_LITERAL(10000000) +#define USEC_PER_SEC U64_LITERAL(1000000) +#define UNITS_PER_USEC U64_LITERAL(10) + union { + uint64_t ft_64; + FILETIME ft_ft; + } ft; + /* number of 100-nsec units since Jan 1, 1601 */ + GetSystemTimeAsFileTime(&ft.ft_ft); + if (ft.ft_64 < EPOCH_BIAS) { + /* LCOV_EXCL_START */ + log_err(LD_GENERAL,"System time is before 1970; failing."); + exit(1); + /* LCOV_EXCL_STOP */ + } + ft.ft_64 -= EPOCH_BIAS; + timeval->tv_sec = (unsigned) (ft.ft_64 / UNITS_PER_SEC); + timeval->tv_usec = (unsigned) ((ft.ft_64 / UNITS_PER_USEC) % USEC_PER_SEC); +#elif defined(HAVE_GETTIMEOFDAY) + if (gettimeofday(timeval, NULL)) { + /* LCOV_EXCL_START */ + log_err(LD_GENERAL,"gettimeofday failed."); + /* If gettimeofday dies, we have either given a bad timezone (we didn't), + or segfaulted.*/ + exit(1); + /* LCOV_EXCL_STOP */ + } +#elif defined(HAVE_FTIME) + struct timeb tb; + ftime(&tb); + timeval->tv_sec = tb.time; + timeval->tv_usec = tb.millitm * 1000; +#else +#error "No way to get time." +#endif + return; +} + diff --git a/src/common/compat_time.h b/src/common/compat_time.h new file mode 100644 index 0000000000..56126ba84c --- /dev/null +++ b/src/common/compat_time.h @@ -0,0 +1,26 @@ +/* Copyright (c) 2003-2004, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_COMPAT_TIME_H +#define TOR_COMPAT_TIME_H + +#include "orconfig.h" + +#if !defined(HAVE_GETTIMEOFDAY) && !defined(HAVE_STRUCT_TIMEVAL_TV_SEC) +/** Implementation of timeval for platforms that don't have it. */ +struct timeval { + time_t tv_sec; + unsigned int tv_usec; +}; +#endif + +void tor_gettimeofday(struct timeval *timeval); + +#ifdef TOR_UNIT_TESTS +void tor_sleep_msec(int msec); +#endif + +#endif + diff --git a/src/common/include.am b/src/common/include.am index 222afe0291..b0226802a8 100644 --- a/src/common/include.am +++ b/src/common/include.am @@ -83,6 +83,7 @@ LIBOR_A_SRC = \ src/common/backtrace.c \ src/common/compat.c \ src/common/compat_threads.c \ + src/common/compat_time.c \ src/common/container.c \ src/common/log.c \ src/common/memarea.c \ From dc6f5d1dc1ccc86f32b39b939a5f83c35865d666 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 8 Jul 2016 12:53:51 -0400 Subject: [PATCH 02/10] Basic portable monotonic timer implementation This code uses QueryPerformanceCounter() [**] on Windows, mach_absolute_time() on OSX, clock_gettime() where available, and gettimeofday() [*] elsewhere. Timer types are stored in an opaque OS-specific format; the only supported operation is to compute the difference between two timers. [*] As you know, gettimeofday() isn't monotonic, so we include a simple ratchet function to ensure that it only moves forward. [**] As you may not know, QueryPerformanceCounter() isn't actually always as monotonic as you might like it to be, so we ratchet that one too. We also include a "coarse monotonic timer" for cases where we don't actually need high-resolution time. This is GetTickCount{,64}() on Windows, clock_gettime(CLOCK_MONOTONIC_COARSE) on Linux, and falls back to regular monotonic time elsewhere. --- src/common/compat_time.c | 381 ++++++++++++++++++++++++++++++++++++++- src/common/compat_time.h | 121 +++++++++++++ 2 files changed, 501 insertions(+), 1 deletion(-) diff --git a/src/common/compat_time.c b/src/common/compat_time.c index 866c288fe6..bbbca33ca5 100644 --- a/src/common/compat_time.c +++ b/src/common/compat_time.c @@ -9,7 +9,7 @@ * timers, etc. **/ -#define COMPAT_PRIVATE +#define COMPAT_TIME_PRIVATE #include "compat.h" #ifdef _WIN32 @@ -30,6 +30,10 @@ #endif #endif +#ifdef __APPLE__ +#include +#endif + #include "torlog.h" #include "util.h" #include "container.h" @@ -108,3 +112,378 @@ tor_gettimeofday(struct timeval *timeval) return; } +#define ONE_MILLION ((int64_t) (1000 * 1000)) +#define ONE_BILLION ((int64_t) (1000 * 1000 * 1000)) + +/** True iff monotime_init has been called. */ +static int monotime_initialized = 0; + +#ifdef __APPLE__ + +/** Initialized on startup: tells is how to convert from ticks to + * nanoseconds. + */ +static struct mach_timebase_info mach_time_info; + +static void +monotime_init_internal(void) +{ + tor_assert(!monotime_initialized); + int r = mach_timebase_info(&mach_time_info); + tor_assert(r == 0); + tor_assert(mach_time_info.denom != 0); +} + +/** + * Set "out" to the most recent monotonic time value + */ +void +monotime_get(monotime_t *out) +{ + out->abstime_ = mach_absolute_time(); +} + +/** + * Return the number of nanoseconds between start and end. + */ +int64_t +monotime_diff_nsec(const monotime_t *start, + const monotime_t *end) +{ + if (BUG(mach_time_info.denom == 0)) { + monotime_init(); + } + const int64_t diff_ticks = end->abstime_ - start->abstime_; + const int64_t diff_nsec = + (diff_ticks * mach_time_info.numer) / mach_time_info.denom; + return diff_nsec; +} + +/* end of "__APPLE__" */ +#elif defined(HAVE_CLOCK_GETTIME) + +static void +monotime_init_internal(void) +{ + /* no action needed. */ +} + +void +monotime_get(monotime_t *out) +{ + int r = clock_gettime(CLOCK_MONOTONIC, &out->ts_); + tor_assert(r == 0); +} + +#ifdef CLOCK_MONOTONIC_COARSE +void +monotime_coarse_get(monotime_coarse_t *out) +{ + int r = clock_gettime(CLOCK_MONOTONIC_COARSE, &out->ts_); + tor_assert(r == 0); +} +#endif + +int64_t +monotime_diff_nsec(const monotime_t *start, + const monotime_t *end) +{ + const int64_t diff_sec = end->ts_.tv_sec - start->ts_.tv_sec; + const int64_t diff_nsec = diff_sec * ONE_BILLION + + (end->ts_.tv_nsec - start->ts_.tv_nsec); + + return diff_nsec; +} + +/* end of "HAVE_CLOCK_GETTIME" */ +#elif defined (_WIN32) + +/** Result of QueryPerformanceFrequency, as an int64_t. */ +static int64_t ticks_per_second = 0; + +/** Protected by lock: last value returned by monotime_get(). */ +static int64_t last_pctr = 0; +/** Protected by lock: offset we must add to monotonic time values. */ +static int64_t pctr_offset = 0; +/** Lock to protect last_pctr and pctr_offset */ +static CRITICAL_SECTION monotime_lock; +/* If we are using GetTickCount(), how many times has it rolled over? */ +static uint32_t rollover_count = 0; +/* If we are using GetTickCount(), what's the last value it returned? */ +static int64_t last_tick_count = 0; +/** Lock to protect rollover_count and */ +static CRITICAL_SECTION monotime_coarse_lock; + +typedef ULONGLONG (WINAPI *GetTickCount64_fn_t)(void); +static GetTickCount64_fn_t GetTickCount64_fn = NULL; + +static void +monotime_init_internal(void) +{ + tor_assert(!monotime_initialized); + BOOL ok = InitializeCriticalSectionAndSpinCount(&monotime_lock, 200); + tor_assert(ok); + ok = InitializeCriticalSectionAndSpinCount(&monotime_coarse_lock, 200); + tor_assert(ok); + LARGE_INTEGER li; + ok = QueryPerformanceFrequency(&li); + tor_assert(ok); + tor_assert(li.QuadPart); + ticks_per_second = li.QuadPart; + last_pctr = 0; + pctr_offset = 0; + + HANDLE h = load_windows_system_library(TEXT("kernel32.dll")); + if (h) { + GetTickCount64_fn = (GetTickCount64_fn_t) + GetProcAddress(h, "GetTickCount64"); + } + // FreeLibrary(h) ? +} + +/** Helper for windows: Called with a sequence of times that are supposed + * to be monotonic; increments them as appropriate so that they actually + * _are_ monotonic. + * + * Caller must hold lock. */ +STATIC int64_t +ratchet_performance_counter(int64_t count_raw) +{ + /* must hold lock */ + const int64_t count_adjusted = count_raw + pctr_offset; + + if (PREDICT_UNLIKELY(count_adjusted < last_pctr)) { + /* Monotonicity failed! Pretend no time elapsed. */ + pctr_offset = last_pctr - count_raw; + return last_pctr; + } else { + last_pctr = count_adjusted; + return count_adjusted; + } +} + +STATIC int64_t +ratchet_coarse_performance_counter(const int64_t count_raw) +{ + int64_t count = count_raw + (((int64_t)rollover_count) << 32); + while (PREDICT_UNLIKELY(count < last_tick_count)) { + ++rollover_count; + count = count_raw + (((int64_t)rollover_count) << 32); + } + last_tick_count = count; + return count; +} + +void +monotime_get(monotime_t *out) +{ + if (BUG(monotime_initialized == 0)) { + monotime_init(); + } + + /* Alas, QueryPerformanceCounter is not always monotonic: see bug list at + + https://www.python.org/dev/peps/pep-0418/#windows-queryperformancecounter + */ + + EnterCriticalSection(&monotime_lock); + LARGE_INTEGER res; + BOOL ok = QueryPerformanceCounter(&res); + tor_assert(ok); + const int64_t count_raw = res.QuadPart; + out->pcount_ = ratchet_performance_counter(count_raw); + LeaveCriticalSection(&monotime_lock); +} + +void +monotime_coarse_get(monotime_coarse_t *out) +{ + if (GetTickCount64_fn) { + out->tick_count_ = (int64_t)GetTickCount64_fn(); + } else { + EnterCriticalSection(&monotime_coarse_lock); + DWORD tick = GetTickCount(); + out->tick_count_ = ratchet_coarse_performance_counter(tick); + LeaveCriticalSection(&monotime_coarse_lock); + } +} + +int64_t +monotime_diff_nsec(const monotime_t *start, + const monotime_t *end) +{ + if (BUG(monotime_initialized == 0)) { + monotime_init(); + } + const int64_t diff_ticks = end->pcount_ - start->pcount_; + return (diff_ticks * ONE_BILLION) / ticks_per_second; +} + +int64_t +monotime_coarse_diff_msec(const monotime_coarse_t *start, + const monotime_coarse_t *end) +{ + const int64_t diff_ticks = end->tick_count_ - start->tick_count_; + return diff_ticks; +} + +int64_t +monotime_coarse_diff_usec(const monotime_coarse_t *start, + const monotime_coarse_t *end) +{ + return monotime_coarse_diff_msec(start, end) * 1000; +} + +int64_t +monotime_coarse_diff_nsec(const monotime_coarse_t *start, + const monotime_coarse_t *end) +{ + return monotime_coarse_diff_msec(start, end) * ONE_MILLION; +} + +/* end of "_WIN32" */ +#elif defined(MONOTIME_USING_GETTIMEOFDAY) + +static int monotime_initialized = 0; +static struct timeval last_timeofday = { 0, 0 }; +static struct timeval timeofday_offset = { 0, 0 }; +static tor_mutex_t monotime_lock; + +/** Initialize the monotonic timer subsystem. */ +static void +monotime_init_internal(void) +{ + tor_assert(!monotime_initialized); + tor_mutex_init(&monotime_lock); +} + +/** Helper for gettimeofday(): Called with a sequence of times that are + * supposed to be monotonic; increments them as appropriate so that they + * actually _are_ monotonic. + * + * Caller must hold lock. */ +STATIC void +ratchet_timeval(const struct timeval *timeval_raw, struct timeval *out) +{ + /* must hold lock */ + timeradd(timeval_raw, &timeofday_offset, out); + if (PREDICT_UNLIKELY(timercmp(out, &last_timeofday, <))) { + /* time ran backwards. Instead, declare that no time occurred. */ + timersub(&last_timeofday, timeval_raw, &timeofday_offset); + memcpy(out, &last_timeofday, sizeof(struct timeval)); + } else { + memcpy(&last_timeofday, out, sizeof(struct timeval)); + } +} + +void +monotime_get(monotime_t *out) +{ + if (BUG(monotime_initialized == 0)) { + monotime_init(); + } + + tor_mutex_acquire(&monotime_lock); + struct timeval timeval_raw; + tor_gettimeofday(&timeval_raw); + ratchet_timeval(&timeval_raw, &out->tv_); + tor_mutex_release(&monotime_lock); +} + +int64_t +monotime_diff_nsec(const monotime_t *start, + const monotime_t *end) +{ + struct timeval diff; + timersub(&end->tv_, &start->tv_, &diff); + return (diff.tv_sec * ONE_BILLION + diff.tv_usec * 1000); +} + +/* end of "MONOTIME_USING_GETTIMEOFDAY" */ +#else +#error "No way to implement monotonic timers." +#endif + +static monotime_t initialized_at; +#ifdef MONOTIME_COARSE_FN_IS_DIFFERENT +static monotime_coarse_t initialized_at_coarse; +#endif + +/** + * Initialize the monotonic timer subsystem. Must be called before any + * monotonic timer functions. This function is idempotent. + */ +void +monotime_init(void) +{ + if (!monotime_initialized) { + monotime_init_internal(); + monotime_get(&initialized_at); +#ifdef MONOTIME_COARSE_FN_IS_DIFFERENT + monotime_coarse_get(&initialized_at_coarse); +#endif + monotime_initialized = 1; + } +} + +int64_t +monotime_diff_usec(const monotime_t *start, + const monotime_t *end) +{ + const int64_t nsec = monotime_diff_nsec(start, end); + return CEIL_DIV(nsec, 1000); +} + +int64_t +monotime_diff_msec(const monotime_t *start, + const monotime_t *end) +{ + const int64_t nsec = monotime_diff_nsec(start, end); + return CEIL_DIV(nsec, ONE_MILLION); +} + +uint64_t +monotime_absolute_nsec(void) +{ + monotime_t now; + monotime_get(&now); + return monotime_diff_nsec(&initialized_at, &now); +} + +uint64_t +monotime_absolute_usec(void) +{ + return monotime_absolute_nsec() / 1000; +} + +uint64_t +monotime_absolute_msec(void) +{ + return monotime_absolute_nsec() / ONE_MILLION; +} + +#ifdef MONOTIME_COARSE_FN_IS_DIFFERENT +uint64_t +monotime_coarse_absolute_nsec(void) +{ + monotime_coarse_t now; + monotime_coarse_get(&now); + return monotime_coarse_diff_nsec(&initialized_at_coarse, &now); +} + +uint64_t +monotime_coarse_absolute_usec(void) +{ + monotime_coarse_t now; + monotime_coarse_get(&now); + return monotime_coarse_diff_usec(&initialized_at_coarse, &now); +} + +uint64_t +monotime_coarse_absolute_msec(void) +{ + monotime_coarse_t now; + monotime_coarse_get(&now); + return monotime_coarse_diff_msec(&initialized_at_coarse, &now); +} +#endif diff --git a/src/common/compat_time.h b/src/common/compat_time.h index 56126ba84c..d69af8f98c 100644 --- a/src/common/compat_time.h +++ b/src/common/compat_time.h @@ -3,11 +3,28 @@ * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * \file compat_time.h + * + * \brief Functions and types for monotonic times. + * + * monotime_* functions try to provide a high-resolution monotonic timer with + * something the best resolution the system provides. monotime_coarse_* + * functions run faster (if the operating system gives us a way to do that) + * but produce a less accurate timer: accuracy will probably be on the order + * of tens of milliseconds. + */ + #ifndef TOR_COMPAT_TIME_H #define TOR_COMPAT_TIME_H #include "orconfig.h" +#if defined(HAVE_CLOCK_GETTIME) +/* to ensure definition of CLOCK_MONOTONIC_COARSE if it's there */ +#include +#endif + #if !defined(HAVE_GETTIMEOFDAY) && !defined(HAVE_STRUCT_TIMEVAL_TV_SEC) /** Implementation of timeval for platforms that don't have it. */ struct timeval { @@ -16,11 +33,115 @@ struct timeval { }; #endif +/** Represents a monotonic timer in a platform-dependent way. */ +typedef struct monotime_t { +#ifdef __APPLE__ + /* On apple, there is a 64-bit counter whose precision we must look up. */ + uint64_t abstime_; +#elif defined(HAVE_CLOCK_GETTIME) + /* It sure would be nice to use clock_gettime(). Posix is a nice thing. */ + struct timespec ts_; +#elif defined (_WIN32) + /* On Windows, there is a 64-bit counter whose precision we must look up. */ + int64_t pcount_; +#else +#define MONOTIME_USING_GETTIMEOFDAY + /* Otherwise, we will be stuck using gettimeofday. */ + struct timeval tv_; +#endif +} monotime_t; + +#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC_COARSE) +#define MONOTIME_COARSE_FN_IS_DIFFERENT +#define monotime_coarse_t monotime_t +#elif defined(_WIN32) +#define MONOTIME_COARSE_FN_IS_DIFFERENT +#define MONOTIME_COARSE_TYPE_IS_DIFFERENT +/** Represents a coarse monotonic time in a platform-independent way. */ +typedef struct monotime_coarse_t { + uint64_t tick_count_; +} monotime_coarse_t; +#else +#define monotime_coarse_t monotime_t +#endif + +/** + * Initialize the timing subsystem. This function is idempotent. + */ +void monotime_init(void); +/** + * Set out to the current time. + */ +void monotime_get(monotime_t *out); +/** + * Return the number of nanoseconds between start and end. + */ +int64_t monotime_diff_nsec(const monotime_t *start, const monotime_t *end); +/** + * Return the number of microseconds between start and end. + */ +int64_t monotime_diff_usec(const monotime_t *start, const monotime_t *end); +/** + * Return the number of milliseconds between start and end. + */ +int64_t monotime_diff_msec(const monotime_t *start, const monotime_t *end); +/** + * Return the number of nanoseconds since the timer system was initialized. + */ +uint64_t monotime_absolute_nsec(void); +/** + * Return the number of microseconds since the timer system was initialized. + */ +uint64_t monotime_absolute_usec(void); +/** + * Return the number of milliseconds since the timer system was initialized. + */ +uint64_t monotime_absolute_msec(void); + +#if defined(MONOTIME_COARSE_FN_IS_DIFFERENT) +/** + * Set out to the current coarse time. + */ +void monotime_coarse_get(monotime_coarse_t *out); +uint64_t monotime_coarse_absolute_nsec(void); +uint64_t monotime_coarse_absolute_usec(void); +uint64_t monotime_coarse_absolute_msec(void); +#else +#define monotime_coarse_get monotime_get +#define monotime_coarse_absolute_nsec monotime_absolute_nsec +#define monotime_coarse_absolute_usec monotime_absolute_usec +#define monotime_coarse_absolute_msec monotime_absolute_msec +#endif + +#if defined(MONOTIME_COARSE_TYPE_IS_DIFFERENT) +int64_t monotime_coarse_diff_nsec(const monotime_coarse_t *start, + const monotime_coarse_t *end); +int64_t monotime_coarse_diff_usec(const monotime_coarse_t *start, + const monotime_coarse_t *end); +int64_t monotime_coarse_diff_msec(const monotime_coarse_t *start, + const monotime_coarse_t *end); +#else +#define monotime_coarse_diff_nsec monotime_diff_nsec +#define monotime_coarse_diff_usec monotime_diff_usec +#define monotime_coarse_diff_msec monotime_diff_msec +#endif + void tor_gettimeofday(struct timeval *timeval); #ifdef TOR_UNIT_TESTS void tor_sleep_msec(int msec); #endif +#ifdef COMPAT_TIME_PRIVATE +#ifdef _WIN32 +STATIC int64_t ratchet_performance_counter(int64_t count_raw); +STATIC int64_t ratchet_coarse_performance_counter(int64_t count_raw); +#endif +#ifdef MONOTIME_USING_GETTIMEOFDAY +STATIC void ratchet_timeval(const struct timeval *timeval_raw, + struct timeval *out); +#endif +#endif + #endif From 6a2002fc091506f576934bbf50e7b8644b43bf4c Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 8 Jul 2016 14:38:02 -0400 Subject: [PATCH 03/10] convert timers.c to use real monotonic time. --- src/common/timers.c | 50 +++++++++++++++++++++--------------------- src/common/timers.h | 3 ++- src/test/test-timers.c | 20 ++++++++++++++--- 3 files changed, 44 insertions(+), 29 deletions(-) diff --git a/src/common/timers.c b/src/common/timers.c index 2eb75a148b..41b2008ac4 100644 --- a/src/common/timers.c +++ b/src/common/timers.c @@ -18,9 +18,6 @@ */ /* Notes: - * - * The use of tor_gettimeofday_cached_monotonic() is kind of ugly. It would - * be neat to fix it. * * Having a way to free all timers on shutdown would free people from the * need to track them. Not sure if that's clever though. @@ -72,6 +69,8 @@ struct timeout_cb { static struct timeouts *global_timeouts = NULL; static struct event *global_timer_event = NULL; +static monotime_t start_of_time; + /** We need to choose this value carefully. Because we're using timer wheels, * it actually costs us to have extra resolution we don't use. So for now, * I'm going to define our resolution as .1 msec, and hope that's good enough. @@ -95,9 +94,8 @@ static struct event *global_timer_event = NULL; /** * Convert the timeval in tv to a timeout_t, and return it. * - * The output resolution is set by USEC_PER_TICK, and the time corresponding - * to 0 is the same as the time corresponding to 0 from - * tor_gettimeofday_cached_monotonic(). + * The output resolution is set by USEC_PER_TICK. Only use this to convert + * delays to number of ticks; the time represented by 0 is undefined. */ static timeout_t tv_to_timeout(const struct timeval *tv) @@ -108,7 +106,8 @@ tv_to_timeout(const struct timeval *tv) } /** - * Convert the timeout in t to a timeval in tv_out + * Convert the timeout in t to a timeval in tv_out. Only + * use this for delays, not absolute times. */ static void timeout_to_tv(timeout_t t, struct timeval *tv_out) @@ -122,12 +121,10 @@ timeout_to_tv(timeout_t t, struct timeval *tv_out) * Update the timer tv to the current time in tv. */ static void -timer_advance_to_cur_time(const struct timeval *tv) +timer_advance_to_cur_time(const monotime_t *now) { - timeout_t cur_tick = tv_to_timeout(tv); - if (BUG(cur_tick < timeouts_get_curtime(global_timeouts))) { - cur_tick = timeouts_get_curtime(global_timeouts); // LCOV_EXCL_LINE - } + timeout_t cur_tick = CEIL_DIV(monotime_diff_usec(&start_of_time, now), + USEC_PER_TICK); timeouts_update(global_timeouts, cur_tick); } @@ -138,11 +135,12 @@ timer_advance_to_cur_time(const struct timeval *tv) static void libevent_timer_reschedule(void) { - struct timeval now; - tor_gettimeofday_cached_monotonic(&now); + monotime_t now; + monotime_get(&now); timer_advance_to_cur_time(&now); timeout_t delay = timeouts_timeout(global_timeouts); + struct timeval d; if (delay > MIN_CHECK_TICKS) delay = MIN_CHECK_TICKS; @@ -161,9 +159,8 @@ libevent_timer_callback(evutil_socket_t fd, short what, void *arg) (void)what; (void)arg; - struct timeval now; - tor_gettimeofday_cache_clear(); - tor_gettimeofday_cached_monotonic(&now); + monotime_t now; + monotime_get(&now); timer_advance_to_cur_time(&now); tor_timer_t *t; @@ -171,7 +168,6 @@ libevent_timer_callback(evutil_socket_t fd, short what, void *arg) t->callback.cb(t, t->callback.arg, &now); } - tor_gettimeofday_cache_clear(); libevent_timer_reschedule(); } @@ -194,6 +190,9 @@ timers_initialize(void) // LCOV_EXCL_STOP } + monotime_init(); + monotime_get(&start_of_time); + struct event *timer_event; timer_event = tor_event_new(tor_libevent_get_base(), -1, 0, libevent_timer_callback, NULL); @@ -256,24 +255,25 @@ timer_set_cb(tor_timer_t *t, timer_cb_fn_t cb, void *arg) } /** - * Schedule the timer t to fire at the current time plus a delay of tv. - * All times are relative to tor_gettimeofday_cached_monotonic. + * Schedule the timer t to fire at the current time plus a delay of + * delay microseconds. All times are relative to monotime_get(). */ void timer_schedule(tor_timer_t *t, const struct timeval *tv) { - const timeout_t when = tv_to_timeout(tv); - struct timeval now; - tor_gettimeofday_cached_monotonic(&now); + const timeout_t delay = tv_to_timeout(tv); + + monotime_t now; + monotime_get(&now); timer_advance_to_cur_time(&now); /* Take the old timeout value. */ timeout_t to = timeouts_timeout(global_timeouts); - timeouts_add(global_timeouts, t, when); + timeouts_add(global_timeouts, t, delay); /* Should we update the libevent timer? */ - if (to <= when) { + if (to <= delay) { return; /* we're already going to fire before this timer would trigger. */ } libevent_timer_reschedule(); diff --git a/src/common/timers.h b/src/common/timers.h index 594cf38a64..2c36af172d 100644 --- a/src/common/timers.h +++ b/src/common/timers.h @@ -7,8 +7,9 @@ #include "orconfig.h" #include "testsupport.h" +struct monotime_t; typedef struct timeout tor_timer_t; -typedef void (*timer_cb_fn_t)(tor_timer_t *, void *, const struct timeval *); +typedef void (*timer_cb_fn_t)(tor_timer_t *, void *, const struct monotime_t *); tor_timer_t *timer_new(timer_cb_fn_t cb, void *arg); void timer_set_cb(tor_timer_t *t, timer_cb_fn_t cb, void *arg); void timer_schedule(tor_timer_t *t, const struct timeval *delay); diff --git a/src/test/test-timers.c b/src/test/test-timers.c index 1189fd8792..b5fcade7f8 100644 --- a/src/test/test-timers.c +++ b/src/test/test-timers.c @@ -28,15 +28,26 @@ static tor_timer_t *timers[N_TIMERS] = {NULL}; static int n_active_timers = 0; static int n_fired = 0; +static monotime_t started_at; +static int64_t delay_usec[N_TIMERS]; +static int64_t diffs_mono_usec[N_TIMERS]; + static void -timer_cb(tor_timer_t *t, void *arg, const struct timeval *now) +timer_cb(tor_timer_t *t, void *arg, const monotime_t *now_mono) { + struct timeval now; + + tor_gettimeofday(&now); tor_timer_t **t_ptr = arg; tor_assert(*t_ptr == t); int idx = (int) (t_ptr - timers); ++fired[idx]; - timersub(now, &fire_at[idx], &difference[idx]); + timersub(&now, &fire_at[idx], &difference[idx]); + diffs_mono_usec[idx] = + monotime_diff_usec(&started_at, now_mono) - + delay_usec[idx]; ++n_fired; + // printf("%d / %d\n",n_fired, N_TIMERS); if (n_fired == n_active_timers) { event_base_loopbreak(tor_libevent_get_base()); @@ -57,10 +68,12 @@ main(int argc, char **argv) int ret; struct timeval now; tor_gettimeofday(&now); + monotime_get(&started_at); for (i = 0; i < N_TIMERS; ++i) { struct timeval delay; delay.tv_sec = crypto_rand_int_range(0,MAX_DURATION); delay.tv_usec = crypto_rand_int_range(0,1000000); + delay_usec[i] = delay.tv_sec * 1000000 + delay.tv_usec; timeradd(&now, &delay, &fire_at[i]); timers[i] = timer_new(timer_cb, &timers[i]); timer_schedule(timers[i], &delay); @@ -88,7 +101,8 @@ main(int argc, char **argv) continue; } tor_assert(fired[i] == 1); - int64_t diff = difference[i].tv_usec + difference[i].tv_sec * 1000000; + //int64_t diff = difference[i].tv_usec + difference[i].tv_sec * 1000000; + int64_t diff = diffs_mono_usec[i]; total_difference += diff; total_square_difference += diff*diff; } From c7558c906a1069308f8f4519b8c93245fbca9efd Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 8 Jul 2016 15:24:21 -0400 Subject: [PATCH 04/10] Use coarse monotonic timer instead of cached monotonized libevent time. --- src/or/buffers.c | 9 ++++----- src/or/circuitlist.c | 6 ++---- src/or/relay.c | 4 +--- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/or/buffers.c b/src/or/buffers.c index 8b9a53c699..970d17ee41 100644 --- a/src/or/buffers.c +++ b/src/or/buffers.c @@ -405,7 +405,7 @@ static chunk_t * buf_add_chunk_with_capacity(buf_t *buf, size_t capacity, int capped) { chunk_t *chunk; - struct timeval now; + if (CHUNK_ALLOC_SIZE(capacity) < buf->default_chunk_size) { chunk = chunk_new_with_alloc_size(buf->default_chunk_size); } else if (capped && CHUNK_ALLOC_SIZE(capacity) > MAX_CHUNK_ALLOC) { @@ -414,8 +414,7 @@ buf_add_chunk_with_capacity(buf_t *buf, size_t capacity, int capped) chunk = chunk_new_with_alloc_size(preferred_chunk_size(capacity)); } - tor_gettimeofday_cached_monotonic(&now); - chunk->inserted_time = (uint32_t)tv_to_msec(&now); + chunk->inserted_time = (uint32_t)monotime_coarse_absolute_msec(); if (buf->tail) { tor_assert(buf->head); @@ -430,8 +429,8 @@ buf_add_chunk_with_capacity(buf_t *buf, size_t capacity, int capped) } /** Return the age of the oldest chunk in the buffer buf, in - * milliseconds. Requires the current time, in truncated milliseconds since - * the epoch, as its input now. + * milliseconds. Requires the current monotonic time, in truncated msec, + * as its input now. */ uint32_t buf_get_oldest_chunk_timestamp(const buf_t *buf, uint32_t now) diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index d2ba7d4781..5c691644a4 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -2015,7 +2015,7 @@ circuit_max_queued_cell_age(const circuit_t *c, uint32_t now) /** Return the age in milliseconds of the oldest buffer chunk on conn, * where age is taken in milliseconds before the time now (in truncated - * milliseconds since the epoch). If the connection has no data, treat + * absolute monotonic msec). If the connection has no data, treat * it as having age zero. **/ static uint32_t @@ -2138,7 +2138,6 @@ circuits_handle_oom(size_t current_allocation) size_t mem_recovered=0; int n_circuits_killed=0; int n_dirconns_killed=0; - struct timeval now; uint32_t now_ms; log_notice(LD_GENERAL, "We're low on memory. Killing circuits with " "over-long queues. (This behavior is controlled by " @@ -2152,8 +2151,7 @@ circuits_handle_oom(size_t current_allocation) mem_to_recover = current_allocation - mem_target; } - tor_gettimeofday_cached_monotonic(&now); - now_ms = (uint32_t)tv_to_msec(&now); + now_ms = (uint32_t)monotime_coarse_absolute_msec(); circlist = circuit_get_global_list(); SMARTLIST_FOREACH_BEGIN(circlist, circuit_t *, circ) { diff --git a/src/or/relay.c b/src/or/relay.c index fb8c8e74d6..a815a55d69 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -2320,14 +2320,12 @@ cell_queue_append_packed_copy(circuit_t *circ, cell_queue_t *queue, int exitward, const cell_t *cell, int wide_circ_ids, int use_stats) { - struct timeval now; packed_cell_t *copy = packed_cell_copy(cell, wide_circ_ids); (void)circ; (void)exitward; (void)use_stats; - tor_gettimeofday_cached_monotonic(&now); - copy->inserted_time = (uint32_t)tv_to_msec(&now); + copy->inserted_time = (uint32_t) monotime_coarse_absolute_msec(); cell_queue_append(queue, copy); } From 7bc4ca7de9d434edeb21525917c81d6963a26511 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 13 Jul 2016 09:28:46 -0400 Subject: [PATCH 05/10] Remove tor_gettimeofday_cached_monotonic as broken and unneeded --- src/common/compat_libevent.c | 30 ------------------------------ src/common/compat_libevent.h | 1 - 2 files changed, 31 deletions(-) diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c index c5f73dc51a..4dab54493f 100644 --- a/src/common/compat_libevent.c +++ b/src/common/compat_libevent.c @@ -388,33 +388,3 @@ tor_gettimeofday_cache_set(const struct timeval *tv) #endif #endif -/** - * As tor_gettimeofday_cached, but can never move backwards in time. - * - * The returned value may diverge from wall-clock time, since wall-clock time - * can trivially be adjusted backwards, and this can't. Don't mix wall-clock - * time with these values in the same calculation. - * - * Depending on implementation, this function may or may not "smooth out" huge - * jumps forward in wall-clock time. It may or may not keep its results - * advancing forward (as opposed to stalling) if the wall-clock time goes - * backwards. The current implementation does neither of of these. - * - * This function is not thread-safe; do not call it outside the main thread. - * - * In future versions of Tor, this may return a time does not have its - * origin at the Unix epoch. - */ -void -tor_gettimeofday_cached_monotonic(struct timeval *tv) -{ - struct timeval last_tv = { 0, 0 }; - - tor_gettimeofday_cached(tv); - if (timercmp(tv, &last_tv, OP_LT)) { - memcpy(tv, &last_tv, sizeof(struct timeval)); - } else { - memcpy(&last_tv, tv, sizeof(struct timeval)); - } -} - diff --git a/src/common/compat_libevent.h b/src/common/compat_libevent.h index 89b35c9faa..e64095bbfa 100644 --- a/src/common/compat_libevent.h +++ b/src/common/compat_libevent.h @@ -70,7 +70,6 @@ void tor_gettimeofday_cache_clear(void); #ifdef TOR_UNIT_TESTS void tor_gettimeofday_cache_set(const struct timeval *tv); #endif -void tor_gettimeofday_cached_monotonic(struct timeval *tv); #ifdef COMPAT_LIBEVENT_PRIVATE From 2a217ef72392d685e6c9b48b2be3b467a8244b48 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 13 Jul 2016 10:18:15 -0400 Subject: [PATCH 06/10] Expose monotonic time ratchet functions for testing. --- src/common/compat_time.c | 156 +++++++++++++++++++++------------------ src/common/compat_time.h | 7 +- 2 files changed, 91 insertions(+), 72 deletions(-) diff --git a/src/common/compat_time.c b/src/common/compat_time.c index bbbca33ca5..de3956685a 100644 --- a/src/common/compat_time.c +++ b/src/common/compat_time.c @@ -118,6 +118,89 @@ tor_gettimeofday(struct timeval *timeval) /** True iff monotime_init has been called. */ static int monotime_initialized = 0; +/* "ratchet" functions for monotonic time. */ + +#if defined(_WIN32) || defined(TOR_UNIT_TESTS) + +/** Protected by lock: last value returned by monotime_get(). */ +static int64_t last_pctr = 0; +/** Protected by lock: offset we must add to monotonic time values. */ +static int64_t pctr_offset = 0; +/* If we are using GetTickCount(), how many times has it rolled over? */ +static uint32_t rollover_count = 0; +/* If we are using GetTickCount(), what's the last value it returned? */ +static int64_t last_tick_count = 0; + +/** Helper for windows: Called with a sequence of times that are supposed + * to be monotonic; increments them as appropriate so that they actually + * _are_ monotonic. + * + * Caller must hold lock. */ +STATIC int64_t +ratchet_performance_counter(int64_t count_raw) +{ + /* must hold lock */ + const int64_t count_adjusted = count_raw + pctr_offset; + + if (PREDICT_UNLIKELY(count_adjusted < last_pctr)) { + /* Monotonicity failed! Pretend no time elapsed. */ + pctr_offset = last_pctr - count_raw; + return last_pctr; + } else { + last_pctr = count_adjusted; + return count_adjusted; + } +} + +STATIC int64_t +ratchet_coarse_performance_counter(const int64_t count_raw) +{ + int64_t count = count_raw + (((int64_t)rollover_count) << 32); + while (PREDICT_UNLIKELY(count < last_tick_count)) { + ++rollover_count; + count = count_raw + (((int64_t)rollover_count) << 32); + } + last_tick_count = count; + return count; +} +#endif + +#if defined(MONOTIME_USING_GETTIMEOFDAY) || defined(TOR_UNIT_TESTS) +static struct timeval last_timeofday = { 0, 0 }; +static struct timeval timeofday_offset = { 0, 0 }; + +/** Helper for gettimeofday(): Called with a sequence of times that are + * supposed to be monotonic; increments them as appropriate so that they + * actually _are_ monotonic. + * + * Caller must hold lock. */ +STATIC void +ratchet_timeval(const struct timeval *timeval_raw, struct timeval *out) +{ + /* must hold lock */ + timeradd(timeval_raw, &timeofday_offset, out); + if (PREDICT_UNLIKELY(timercmp(out, &last_timeofday, <))) { + /* time ran backwards. Instead, declare that no time occurred. */ + timersub(&last_timeofday, timeval_raw, &timeofday_offset); + memcpy(out, &last_timeofday, sizeof(struct timeval)); + } else { + memcpy(&last_timeofday, out, sizeof(struct timeval)); + } +} +#endif + +#ifdef TOR_UNIT_TESTS +/** For testing: reset all the ratchets */ +void +monotime_reset_ratchets_for_testing(void) +{ + last_pctr = pctr_offset = last_tick_count = 0; + rollover_count = 0; + memset(&last_timeofday, 0, sizeof(struct timeval)); + memset(&timeofday_offset, 0, sizeof(struct timeval)); +} +#endif + #ifdef __APPLE__ /** Initialized on startup: tells is how to convert from ticks to @@ -201,17 +284,9 @@ monotime_diff_nsec(const monotime_t *start, /** Result of QueryPerformanceFrequency, as an int64_t. */ static int64_t ticks_per_second = 0; -/** Protected by lock: last value returned by monotime_get(). */ -static int64_t last_pctr = 0; -/** Protected by lock: offset we must add to monotonic time values. */ -static int64_t pctr_offset = 0; /** Lock to protect last_pctr and pctr_offset */ static CRITICAL_SECTION monotime_lock; -/* If we are using GetTickCount(), how many times has it rolled over? */ -static uint32_t rollover_count = 0; -/* If we are using GetTickCount(), what's the last value it returned? */ -static int64_t last_tick_count = 0; -/** Lock to protect rollover_count and */ +/** Lock to protect rollover_count and last_tick_count */ static CRITICAL_SECTION monotime_coarse_lock; typedef ULONGLONG (WINAPI *GetTickCount64_fn_t)(void); @@ -241,39 +316,6 @@ monotime_init_internal(void) // FreeLibrary(h) ? } -/** Helper for windows: Called with a sequence of times that are supposed - * to be monotonic; increments them as appropriate so that they actually - * _are_ monotonic. - * - * Caller must hold lock. */ -STATIC int64_t -ratchet_performance_counter(int64_t count_raw) -{ - /* must hold lock */ - const int64_t count_adjusted = count_raw + pctr_offset; - - if (PREDICT_UNLIKELY(count_adjusted < last_pctr)) { - /* Monotonicity failed! Pretend no time elapsed. */ - pctr_offset = last_pctr - count_raw; - return last_pctr; - } else { - last_pctr = count_adjusted; - return count_adjusted; - } -} - -STATIC int64_t -ratchet_coarse_performance_counter(const int64_t count_raw) -{ - int64_t count = count_raw + (((int64_t)rollover_count) << 32); - while (PREDICT_UNLIKELY(count < last_tick_count)) { - ++rollover_count; - count = count_raw + (((int64_t)rollover_count) << 32); - } - last_tick_count = count; - return count; -} - void monotime_get(monotime_t *out) { @@ -344,9 +386,6 @@ monotime_coarse_diff_nsec(const monotime_coarse_t *start, /* end of "_WIN32" */ #elif defined(MONOTIME_USING_GETTIMEOFDAY) -static int monotime_initialized = 0; -static struct timeval last_timeofday = { 0, 0 }; -static struct timeval timeofday_offset = { 0, 0 }; static tor_mutex_t monotime_lock; /** Initialize the monotonic timer subsystem. */ @@ -357,25 +396,6 @@ monotime_init_internal(void) tor_mutex_init(&monotime_lock); } -/** Helper for gettimeofday(): Called with a sequence of times that are - * supposed to be monotonic; increments them as appropriate so that they - * actually _are_ monotonic. - * - * Caller must hold lock. */ -STATIC void -ratchet_timeval(const struct timeval *timeval_raw, struct timeval *out) -{ - /* must hold lock */ - timeradd(timeval_raw, &timeofday_offset, out); - if (PREDICT_UNLIKELY(timercmp(out, &last_timeofday, <))) { - /* time ran backwards. Instead, declare that no time occurred. */ - timersub(&last_timeofday, timeval_raw, &timeofday_offset); - memcpy(out, &last_timeofday, sizeof(struct timeval)); - } else { - memcpy(&last_timeofday, out, sizeof(struct timeval)); - } -} - void monotime_get(monotime_t *out) { @@ -474,16 +494,12 @@ monotime_coarse_absolute_nsec(void) uint64_t monotime_coarse_absolute_usec(void) { - monotime_coarse_t now; - monotime_coarse_get(&now); - return monotime_coarse_diff_usec(&initialized_at_coarse, &now); + return monotime_coarse_absolute_nsec() / 1000; } uint64_t monotime_coarse_absolute_msec(void) { - monotime_coarse_t now; - monotime_coarse_get(&now); - return monotime_coarse_diff_msec(&initialized_at_coarse, &now); + return monotime_coarse_absolute_nsec() / ONE_MILLION; } #endif diff --git a/src/common/compat_time.h b/src/common/compat_time.h index d69af8f98c..8d61bc2580 100644 --- a/src/common/compat_time.h +++ b/src/common/compat_time.h @@ -133,14 +133,17 @@ void tor_sleep_msec(int msec); #endif #ifdef COMPAT_TIME_PRIVATE -#ifdef _WIN32 +#if defined(_WIN32) || defined(TOR_UNIT_TESTS) STATIC int64_t ratchet_performance_counter(int64_t count_raw); STATIC int64_t ratchet_coarse_performance_counter(int64_t count_raw); #endif -#ifdef MONOTIME_USING_GETTIMEOFDAY +#if defined(MONOTIME_USING_GETTIMEOFDAY) || defined(TOR_UNIT_TESTS) STATIC void ratchet_timeval(const struct timeval *timeval_raw, struct timeval *out); #endif +#ifdef TOR_UNIT_TESTS +void monotime_reset_ratchets_for_testing(void); +#endif #endif #endif From abcb8ce25dbd2d76d3b84ea2aacbfc7d69d41eee Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 13 Jul 2016 10:23:00 -0400 Subject: [PATCH 07/10] Unit tests for monotonic time --- src/test/test_util.c | 125 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/src/test/test_util.c b/src/test/test_util.c index 19763fb737..38df7bb445 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -5,6 +5,7 @@ #include "orconfig.h" #define COMPAT_PRIVATE +#define COMPAT_TIME_PRIVATE #define CONTROL_PRIVATE #define UTIL_PRIVATE #include "or.h" @@ -5285,6 +5286,128 @@ test_util_calloc_check(void *arg) ; } +static void +test_util_monotonic_time(void *arg) +{ + (void)arg; + + monotime_t mt1, mt2; + monotime_coarse_t mtc1, mtc2; + uint64_t nsec1, nsec2, usec1, msec1; + uint64_t nsecc1, nsecc2, usecc1, msecc1; + + monotime_get(&mt1); + monotime_coarse_get(&mtc1); + nsec1 = monotime_absolute_nsec(); + usec1 = monotime_absolute_usec(); + msec1 = monotime_absolute_msec(); + nsecc1 = monotime_coarse_absolute_nsec(); + usecc1 = monotime_coarse_absolute_usec(); + msecc1 = monotime_coarse_absolute_msec(); + + tor_sleep_msec(200); + + monotime_get(&mt2); + monotime_coarse_get(&mtc2); + nsec2 = monotime_absolute_nsec(); + nsecc2 = monotime_coarse_absolute_nsec(); + + /* We need to be a little careful here since we don't know the system load. + */ + tt_i64_op(monotime_diff_msec(&mt1, &mt2), OP_GE, 175); + tt_i64_op(monotime_diff_msec(&mt1, &mt2), OP_LT, 1000); + tt_i64_op(monotime_coarse_diff_msec(&mtc1, &mtc2), OP_GE, 125); + tt_i64_op(monotime_coarse_diff_msec(&mtc1, &mtc2), OP_LT, 1000); + tt_u64_op(nsec2-nsec1, OP_GE, 175000000); + tt_u64_op(nsec2-nsec1, OP_LT, 1000000000); + tt_u64_op(nsecc2-nsecc1, OP_GE, 125000000); + tt_u64_op(nsecc2-nsecc1, OP_LT, 1000000000); + + tt_u64_op(msec1, OP_GE, nsec1 / 1000000); + tt_u64_op(usec1, OP_GE, nsec1 / 1000); + tt_u64_op(msecc1, OP_GE, nsecc1 / 1000000); + tt_u64_op(usecc1, OP_GE, nsecc1 / 1000); + tt_u64_op(msec1, OP_LE, nsec1 / 1000000 + 1); + tt_u64_op(usec1, OP_LE, nsec1 / 1000 +10); + tt_u64_op(msecc1, OP_LE, nsecc1 / 1000000 + 1); + tt_u64_op(usecc1, OP_LE, nsecc1 / 1000 + 10); + + done: + ; +} + +static void +test_util_monotonic_time_ratchet(void *arg) +{ + (void)arg; + monotime_reset_ratchets_for_testing(); + + /* win32, performance counter ratchet. */ + tt_i64_op(100, OP_EQ, ratchet_performance_counter(100)); + tt_i64_op(101, OP_EQ, ratchet_performance_counter(101)); + tt_i64_op(2000, OP_EQ, ratchet_performance_counter(2000)); + tt_i64_op(2000, OP_EQ, ratchet_performance_counter(100)); + tt_i64_op(2005, OP_EQ, ratchet_performance_counter(105)); + tt_i64_op(3005, OP_EQ, ratchet_performance_counter(1105)); + tt_i64_op(3005, OP_EQ, ratchet_performance_counter(1000)); + tt_i64_op(3010, OP_EQ, ratchet_performance_counter(1005)); + + /* win32, GetTickCounts32 ratchet-and-rollover-detector. */ + const int64_t R = ((int64_t)1) << 32; + tt_i64_op(5, OP_EQ, ratchet_coarse_performance_counter(5)); + tt_i64_op(1000, OP_EQ, ratchet_coarse_performance_counter(1000)); + tt_i64_op(5+R, OP_EQ, ratchet_coarse_performance_counter(5)); + tt_i64_op(10+R, OP_EQ, ratchet_coarse_performance_counter(10)); + tt_i64_op(4+R*2, OP_EQ, ratchet_coarse_performance_counter(4)); + + /* gettimeofday regular ratchet. */ + struct timeval tv_in = {0,0}, tv_out; + tv_in.tv_usec = 9000; + + ratchet_timeval(&tv_in, &tv_out); + tt_int_op(tv_out.tv_usec, OP_EQ, 9000); + tt_i64_op(tv_out.tv_sec, OP_EQ, 0); + + tv_in.tv_sec = 1337; + tv_in.tv_usec = 0; + ratchet_timeval(&tv_in, &tv_out); + tt_int_op(tv_out.tv_usec, OP_EQ, 0); + tt_i64_op(tv_out.tv_sec, OP_EQ, 1337); + + tv_in.tv_sec = 1336; + tv_in.tv_usec = 500000; + ratchet_timeval(&tv_in, &tv_out); + tt_int_op(tv_out.tv_usec, OP_EQ, 0); + tt_i64_op(tv_out.tv_sec, OP_EQ, 1337); + + tv_in.tv_sec = 1337; + tv_in.tv_usec = 0; + ratchet_timeval(&tv_in, &tv_out); + tt_int_op(tv_out.tv_usec, OP_EQ, 500000); + tt_i64_op(tv_out.tv_sec, OP_EQ, 1337); + + tv_in.tv_sec = 1337; + tv_in.tv_usec = 600000; + ratchet_timeval(&tv_in, &tv_out); + tt_int_op(tv_out.tv_usec, OP_EQ, 100000); + tt_i64_op(tv_out.tv_sec, OP_EQ, 1338); + + tv_in.tv_sec = 1000; + tv_in.tv_usec = 1000; + ratchet_timeval(&tv_in, &tv_out); + tt_int_op(tv_out.tv_usec, OP_EQ, 100000); + tt_i64_op(tv_out.tv_sec, OP_EQ, 1338); + + tv_in.tv_sec = 2000; + tv_in.tv_usec = 2000; + ratchet_timeval(&tv_in, &tv_out); + tt_int_op(tv_out.tv_usec, OP_EQ, 101000); + tt_i64_op(tv_out.tv_sec, OP_EQ, 2338); + + done: + ; +} + #define UTIL_LEGACY(name) \ { #name, test_util_ ## name , 0, NULL, NULL } @@ -5373,6 +5496,8 @@ struct testcase_t util_tests[] = { UTIL_TEST(touch_file, 0), UTIL_TEST_NO_WIN(pwdb, TT_FORK), UTIL_TEST(calloc_check, 0), + UTIL_TEST(monotonic_time, 0), + UTIL_TEST(monotonic_time_ratchet, TT_FORK), END_OF_TESTCASES }; From 493142d91f7a553b26094308ebd2694dd30c3e0e Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 13 Jul 2016 10:25:04 -0400 Subject: [PATCH 08/10] Changes file for monotonic time branch. --- changes/monotonic | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 changes/monotonic diff --git a/changes/monotonic b/changes/monotonic new file mode 100644 index 0000000000..7143f69b89 --- /dev/null +++ b/changes/monotonic @@ -0,0 +1,6 @@ + o Minor features (backend): + - Tor now uses the operating system's monotonic timers (where available) + for internal fine-grained timing. Previously we would look at the + system clock, and then attempt to compensate for the clock running + backwards. Closes ticket 18908. + From 6ba415d4000712aecaf3d11903db5699f143569d Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 19 Jul 2016 11:36:43 +0200 Subject: [PATCH 09/10] Make sure initialized_at is initialized before use. --- src/common/compat_time.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/common/compat_time.c b/src/common/compat_time.c index de3956685a..b7d69cf400 100644 --- a/src/common/compat_time.c +++ b/src/common/compat_time.c @@ -466,6 +466,10 @@ uint64_t monotime_absolute_nsec(void) { monotime_t now; + if (BUG(monotime_initialized == 0)) { + monotime_init(); + } + monotime_get(&now); return monotime_diff_nsec(&initialized_at, &now); } @@ -486,6 +490,10 @@ monotime_absolute_msec(void) uint64_t monotime_coarse_absolute_nsec(void) { + if (BUG(monotime_initialized == 0)) { + monotime_init(); + } + monotime_coarse_t now; monotime_coarse_get(&now); return monotime_coarse_diff_nsec(&initialized_at_coarse, &now); From 1e3cf1cc8321ec29acaae6acb22340f0e2f5e9da Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 19 Jul 2016 11:40:21 +0200 Subject: [PATCH 10/10] Be sure to call monotime_init() at startup. --- src/or/main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/or/main.c b/src/or/main.c index 404d741c38..76af910b3e 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -3646,6 +3646,8 @@ tor_main(int argc, char *argv[]) #endif } + monotime_init(); + switch (get_options()->command) { case CMD_RUN_TOR: #ifdef NT_SERVICE