From 2012d45e273c3016dbd09b2606efc9ffab07c57f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 10 Sep 2012 15:44:52 +0930 Subject: [PATCH] time: use timespec instead of timeval. It's a superset of struct timeval. It's also a bit trickier, since we need -lrt on Linux to use clock_gettime(), and also we can't just cram the time into a u64 for multiply and divide. --- ccan/time/_info | 13 ++++- ccan/time/test/run.c | 67 +++++++++++++++------ ccan/time/time.c | 136 +++++++++++++++++++++++++++++++++---------- ccan/time/time.h | 112 ++++++++++++++++++++++++++++------- 4 files changed, 253 insertions(+), 75 deletions(-) diff --git a/ccan/time/_info b/ccan/time/_info index fdad2756..c562e49f 100644 --- a/ccan/time/_info +++ b/ccan/time/_info @@ -17,7 +17,7 @@ * * int main(int argc, char *argv[]) * { - * struct timeval t; + * struct timespec t; * * if (argc != 2) * errx(1, "Usage: %s ", argv[0]); @@ -28,8 +28,8 @@ * else * t = time_add(t, time_from_msec(atol(argv[1]))); * - * printf("%lu.%06u\n", - * (unsigned long)t.tv_sec, (unsigned)t.tv_usec); + * printf("%lu.%09u\n", + * (unsigned long)t.tv_sec, (unsigned)t.tv_nsec); * return 0; * } */ @@ -43,5 +43,12 @@ int main(int argc, char *argv[]) return 0; } +#if HAVE_CLOCK_GETTIME_IN_LIBRT + if (strcmp(argv[1], "libs") == 0) { + printf("rt\n"); + return 0; + } +#endif + return 1; } diff --git a/ccan/time/test/run.c b/ccan/time/test/run.c index 80391694..7f436600 100644 --- a/ccan/time/test/run.c +++ b/ccan/time/test/run.c @@ -4,9 +4,9 @@ int main(void) { - struct timeval t1, t2, t3, zero = { 0, 0 }; + struct timespec t1, t2, t3, zero = { 0, 0 }; - plan_tests(46); + plan_tests(58); /* Test time_now */ t1 = time_now(); @@ -14,11 +14,11 @@ int main(void) /* Test time_sub. */ t3 = time_sub(t2, t1); - ok1(t3.tv_sec > 0 || t3.tv_usec >= 0); + ok1(t3.tv_sec > 0 || t3.tv_nsec >= 0); t3 = time_sub(t2, t2); - ok1(t3.tv_sec == 0 && t3.tv_usec == 0); + ok1(t3.tv_sec == 0 && t3.tv_nsec == 0); t3 = time_sub(t1, t1); - ok1(t3.tv_sec == 0 && t3.tv_usec == 0); + ok1(t3.tv_sec == 0 && t3.tv_nsec == 0); /* Test time_eq */ ok1(time_eq(t1, t1)); @@ -28,7 +28,7 @@ int main(void) /* Make sure t2 > t1. */ t3.tv_sec = 0; - t3.tv_usec = 1; + t3.tv_nsec = 1; t2 = time_add(t2, t3); /* Test time_less and time_greater. */ @@ -38,7 +38,7 @@ int main(void) ok1(time_greater(t2, t1)); ok1(!time_less(t2, t1)); t3.tv_sec = 0; - t3.tv_usec = 999999; + t3.tv_nsec = 999999999; t2 = time_add(t2, t3); ok1(!time_eq(t1, t2)); ok1(!time_greater(t1, t2)); @@ -54,58 +54,89 @@ int main(void) /* time_from_msec / time_to_msec */ t3 = time_from_msec(500); ok1(t3.tv_sec == 0); - ok1(t3.tv_usec == 500000); + ok1(t3.tv_nsec == 500000000); ok1(time_to_msec(t3) == 500); t3 = time_from_msec(1000); ok1(t3.tv_sec == 1); - ok1(t3.tv_usec == 0); + ok1(t3.tv_nsec == 0); ok1(time_to_msec(t3) == 1000); t3 = time_from_msec(1500); ok1(t3.tv_sec == 1); - ok1(t3.tv_usec == 500000); + ok1(t3.tv_nsec == 500000000); ok1(time_to_msec(t3) == 1500); /* time_from_usec */ t3 = time_from_usec(500000); ok1(t3.tv_sec == 0); - ok1(t3.tv_usec == 500000); + ok1(t3.tv_nsec == 500000000); ok1(time_to_usec(t3) == 500000); t3 = time_from_usec(1000000); ok1(t3.tv_sec == 1); - ok1(t3.tv_usec == 0); + ok1(t3.tv_nsec == 0); ok1(time_to_usec(t3) == 1000000); t3 = time_from_usec(1500000); ok1(t3.tv_sec == 1); - ok1(t3.tv_usec == 500000); + ok1(t3.tv_nsec == 500000000); ok1(time_to_usec(t3) == 1500000); + /* time_from_nsec */ + t3 = time_from_nsec(500000000); + ok1(t3.tv_sec == 0); + ok1(t3.tv_nsec == 500000000); + ok1(time_to_nsec(t3) == 500000000); + + t3 = time_from_nsec(1000000000); + ok1(t3.tv_sec == 1); + ok1(t3.tv_nsec == 0); + ok1(time_to_nsec(t3) == 1000000000); + + t3 = time_from_nsec(1500000000); + ok1(t3.tv_sec == 1); + ok1(t3.tv_nsec == 500000000); + ok1(time_to_nsec(t3) == 1500000000); + /* Test wrapunder */ t3 = time_sub(time_sub(t2, time_from_msec(500)), time_from_msec(500)); ok1(t3.tv_sec == t2.tv_sec - 1); - ok1(t3.tv_usec == t2.tv_usec); + ok1(t3.tv_nsec == t2.tv_nsec); /* time_divide and time_multiply */ - t1.tv_usec = 100; + t1.tv_nsec = 100; t1.tv_sec = 100; t3 = time_divide(t1, 2); ok1(t3.tv_sec == 50); - ok1(t3.tv_usec == 50); + ok1(t3.tv_nsec == 50); t3 = time_divide(t1, 100); ok1(t3.tv_sec == 1); - ok1(t3.tv_usec == 1); + ok1(t3.tv_nsec == 1); t3 = time_multiply(t3, 100); ok1(time_eq(t3, t1)); t3 = time_divide(t1, 200); ok1(t3.tv_sec == 0); - ok1(t3.tv_usec == 500000); + ok1(t3.tv_nsec == 500000000); + + /* Divide by huge number. */ + t1.tv_sec = (1U << 31) - 1; + t1.tv_nsec = 999999999; + t2 = time_divide(t1, 1 << 30); + /* Allow us to round either way. */ + ok1((t2.tv_sec == 2 && t2.tv_nsec == 0) + || (t2.tv_sec == 1 && t2.tv_nsec == 999999999)); + + /* Multiply by huge number. */ + t1.tv_sec = 0; + t1.tv_nsec = 1; + t2 = time_multiply(t1, 1UL << 31); + ok1(t2.tv_sec == 2); + ok1(t2.tv_nsec == 147483648); return exit_status(); } diff --git a/ccan/time/time.c b/ccan/time/time.c index 5e36bf7b..65a257b3 100644 --- a/ccan/time/time.c +++ b/ccan/time/time.c @@ -3,106 +3,178 @@ #include #include -struct timeval time_now(void) +#if !HAVE_CLOCK_GETTIME && !HAVE_CLOCK_GETTIME_IN_LIBRT +#include + +struct timespec time_now(void) { struct timeval now; + struct timespec ret; gettimeofday(&now, NULL); - return now; + ret.tv_sec = now.tv_sec; + ret.tv_nsec = now.tv_usec * 1000; + return ret; +} +#else +#include +struct timespec time_now(void) +{ + struct timespec ret; + clock_gettime(CLOCK_REALTIME, &ret); + return ret; } +#endif /* HAVE_CLOCK_GETTIME || HAVE_CLOCK_GETTIME_IN_LIBRT */ -bool time_greater(struct timeval a, struct timeval b) +bool time_greater(struct timespec a, struct timespec b) { if (a.tv_sec > b.tv_sec) return true; else if (a.tv_sec < b.tv_sec) return false; - return a.tv_usec > b.tv_usec; + return a.tv_nsec > b.tv_nsec; } -bool time_less(struct timeval a, struct timeval b) +bool time_less(struct timespec a, struct timespec b) { if (a.tv_sec < b.tv_sec) return true; else if (a.tv_sec > b.tv_sec) return false; - return a.tv_usec < b.tv_usec; + return a.tv_nsec < b.tv_nsec; } -bool time_eq(struct timeval a, struct timeval b) +bool time_eq(struct timespec a, struct timespec b) { - return a.tv_sec == b.tv_sec && a.tv_usec == b.tv_usec; + return a.tv_sec == b.tv_sec && a.tv_nsec == b.tv_nsec; } -struct timeval time_sub(struct timeval recent, struct timeval old) +struct timespec time_sub(struct timespec recent, struct timespec old) { - struct timeval diff; + struct timespec diff; diff.tv_sec = recent.tv_sec - old.tv_sec; - if (old.tv_usec > recent.tv_usec) { + if (old.tv_nsec > recent.tv_nsec) { diff.tv_sec--; - diff.tv_usec = 1000000 + recent.tv_usec - old.tv_usec; + diff.tv_nsec = 1000000000 + recent.tv_nsec - old.tv_nsec; } else - diff.tv_usec = recent.tv_usec - old.tv_usec; + diff.tv_nsec = recent.tv_nsec - old.tv_nsec; assert(diff.tv_sec >= 0); return diff; } -struct timeval time_add(struct timeval a, struct timeval b) +struct timespec time_add(struct timespec a, struct timespec b) { - struct timeval sum; + struct timespec sum; sum.tv_sec = a.tv_sec + b.tv_sec; - sum.tv_usec = a.tv_usec + b.tv_usec; - if (sum.tv_usec > 1000000) { + sum.tv_nsec = a.tv_nsec + b.tv_nsec; + if (sum.tv_nsec >= 1000000000) { sum.tv_sec++; - sum.tv_usec -= 1000000; + sum.tv_nsec -= 1000000000; } return sum; } -struct timeval time_divide(struct timeval t, unsigned long div) +struct timespec time_divide(struct timespec t, unsigned long div) { - return time_from_usec(time_to_usec(t) / div); + struct timespec res; + uint64_t rem, ns; + + /* Dividing seconds is simple. */ + res.tv_sec = t.tv_sec / div; + rem = t.tv_sec % div; + + /* If we can't fit remainder * 1,000,000,000 in 64 bits? */ +#if 0 /* ilog is great, but we use fp for multiply anyway. */ + bits = ilog64(rem); + if (bits + 30 >= 64) { + /* Reduce accuracy slightly */ + rem >>= (bits - (64 - 30)); + div >>= (bits - (64 - 30)); + } +#endif + if (rem & ~(((uint64_t)1 << 30) - 1)) { + /* FIXME: fp is cheating! */ + double nsec = rem * 1000000000.0 + t.tv_nsec; + res.tv_nsec = nsec / div; + } else { + ns = rem * 1000000000 + t.tv_nsec; + res.tv_nsec = ns / div; + } + return res; } -struct timeval time_multiply(struct timeval t, unsigned long mult) +struct timespec time_multiply(struct timespec t, unsigned long mult) { - return time_from_usec(time_to_usec(t) * mult); + struct timespec res; + + /* Are we going to overflow if we multiply nsec? */ + if (mult & ~((1UL << 30) - 1)) { + /* FIXME: fp is cheating! */ + double nsec = (double)t.tv_nsec * mult; + + res.tv_sec = nsec / 1000000000.0; + res.tv_nsec = nsec - (res.tv_sec * 1000000000.0); + } else { + uint64_t nsec = t.tv_nsec * mult; + + res.tv_nsec = nsec % 1000000000; + res.tv_sec = nsec / 1000000000; + } + res.tv_sec += t.tv_sec * mult; + return res; } -uint64_t time_to_msec(struct timeval t) +uint64_t time_to_msec(struct timespec t) { uint64_t msec; - msec = t.tv_usec / 1000 + (uint64_t)t.tv_sec * 1000; + msec = t.tv_nsec / 1000000 + (uint64_t)t.tv_sec * 1000; return msec; } -uint64_t time_to_usec(struct timeval t) +uint64_t time_to_usec(struct timespec t) { uint64_t usec; - usec = t.tv_usec + (uint64_t)t.tv_sec * 1000000; + usec = t.tv_nsec / 1000 + (uint64_t)t.tv_sec * 1000000; return usec; } -struct timeval time_from_msec(uint64_t msec) +uint64_t time_to_nsec(struct timespec t) { - struct timeval t; + uint64_t nsec; - t.tv_usec = (msec % 1000) * 1000; + nsec = t.tv_nsec + (uint64_t)t.tv_sec * 1000000000; + return nsec; +} + +struct timespec time_from_msec(uint64_t msec) +{ + struct timespec t; + + t.tv_nsec = (msec % 1000) * 1000000; t.tv_sec = msec / 1000; return t; } -struct timeval time_from_usec(uint64_t usec) +struct timespec time_from_usec(uint64_t usec) { - struct timeval t; + struct timespec t; - t.tv_usec = usec % 1000000; + t.tv_nsec = (usec % 1000000) * 1000; t.tv_sec = usec / 1000000; return t; } + +struct timespec time_from_nsec(uint64_t nsec) +{ + struct timespec t; + + t.tv_nsec = nsec % 1000000000; + t.tv_sec = nsec / 1000000000; + return t; +} diff --git a/ccan/time/time.h b/ccan/time/time.h index fb2ee459..6fb622c6 100644 --- a/ccan/time/time.h +++ b/ccan/time/time.h @@ -3,6 +3,14 @@ #define CCAN_TIME_H #include "config.h" #include +#if HAVE_STRUCT_TIMESPEC +#include +#else +struct timespec { + time_t tv_sec; /* seconds */ + long tv_nsec; /* nanoseconds */ +}; +#endif #include #include @@ -12,7 +20,7 @@ * Example: * printf("Now is %lu seconds since epoch\n", (long)time_now().tv_sec); */ -struct timeval time_now(void); +struct timespec time_now(void); /** * time_greater - is a after b? @@ -20,13 +28,13 @@ struct timeval time_now(void); * @b: another time. * * Example: - * static bool timed_out(const struct timeval *start) + * static bool timed_out(const struct timespec *start) * { * #define TIMEOUT time_from_msec(1000) * return time_greater(time_now(), time_add(*start, TIMEOUT)); * } */ -bool time_greater(struct timeval a, struct timeval b); +bool time_greater(struct timespec a, struct timespec b); /** * time_less - is a before b? @@ -34,13 +42,13 @@ bool time_greater(struct timeval a, struct timeval b); * @b: another time. * * Example: - * static bool still_valid(const struct timeval *start) + * static bool still_valid(const struct timespec *start) * { * #define TIMEOUT time_from_msec(1000) * return time_less(time_now(), time_add(*start, TIMEOUT)); * } */ -bool time_less(struct timeval a, struct timeval b); +bool time_less(struct timespec a, struct timespec b); /** * time_eq - is a equal to b? @@ -54,7 +62,7 @@ bool time_less(struct timeval a, struct timeval b); * // Can we fork in under a microsecond? * static bool fast_fork(void) * { - * struct timeval start = time_now(); + * struct timespec start = time_now(); * if (fork() != 0) { * exit(0); * } @@ -62,22 +70,22 @@ bool time_less(struct timeval a, struct timeval b); * return time_eq(start, time_now()); * } */ -bool time_eq(struct timeval a, struct timeval b); +bool time_eq(struct timespec a, struct timespec b); /** * time_sub - subtract two times * @recent: the larger (more recent) time. * @old: the smaller (less recent) time. * - * This returns a well formed struct timeval. + * This returns a well formed struct timespec. * * Example: - * static bool was_recent(const struct timeval *start) + * static bool was_recent(const struct timespec *start) * { * return time_sub(time_now(), *start).tv_sec < 1; * } */ -struct timeval time_sub(struct timeval recent, struct timeval old); +struct timespec time_sub(struct timespec recent, struct timespec old); /** * time_add - add two times @@ -88,12 +96,12 @@ struct timeval time_sub(struct timeval recent, struct timeval old); * * Example: * // We do one every second. - * static struct timeval next_time(void) + * static struct timespec next_time(void) * { * return time_add(time_now(), time_from_msec(1000)); * } */ -struct timeval time_add(struct timeval a, struct timeval b); +struct timespec time_add(struct timespec a, struct timespec b); /** * time_divide - divide a time by a value. @@ -102,9 +110,9 @@ struct timeval time_add(struct timeval a, struct timeval b); * * Example: * // How long does it take to do a fork? - * static struct timeval forking_time(void) + * static struct timespec forking_time(void) * { - * struct timeval start = time_now(); + * struct timespec start = time_now(); * unsigned int i; * * for (i = 0; i < 1000; i++) { @@ -116,7 +124,7 @@ struct timeval time_add(struct timeval a, struct timeval b); * return time_divide(time_sub(time_now(), start), i); * } */ -struct timeval time_divide(struct timeval t, unsigned long div); +struct timespec time_divide(struct timespec t, unsigned long div); /** * time_multiply - multiply a time by a value. @@ -128,7 +136,7 @@ struct timeval time_divide(struct timeval t, unsigned long div); * printf("Time to do 100000 forks would be %u sec\n", * (unsigned)time_multiply(forking_time(), 1000000).tv_sec); */ -struct timeval time_multiply(struct timeval t, unsigned long mult); +struct timespec time_multiply(struct timespec t, unsigned long mult); /** * time_to_msec - return number of milliseconds @@ -143,7 +151,7 @@ struct timeval time_multiply(struct timeval t, unsigned long mult); * printf("Forking time is %u msec\n", * (unsigned)time_to_msec(forking_time())); */ -uint64_t time_to_msec(struct timeval t); +uint64_t time_to_msec(struct timespec t); /** * time_to_usec - return number of microseconds @@ -159,26 +167,86 @@ uint64_t time_to_msec(struct timeval t); * (unsigned)time_to_usec(forking_time())); * */ -uint64_t time_to_usec(struct timeval t); +uint64_t time_to_usec(struct timespec t); /** - * time_from_msec - convert milliseconds to a timeval + * time_to_nsec - return number of nanoseconds + * @t: a time + * + * It's sometimes more convenient to deal with time values as + * nanoseconds. Note that this will fit into a 32-bit variable if + * it's a time difference of less than ~4 seconds. + * + * Example: + * ... + * printf("Forking time is %u nsec\n", + * (unsigned)time_to_nsec(forking_time())); + * + */ +uint64_t time_to_nsec(struct timespec t); + +/** + * time_from_msec - convert milliseconds to a timespec * @msec: time in milliseconds * * Example: * // 1/2 second timeout * #define TIMEOUT time_from_msec(500) */ -struct timeval time_from_msec(uint64_t msec); +struct timespec time_from_msec(uint64_t msec); /** - * time_from_usec - convert microseconds to a timeval + * time_from_usec - convert microseconds to a timespec * @usec: time in microseconds * * Example: * // 1/2 second timeout * #define TIMEOUT time_from_usec(500000) */ -struct timeval time_from_usec(uint64_t usec); +struct timespec time_from_usec(uint64_t usec); + +/** + * time_from_nsec - convert nanoseconds to a timespec + * @nsec: time in nanoseconds + * + * Example: + * // 1/2 second timeout + * #define TIMEOUT time_from_nsec(500000000) + */ +struct timespec time_from_nsec(uint64_t nsec); +/** + * timespec_to_timeval - convert a timespec to a timeval. + * @ts: a timespec. + * + * Example: + * struct timeval tv; + * + * tv = timespec_to_timeval(time_now()); + */ +static inline struct timeval timespec_to_timeval(struct timespec ts) +{ + struct timeval tv; + tv.tv_sec = ts.tv_sec; + tv.tv_usec = ts.tv_nsec / 1000; + return tv; +} + +/** + * timeval_to_timespec - convert a timeval to a timespec. + * @tv: a timeval. + * + * Example: + * struct timeval tv = { 0, 500 }; + * struct timespec ts; + * + * ts = timeval_to_timespec(tv); + */ +static inline struct timespec timeval_to_timespec(struct timeval tv) +{ + struct timespec ts; + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = tv.tv_usec * 1000; + return ts; +} #endif /* CCAN_TIME_H */ -- 2.39.2