diff --git a/Makefile.in b/Makefile.in index c4f8cf90..e314a6f3 100644 --- a/Makefile.in +++ b/Makefile.in @@ -116,7 +116,7 @@ C_TESTLIB_SRCS := $(srcroot)test/src/btalloc.c $(srcroot)test/src/btalloc_0.c \ $(srcroot)test/src/mtx.c $(srcroot)test/src/mq.c \ $(srcroot)test/src/SFMT.c $(srcroot)test/src/test.c \ $(srcroot)test/src/thd.c $(srcroot)test/src/timer.c -C_UTIL_INTEGRATION_SRCS := $(srcroot)src/util.c +C_UTIL_INTEGRATION_SRCS := $(srcroot)src/time.c $(srcroot)src/util.c TESTS_UNIT := $(srcroot)test/unit/atomic.c \ $(srcroot)test/unit/bitmap.c \ $(srcroot)test/unit/ckh.c \ diff --git a/include/jemalloc/internal/private_symbols.txt b/include/jemalloc/internal/private_symbols.txt index 8b1fd45c..4c40af61 100644 --- a/include/jemalloc/internal/private_symbols.txt +++ b/include/jemalloc/internal/private_symbols.txt @@ -460,6 +460,18 @@ tcaches_get tcache_stats_merge thread_allocated_cleanup thread_deallocated_cleanup +ticker_init +ticker_tick +time_add +time_compare +time_copy +time_divide +time_idivide +time_imultiply +time_init +time_nsec +time_sec +time_subtract time_update tsd_arena_get tsd_arena_set diff --git a/include/jemalloc/internal/time.h b/include/jemalloc/internal/time.h index e3e6c5f4..a290f386 100644 --- a/include/jemalloc/internal/time.h +++ b/include/jemalloc/internal/time.h @@ -1,8 +1,11 @@ +/******************************************************************************/ +#ifdef JEMALLOC_H_TYPES + #define JEMALLOC_CLOCK_GETTIME defined(_POSIX_MONOTONIC_CLOCK) \ && _POSIX_MONOTONIC_CLOCK >= 0 -/******************************************************************************/ -#ifdef JEMALLOC_H_TYPES +/* Maximum supported number of seconds (~584 years). */ +#define TIME_SEC_MAX 18446744072 #endif /* JEMALLOC_H_TYPES */ /******************************************************************************/ @@ -12,6 +15,17 @@ /******************************************************************************/ #ifdef JEMALLOC_H_EXTERNS +void time_init(struct timespec *time, time_t sec, long nsec); +time_t time_sec(const struct timespec *time); +long time_nsec(const struct timespec *time); +void time_copy(struct timespec *time, const struct timespec *source); +int time_compare(const struct timespec *a, const struct timespec *b); +void time_add(struct timespec *time, const struct timespec *addend); +void time_subtract(struct timespec *time, const struct timespec *subtrahend); +void time_imultiply(struct timespec *time, uint64_t multiplier); +void time_idivide(struct timespec *time, uint64_t divisor); +uint64_t time_divide(const struct timespec *time, + const struct timespec *divisor); bool time_update(struct timespec *time); #endif /* JEMALLOC_H_EXTERNS */ diff --git a/src/time.c b/src/time.c index 2147c529..3f930385 100644 --- a/src/time.c +++ b/src/time.c @@ -1,11 +1,160 @@ #include "jemalloc/internal/jemalloc_internal.h" +#define BILLION 1000000000 + +UNUSED static bool +time_valid(const struct timespec *time) +{ + + if (time->tv_sec > TIME_SEC_MAX) + return (false); + if (time->tv_nsec >= BILLION) + return (false); + + return (true); +} + +void +time_init(struct timespec *time, time_t sec, long nsec) +{ + + time->tv_sec = sec; + time->tv_nsec = nsec; + + assert(time_valid(time)); +} + +time_t +time_sec(const struct timespec *time) +{ + + assert(time_valid(time)); + + return (time->tv_sec); +} + +long +time_nsec(const struct timespec *time) +{ + + assert(time_valid(time)); + + return (time->tv_nsec); +} + +void +time_copy(struct timespec *time, const struct timespec *source) +{ + + assert(time_valid(source)); + + *time = *source; +} + +int +time_compare(const struct timespec *a, const struct timespec *b) +{ + int ret; + + assert(time_valid(a)); + assert(time_valid(b)); + + ret = (a->tv_sec > b->tv_sec) - (a->tv_sec < b->tv_sec); + if (ret == 0) + ret = (a->tv_nsec > b->tv_nsec) - (a->tv_nsec < b->tv_nsec); + + return (ret); +} + +void +time_add(struct timespec *time, const struct timespec *addend) +{ + + assert(time_valid(time)); + assert(time_valid(addend)); + + time->tv_sec += addend->tv_sec; + time->tv_nsec += addend->tv_nsec; + if (time->tv_nsec >= BILLION) { + time->tv_sec++; + time->tv_nsec -= BILLION; + } + + assert(time_valid(time)); +} + +void +time_subtract(struct timespec *time, const struct timespec *subtrahend) +{ + + assert(time_valid(time)); + assert(time_valid(subtrahend)); + assert(time_compare(time, subtrahend) >= 0); + + time->tv_sec -= subtrahend->tv_sec; + if (time->tv_nsec < subtrahend->tv_nsec) { + time->tv_sec--; + time->tv_nsec += BILLION; + } + time->tv_nsec -= subtrahend->tv_nsec; +} + +void +time_imultiply(struct timespec *time, uint64_t multiplier) +{ + time_t sec; + uint64_t nsec; + + assert(time_valid(time)); + + sec = time->tv_sec * multiplier; + nsec = time->tv_nsec * multiplier; + sec += nsec / BILLION; + nsec %= BILLION; + time_init(time, sec, (long)nsec); + + assert(time_valid(time)); +} + +void +time_idivide(struct timespec *time, uint64_t divisor) +{ + time_t sec; + uint64_t nsec; + + assert(time_valid(time)); + + sec = time->tv_sec / divisor; + nsec = ((time->tv_sec % divisor) * BILLION + time->tv_nsec) / divisor; + sec += nsec / BILLION; + nsec %= BILLION; + time_init(time, sec, (long)nsec); + + assert(time_valid(time)); +} + +uint64_t +time_divide(const struct timespec *time, const struct timespec *divisor) +{ + uint64_t t, d; + + assert(time_valid(time)); + assert(time_valid(divisor)); + + t = time_sec(time) * BILLION + time_nsec(time); + d = time_sec(divisor) * BILLION + time_nsec(divisor); + assert(d != 0); + return (t / d); +} + bool time_update(struct timespec *time) { struct timespec old_time; - memcpy(&old_time, time, sizeof(struct timespec)); + assert(time_valid(time)); + + time_copy(&old_time, time); #ifdef _WIN32 FILETIME ft; @@ -27,10 +176,11 @@ time_update(struct timespec *time) #endif /* Handle non-monotonic clocks. */ - if (unlikely(old_time.tv_sec > time->tv_sec)) + if (unlikely(time_compare(&old_time, time) > 0)) { + time_copy(time, &old_time); return (true); - if (unlikely(old_time.tv_sec == time->tv_sec)) - return old_time.tv_nsec > time->tv_nsec; + } + assert(time_valid(time)); return (false); } diff --git a/test/include/test/jemalloc_test.h.in b/test/include/test/jemalloc_test.h.in index 455569da..223162e1 100644 --- a/test/include/test/jemalloc_test.h.in +++ b/test/include/test/jemalloc_test.h.in @@ -94,6 +94,7 @@ # define JEMALLOC_H_STRUCTS # define JEMALLOC_H_EXTERNS # define JEMALLOC_H_INLINES +# include "jemalloc/internal/time.h" # include "jemalloc/internal/util.h" # include "jemalloc/internal/qr.h" # include "jemalloc/internal/ql.h" diff --git a/test/include/test/timer.h b/test/include/test/timer.h index a7fefdfd..a791f9ce 100644 --- a/test/include/test/timer.h +++ b/test/include/test/timer.h @@ -3,21 +3,9 @@ #include #include -#define JEMALLOC_CLOCK_GETTIME defined(_POSIX_MONOTONIC_CLOCK) \ - && _POSIX_MONOTONIC_CLOCK >= 0 - typedef struct { -#ifdef _WIN32 - FILETIME ft0; - FILETIME ft1; -#elif JEMALLOC_CLOCK_GETTIME - struct timespec ts0; - struct timespec ts1; - int clock_id; -#else - struct timeval tv0; - struct timeval tv1; -#endif + struct timespec t0; + struct timespec t1; } timedelta_t; void timer_start(timedelta_t *timer); diff --git a/test/src/timer.c b/test/src/timer.c index 0c93abaf..15306cfd 100644 --- a/test/src/timer.c +++ b/test/src/timer.c @@ -4,50 +4,26 @@ void timer_start(timedelta_t *timer) { -#ifdef _WIN32 - GetSystemTimeAsFileTime(&timer->ft0); -#elif JEMALLOC_CLOCK_GETTIME - if (sysconf(_SC_MONOTONIC_CLOCK) <= 0) - timer->clock_id = CLOCK_REALTIME; - else - timer->clock_id = CLOCK_MONOTONIC; - clock_gettime(timer->clock_id, &timer->ts0); -#else - gettimeofday(&timer->tv0, NULL); -#endif + time_init(&timer->t0, 0, 0); + time_update(&timer->t0); } void timer_stop(timedelta_t *timer) { -#ifdef _WIN32 - GetSystemTimeAsFileTime(&timer->ft0); -#elif JEMALLOC_CLOCK_GETTIME - clock_gettime(timer->clock_id, &timer->ts1); -#else - gettimeofday(&timer->tv1, NULL); -#endif + time_copy(&timer->t1, &timer->t0); + time_update(&timer->t1); } uint64_t timer_usec(const timedelta_t *timer) { + struct timespec delta; -#ifdef _WIN32 - uint64_t t0, t1; - t0 = (((uint64_t)timer->ft0.dwHighDateTime) << 32) | - timer->ft0.dwLowDateTime; - t1 = (((uint64_t)timer->ft1.dwHighDateTime) << 32) | - timer->ft1.dwLowDateTime; - return ((t1 - t0) / 10); -#elif JEMALLOC_CLOCK_GETTIME - return (((timer->ts1.tv_sec - timer->ts0.tv_sec) * 1000000) + - (timer->ts1.tv_nsec - timer->ts0.tv_nsec) / 1000); -#else - return (((timer->tv1.tv_sec - timer->tv0.tv_sec) * 1000000) + - timer->tv1.tv_usec - timer->tv0.tv_usec); -#endif + time_copy(&delta, &timer->t1); + time_subtract(&delta, &timer->t0); + return (time_sec(&delta) * 1000000 + time_nsec(&delta) / 1000); } void diff --git a/test/unit/time.c b/test/unit/time.c index 80460f98..941e6f13 100644 --- a/test/unit/time.c +++ b/test/unit/time.c @@ -1,16 +1,206 @@ #include "test/jemalloc_test.h" +#define BILLION 1000000000 + +TEST_BEGIN(test_time_init) +{ + struct timespec ts; + + time_init(&ts, 42, 43); + assert_ld_eq(ts.tv_sec, 42, "tv_sec incorrectly initialized"); + assert_ld_eq(ts.tv_nsec, 43, "tv_nsec incorrectly initialized"); +} +TEST_END + +TEST_BEGIN(test_time_sec) +{ + struct timespec ts; + + time_init(&ts, 42, 43); + assert_ld_eq(time_sec(&ts), 42, "tv_sec incorrectly read"); +} +TEST_END + +TEST_BEGIN(test_time_nsec) +{ + struct timespec ts; + + time_init(&ts, 42, 43); + assert_ld_eq(time_nsec(&ts), 43, "tv_nsec incorrectly read"); +} +TEST_END + +TEST_BEGIN(test_time_copy) +{ + struct timespec tsa, tsb; + + time_init(&tsa, 42, 43); + time_init(&tsb, 0, 0); + time_copy(&tsb, &tsa); + assert_ld_eq(time_sec(&tsb), 42, "tv_sec incorrectly copied"); + assert_ld_eq(time_nsec(&tsb), 43, "tv_nsec incorrectly copied"); +} +TEST_END + +TEST_BEGIN(test_time_compare) +{ + struct timespec tsa, tsb; + + time_init(&tsa, 42, 43); + time_copy(&tsb, &tsa); + assert_d_eq(time_compare(&tsa, &tsb), 0, "Times should be equal"); + assert_d_eq(time_compare(&tsb, &tsa), 0, "Times should be equal"); + + time_init(&tsb, 42, 42); + assert_d_eq(time_compare(&tsa, &tsb), 1, + "tsa should be greater than tsb"); + assert_d_eq(time_compare(&tsb, &tsa), -1, + "tsb should be less than tsa"); + + time_init(&tsb, 42, 44); + assert_d_eq(time_compare(&tsa, &tsb), -1, + "tsa should be less than tsb"); + assert_d_eq(time_compare(&tsb, &tsa), 1, + "tsb should be greater than tsa"); + + time_init(&tsb, 41, BILLION - 1); + assert_d_eq(time_compare(&tsa, &tsb), 1, + "tsa should be greater than tsb"); + assert_d_eq(time_compare(&tsb, &tsa), -1, + "tsb should be less than tsa"); + + time_init(&tsb, 43, 0); + assert_d_eq(time_compare(&tsa, &tsb), -1, + "tsa should be less than tsb"); + assert_d_eq(time_compare(&tsb, &tsa), 1, + "tsb should be greater than tsa"); +} +TEST_END + +TEST_BEGIN(test_time_add) +{ + struct timespec tsa, tsb; + + time_init(&tsa, 42, 43); + time_copy(&tsb, &tsa); + time_add(&tsa, &tsb); + time_init(&tsb, 84, 86); + assert_d_eq(time_compare(&tsa, &tsb), 0, "Incorrect addition result"); + + time_init(&tsa, 42, BILLION - 1); + time_copy(&tsb, &tsa); + time_add(&tsa, &tsb); + time_init(&tsb, 85, BILLION - 2); + assert_d_eq(time_compare(&tsa, &tsb), 0, "Incorrect addition result"); +} +TEST_END + +TEST_BEGIN(test_time_subtract) +{ + struct timespec tsa, tsb; + + time_init(&tsa, 42, 43); + time_copy(&tsb, &tsa); + time_subtract(&tsa, &tsb); + time_init(&tsb, 0, 0); + assert_d_eq(time_compare(&tsa, &tsb), 0, + "Incorrect subtraction result"); + + time_init(&tsa, 42, 43); + time_init(&tsb, 41, 44); + time_subtract(&tsa, &tsb); + time_init(&tsb, 0, BILLION - 1); + assert_d_eq(time_compare(&tsa, &tsb), 0, + "Incorrect subtraction result"); +} +TEST_END + +TEST_BEGIN(test_time_imultiply) +{ + struct timespec tsa, tsb; + + time_init(&tsa, 42, 43); + time_imultiply(&tsa, 10); + time_init(&tsb, 420, 430); + assert_d_eq(time_compare(&tsa, &tsb), 0, + "Incorrect multiplication result"); + + time_init(&tsa, 42, 666666666); + time_imultiply(&tsa, 3); + time_init(&tsb, 127, 999999998); + assert_d_eq(time_compare(&tsa, &tsb), 0, + "Incorrect multiplication result"); +} +TEST_END + +TEST_BEGIN(test_time_idivide) +{ + struct timespec tsa, tsb; + + time_init(&tsa, 42, 43); + time_copy(&tsb, &tsa); + time_imultiply(&tsa, 10); + time_idivide(&tsa, 10); + assert_d_eq(time_compare(&tsa, &tsb), 0, "Incorrect division result"); + + time_init(&tsa, 42, 666666666); + time_copy(&tsb, &tsa); + time_imultiply(&tsa, 3); + time_idivide(&tsa, 3); + assert_d_eq(time_compare(&tsa, &tsb), 0, "Incorrect division result"); +} +TEST_END + +TEST_BEGIN(test_time_divide) +{ + struct timespec tsa, tsb, tsc; + + time_init(&tsa, 42, 43); + time_copy(&tsb, &tsa); + time_imultiply(&tsa, 10); + assert_u64_eq(time_divide(&tsa, &tsb), 10, + "Incorrect division result"); + + time_init(&tsa, 42, 43); + time_copy(&tsb, &tsa); + time_imultiply(&tsa, 10); + time_init(&tsc, 0, 1); + time_add(&tsa, &tsc); + assert_u64_eq(time_divide(&tsa, &tsb), 10, + "Incorrect division result"); + + time_init(&tsa, 42, 43); + time_copy(&tsb, &tsa); + time_imultiply(&tsa, 10); + time_init(&tsc, 0, 1); + time_subtract(&tsa, &tsc); + assert_u64_eq(time_divide(&tsa, &tsb), 9, "Incorrect division result"); +} +TEST_END + TEST_BEGIN(test_time_update) { struct timespec ts; - memset(&ts, 0, sizeof(struct timespec)); + time_init(&ts, 0, 0); assert_false(time_update(&ts), "Basic time update failed."); /* Only Rip Van Winkle sleeps this long. */ - ts.tv_sec += 631152000; - assert_true(time_update(&ts), "Update should detect time roll-back."); + { + struct timespec addend; + time_init(&addend, 631152000, 0); + time_add(&ts, &addend); + } + { + struct timespec ts0; + time_copy(&ts0, &ts); + assert_true(time_update(&ts), + "Update should detect time roll-back."); + assert_d_eq(time_compare(&ts, &ts0), 0, + "Time should not have been modified"); + } + } TEST_END @@ -19,5 +209,15 @@ main(void) { return (test( + test_time_init, + test_time_sec, + test_time_nsec, + test_time_copy, + test_time_compare, + test_time_add, + test_time_subtract, + test_time_imultiply, + test_time_idivide, + test_time_divide, test_time_update)); }