From a96e4102522a1433466a13825decd1b739138b7e Mon Sep 17 00:00:00 2001 From: Eddie Kohler Date: Tue, 23 Aug 2011 08:28:01 -0400 Subject: [PATCH] Timestamp: Add recent(), now_steady(), and recent_steady(). The steady() functions return the current time as measured by a steady clock: a monotonic clock that never moves backwards. We call the clock "steady" rather than "monotonic" or "up" because C++0x calls this clock the steady_clock, and that's a good name. The recent() functions return a cached copy of a recent now() or now_steady(). At userlevel at the moment, these functions actually just call now() or now_steady(). In the kernels, though, they will be faster than now() or now_steady(). Signed-off-by: Eddie Kohler --- config-linuxmodule.h.in | 3 + configure | 16 ++ configure.in | 8 + include/click/timerset.hh | 4 +- include/click/timestamp.hh | 322 ++++++++++++++++++++++++++++--------- lib/timestamp.cc | 16 +- userlevel/click.cc | 4 +- 7 files changed, 280 insertions(+), 93 deletions(-) diff --git a/config-linuxmodule.h.in b/config-linuxmodule.h.in index 88f63beece..e637df6761 100644 --- a/config-linuxmodule.h.in +++ b/config-linuxmodule.h.in @@ -52,6 +52,9 @@ /* Define if your Linux kernel has devinet_ioctl. */ #undef HAVE_LINUX_DEVINET_IOCTL +/* Define if your Linux kernel exports get_monotonic_coarse. */ +#undef HAVE_LINUX_GET_MONOTONIC_COARSE + /* Define if your Linux kernel has inet_ctl_sock_create. */ #undef HAVE_LINUX_INET_CTL_SOCK_CREATE diff --git a/configure b/configure index 3142a799d9..692559155d 100755 --- a/configure +++ b/configure @@ -11317,6 +11317,22 @@ $as_echo "$ac_cv_linux_netdev_rx_handler_register" >&6; } fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for get_monotonic_coarse kernel symbol" >&5 +$as_echo_n "checking for get_monotonic_coarse kernel symbol... " >&6; } +if ${ac_cv_linux_get_monotonic_coarse+:} false; then : + $as_echo_n "(cached) " >&6 +else + if grep "__ksymtab_get_monotonic_coarse" $linux_system_map >/dev/null 2>&1; then + ac_cv_linux_get_monotonic_coarse=yes + else ac_cv_linux_get_monotonic_coarse=no; fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_linux_get_monotonic_coarse" >&5 +$as_echo "$ac_cv_linux_get_monotonic_coarse" >&6; } + if test $ac_cv_linux_get_monotonic_coarse = yes; then + $as_echo "#define HAVE_LINUX_GET_MONOTONIC_COARSE 1" >>confdefs.h + + fi + CC="$save_cc" CXX="$save_cxx" CPPFLAGS="$save_cppflags" diff --git a/configure.in b/configure.in index ba464a7242..2d20d1b765 100644 --- a/configure.in +++ b/configure.in @@ -1475,6 +1475,14 @@ void f1(int64_t) { // will fail if long long and int64_t are the same type AC_DEFINE(HAVE_LINUX_NETDEV_RX_HANDLER_REGISTER) fi + AC_CACHE_CHECK([for get_monotonic_coarse kernel symbol], [ac_cv_linux_get_monotonic_coarse], + [if grep "__ksymtab_get_monotonic_coarse" $linux_system_map >/dev/null 2>&1; then + ac_cv_linux_get_monotonic_coarse=yes + else ac_cv_linux_get_monotonic_coarse=no; fi]) + if test $ac_cv_linux_get_monotonic_coarse = yes; then + AC_DEFINE(HAVE_LINUX_GET_MONOTONIC_COARSE) + fi + CC="$save_cc" CXX="$save_cxx" CPPFLAGS="$save_cppflags" diff --git a/include/click/timerset.hh b/include/click/timerset.hh index 0af67bd640..2ff2f8924c 100644 --- a/include/click/timerset.hh +++ b/include/click/timerset.hh @@ -93,7 +93,7 @@ inline Timestamp TimerSet::next_timer_expiry_adjusted() const { Timestamp e = _timer_expiry; -#if CLICK_USERLEVEL +#if TIMESTAMP_WARPABLE if (likely(!Timestamp::warp_jumping())) { #endif if (_timer_stride >= 8 || e.sec() == 0) @@ -102,7 +102,7 @@ TimerSet::next_timer_expiry_adjusted() const e -= Timer::adjustment(); else e -= Timer::adjustment() + Timer::adjustment(); -#if CLICK_USERLEVEL +#if TIMESTAMP_WARPABLE } #endif return e; diff --git a/include/click/timestamp.hh b/include/click/timestamp.hh index a402db899a..abd4170997 100644 --- a/include/click/timestamp.hh +++ b/include/click/timestamp.hh @@ -109,6 +109,14 @@ class String; #endif +// TIMESTAMP_WARPABLE is defined if this Timestamp implementation supports +// timewarping. + +#if !CLICK_LINUXMODULE && !CLICK_BSDMODULE && !CLICK_NS +# define TIMESTAMP_WARPABLE 1 +#endif + + class Timestamp { public: /** @brief Type represents a number of seconds. */ @@ -333,11 +341,7 @@ class Timestamp { public: return t; } - /** @brief Return a timestamp representing the current time. - * - * The current time is measured in seconds since January 1, 1970 GMT. - * @sa assign_now() */ - static inline Timestamp now(); + /** @brief Return the smallest nonzero timestamp, Timestamp(0, 1). */ static inline Timestamp epsilon() { return Timestamp(0, 1); @@ -348,6 +352,7 @@ class Timestamp { public: assign(0, 0); } + /** Set this timestamp to a seconds-and-subseconds value. * * @sa Timestamp(int, int) */ @@ -368,6 +373,7 @@ class Timestamp { public: assign(sec, nsec_to_subsec(nsec)); } + /** @cond never */ /** Assign this timestamp to a seconds-and-subseconds value. * @deprecated Use assign() instead. */ inline void set(seconds_type sec, uint32_t subsec = 0) CLICK_DEPRECATED; @@ -377,23 +383,95 @@ class Timestamp { public: /** Assign this timestamp to a seconds-and-nanoseconds value. * @deprecated Use assign_nsec() instead. */ inline void set_nsec(seconds_type sec, uint32_t nsec) CLICK_DEPRECATED; - - /** @brief Set this timestamp to the current time. - * - * The current time is measured in seconds since January 1, 1970 GMT. - * Returns the most precise timestamp available. - * @sa now() */ - inline void assign_now(); /** @brief Deprecated synonym for assign_now(). * @deprecated Use Timestamp::assign_now() instead. */ inline void set_now() CLICK_DEPRECATED; + /** @endcond never */ #if !CLICK_LINUXMODULE && !CLICK_BSDMODULE int set_timeval_ioctl(int fd, int ioctl_selector); #endif + + /** @brief Return the current system time. + * + * System time is measured in seconds since January 1, 1970 GMT. + * Produces the most precise timestamp available. + * + * @note System time can jump forwards or backwards as a result of user + * actions. For a clock that never moves backwards, see now_steady(). + * @sa recent(), assign_now(), now_steady() */ + static inline Timestamp now(); + + /** @brief Set this timestamp to the current system time. + * + * Like "*this = Timestamp::now()". + * @sa now(), assign_recent() */ + inline void assign_now(); + + /** @brief Return a recent system time. + * + * The Timestamp::now() function calculates the current system time, which + * is relatively expensive. Timestamp::recent() can be faster, but is + * less precise: it returns a cached copy of a recent system time. + * @sa now(), assign_recent() */ + static inline Timestamp recent(); + + /** @brief Set this timestamp to a recent system time. + * + * Like "*this = Timestamp::recent()". + * @sa recent(), assign_now() */ + inline void assign_recent(); + + + /** @brief Return the current steady-clock time. + * + * The steady clock, often called a monotonic clock, is a system clock + * that never moves backwards. Steady-clock time is measured in seconds + * since an undefined start point (often related to the most recent boot). + * Produces the most precise timestamp available. + * + * @note Steady-clock times and system times are incomparable, since they + * have different start points. + * + * @sa recent_steady(), assign_now_steady() */ + static inline Timestamp now_steady(); + + /** @brief Set this timestamp to the current steady-clock time. + * + * Like "*this = Timestamp::now_steady()". + * @sa now_steady() */ + inline void assign_now_steady(); + + /** @brief Return a recent steady-clock time. + * + * The Timestamp::now_steady() function calculates the current + * steady-clock time, which is relatively expensive. + * Timestamp::recent_steady() can be faster, but is less precise: it + * returns a cached copy of a recent steady-clock time. + * @sa now_steady(), assign_recent_steady() */ + static inline Timestamp recent_steady(); + + /** @brief Set this timestamp to a recent steady-clock time. + * + * Like "*this = Timestamp::recent_steady()". + * @sa recent_steady(), assign_now_steady() */ + inline void assign_recent_steady(); + + + /** @brief Unparse this timestamp into a String. + * + * Returns a string formatted like "10.000000", with at least six + * subsecond digits. (Nanosecond-precision timestamps where the number of + * nanoseconds is not evenly divisible by 1000 are given nine subsecond + * digits.) */ String unparse() const; + + /** @brief Unparse this timestamp into a String as an interval. + * + * Returns a string formatted like "1us" or "1.000002s". */ String unparse_interval() const; + /** @brief Convert milliseconds to subseconds. * * Subseconds are either microseconds or nanoseconds, depending on @@ -458,7 +536,7 @@ class Timestamp { public: #endif }; -#if !CLICK_LINUXMODULE && !CLICK_BSDMODULE +#if TIMESTAMP_WARPABLE /** @name Timewarping */ //@{ enum warp_class_type { @@ -542,24 +620,21 @@ class Timestamp { public: * * Does nothing if warp_jumping() is false or @a expiry is in the past. */ static void warp_jump(const Timestamp &expiry); - //@} -#endif - /** @brief Return a timestamp representing the current real time. + /** @brief Return the warp-free current system time. * - * The current time is measured in seconds since January 1, 1970 GMT. - * The time returned is unaffected by timewarping. - * @sa assign_now() */ - static inline Timestamp now_real_time(); + * Like now(), but the time returned is unaffected by timewarping. + * @sa now(), assign_now_unwarped() */ + static inline Timestamp now_unwarped(); - /** @brief Set this timestamp to the current real time. + /** @brief Set this timestamp to the warp-free current system time. * - * The current time is measured in seconds since January 1, 1970 GMT. - * Returns the most precise timestamp available. The time returned is - * unaffected by timewarping. - * @sa assign_now(), now_real_time() */ - inline void assign_now_real_time(); + * Like assign_now(), but the time assigned is unaffected by timewarping. + * @sa assign_now(), now_unwarped() */ + inline void assign_now_unwarped(); + //@} +#endif private: @@ -600,9 +675,9 @@ class Timestamp { public: div = quot; } - inline void assign_now(bool raw); + inline void assign_now(bool recent, bool steady, bool unwarped); -#if !CLICK_LINUXMODULE && !CLICK_BSDMODULE +#if TIMESTAMP_WARPABLE static warp_class_type _warp_class; static Timestamp _warp_flat_offset; static double _warp_speed; @@ -657,94 +732,187 @@ Timestamp::operator unspecified_bool_type() const } inline void -Timestamp::assign_now(bool raw) +Timestamp::assign_now(bool recent, bool steady, bool unwarped) { -#if TIMESTAMP_NANOSEC && (CLICK_LINUXMODULE || CLICK_BSDMODULE || HAVE_USE_CLOCK_GETTIME) - // nanosecond precision -# if TIMESTAMP_PUNS_TIMESPEC - struct timespec *tsp = &_t.tspec; -# else - struct timespec ts, *tsp = &ts; -# endif -# if CLICK_LINUXMODULE - getnstimeofday(tsp); -# elif CLICK_BSDMODULE - nanotime(tsp); // This is the more precise function -# elif CLICK_USERLEVEL || CLICK_TOOL - clock_gettime(CLOCK_REALTIME, tsp); -# else -# error "unknown driver" -# endif -# if !TIMESTAMP_PUNS_TIMESPEC - assign(ts.tv_sec, nsec_to_subsec(ts.tv_nsec)); -# endif + (void) recent, (void) steady, (void) unwarped; +#if TIMESTAMP_PUNS_TIMESPEC +# define TIMESTAMP_DECLARE_TSP struct timespec &tsp = _t.tspec +# define TIMESTAMP_RESOLVE_TSP /* nothing */ #else - // microsecond precision -# if TIMESTAMP_PUNS_TIMEVAL - struct timeval *tvp = &_t.tv; -# else - struct timeval tv, *tvp = &tv; +# define TIMESTAMP_DECLARE_TSP struct timespec ts, &tsp = ts +# define TIMESTAMP_RESOLVE_TSP assign(tsp.tv_sec, nsec_to_subsec(tsp.tv_nsec)) +#endif +#if TIMESTAMP_PUNS_TIMEVAL +# define TIMESTAMP_DECLARE_TVP struct timeval &tvp = _t.tv +# define TIMESTAMP_RESOLVE_TVP /* nothing */ +#else +# define TIMESTAMP_DECLARE_TVP struct timeval tv, &tvp = tv +# define TIMESTAMP_RESOLVE_TVP assign(tvp.tv_sec, usec_to_subsec(tvp.tv_usec)) +#endif + +#if CLICK_LINUXMODULE +# if !TIMESTAMP_NANOSEC + if (!recent && !steady) { + TIMESTAMP_DECLARE_TVP; + do_gettimeofday(&tvp); + TIMESTAMP_RESOLVE_TVP; + return; + } # endif -# if CLICK_LINUXMODULE - do_gettimeofday(tvp); -# elif CLICK_BSDMODULE - microtime(tvp); -# elif CLICK_NS - simclick_gettimeofday(tvp); -# elif CLICK_USERLEVEL || CLICK_TOOL - gettimeofday(tvp, (struct timezone *) 0); + TIMESTAMP_DECLARE_TSP; + if (recent && steady) { +# if HAVE_LINUX_GET_MONOTONIC_COARSE + tsp = get_monotonic_coarse(); # else -# error "unknown driver" -# endif -# if !TIMESTAMP_PUNS_TIMEVAL - assign(tv.tv_sec, usec_to_subsec(tv.tv_usec)); + tsp = current_kernel_time(); + struct timespec delta; + getboottime(&delta); + monotonic_to_bootbased(&delta); + set_normalized_timespec(&tsp, tsp.tv_sec - delta.tv_sec, + tsp.tv_nsec - delta.tv_nsec); # endif + } else if (recent) + tsp = current_kernel_time(); + else if (steady) + ktime_get_ts(&tsp); + else + getnstimeofday(&tsp); + TIMESTAMP_RESOLVE_TSP; + +#elif TIMESTAMP_NANOSEC && CLICK_BSDMODULE + TIMESTAMP_DECLARE_TSP; + if (recent && steady) + getnanouptime(&tsp); + else if (recent) + getnanotime(&tsp); + else if (steady) + nanouptime(&tsp); + else + nanotime(&tsp); + TIMESTAMP_RESOLVE_TSP; + +#elif CLICK_BSDMODULE + TIMESTAMP_DECLARE_TVP; + if (recent && steady) + getmicrouptime(&tvp); + else if (recent) + getmicrotime(&tvp); + else if (steady) + microuptime(&tvp); + else + microtime(&tvp); + TIMESTAMP_RESOLVE_TVP; + +#elif CLICK_NS + TIMESTAMP_DECLARE_TVP; + simclick_gettimeofday(tvp); + TIMESTAMP_RESOLVE_TVP; + +#elif HAVE_USE_CLOCK_GETTIME + TIMESTAMP_DECLARE_TSP; + if (steady) + clock_gettime(CLOCK_MONOTONIC, &tsp); + else + clock_gettime(CLOCK_REALTIME, &tsp); + TIMESTAMP_RESOLVE_TSP; + +#else + TIMESTAMP_DECLARE_TVP; + gettimeofday(&tvp, (struct timezone *) 0); + TIMESTAMP_RESOLVE_TVP; #endif +#undef TIMESTAMP_DECLARE_TSP +#undef TIMESTAMP_RESOLVE_TSP +#undef TIMESTAMP_DECLARE_TVP +#undef TIMESTAMP_RESOLVE_TVP + +#if TIMESTAMP_WARPABLE // timewarping -#if CLICK_USERLEVEL - if (!raw && _warp_class) + if (!unwarped && _warp_class) warp(true); -#else - (void) raw; #endif } inline void Timestamp::assign_now() { - assign_now(false); + assign_now(false, false, false); } inline void Timestamp::set_now() { - assign_now(false); + assign_now(false, false, false); } inline Timestamp Timestamp::now() { Timestamp t = Timestamp::uninitialized_t(); - t.assign_now(false); + t.assign_now(); return t; } inline void -Timestamp::assign_now_real_time() +Timestamp::assign_recent() { - assign_now(true); + assign_now(true, false, false); } inline Timestamp -Timestamp::now_real_time() +Timestamp::recent() { Timestamp t = Timestamp::uninitialized_t(); - t.assign_now(true); + t.assign_recent(); return t; } +inline void +Timestamp::assign_now_steady() +{ + assign_now(false, true, false); +} + +inline Timestamp +Timestamp::now_steady() +{ + Timestamp t = Timestamp::uninitialized_t(); + t.assign_now_steady(); + return t; +} + +inline void +Timestamp::assign_recent_steady() +{ + assign_now(true, true, false); +} + +inline Timestamp +Timestamp::recent_steady() +{ + Timestamp t = Timestamp::uninitialized_t(); + t.assign_recent_steady(); + return t; +} + +#if TIMESTAMP_WARPABLE +inline void +Timestamp::assign_now_unwarped() +{ + assign_now(false, false, true); +} + +inline Timestamp +Timestamp::now_unwarped() +{ + Timestamp t = Timestamp::uninitialized_t(); + t.assign_now_unwarped(); + return t; +} +#endif + /** @brief Set this timestamp's seconds component. The subseconds component is left unchanged. */ @@ -1230,7 +1398,7 @@ operator/(const Timestamp &a, const Timestamp &b) StringAccum& operator<<(StringAccum&, const Timestamp&); -#if !CLICK_LINUXMODULE && !CLICK_BSDMODULE +#if TIMESTAMP_WARPABLE inline Timestamp Timestamp::warp_real_delay() const { diff --git a/lib/timestamp.cc b/lib/timestamp.cc index aec5d4b5e7..d84632b29a 100644 --- a/lib/timestamp.cc +++ b/lib/timestamp.cc @@ -55,7 +55,7 @@ CLICK_DECLS -1, usec() == +900000. */ -#if !CLICK_LINUXMODULE && !CLICK_BSDMODULE +#if TIMESTAMP_WARPABLE Timestamp::warp_class_type Timestamp::_warp_class = Timestamp::warp_none; Timestamp Timestamp::_warp_flat_offset = Timestamp(0, 0); double Timestamp::_warp_speed = 1.0; @@ -111,7 +111,7 @@ void Timestamp::warp_set_now(const Timestamp &t) { Timestamp now_raw = Timestamp::uninitialized_t(); - now_raw.assign_now(true); + now_raw.assign_now_unwarped(); warp_adjust(now_raw, t); } @@ -120,7 +120,7 @@ Timestamp::warp_set_speed(double f) { assert(f > 0); Timestamp now_raw = Timestamp::uninitialized_t(); - now_raw.assign_now(true); + now_raw.assign_now_unwarped(); Timestamp now_adj = now_raw.warped(); _warp_speed = f; if (_warp_class < warp_nowait) @@ -135,7 +135,7 @@ Timestamp::warp_jump(const Timestamp &expiry) _warp_flat_offset = expiry; } else if (_warp_class == warp_nowait) { Timestamp now_raw = Timestamp::uninitialized_t(); - now_raw.assign_now(true); + now_raw.assign_now_unwarped(); if (now_raw.warped() < expiry) warp_adjust(now_raw, expiry); } @@ -222,11 +222,6 @@ operator<<(StringAccum &sa, const Timestamp& ts) return sa; } -/** @brief Unparse this timestamp into a String. - - Returns a string formatted like "10.000000", with at least six subsecond - digits. (Nanosecond-precision timestamps where the number of nanoseconds - is not evenly divisible by 1000 are given nine subsecond digits.) */ String Timestamp::unparse() const { @@ -235,9 +230,6 @@ Timestamp::unparse() const return sa.take_string(); } -/** @brief Unparse this timestamp into a String as an interval. - - Returns a string formatted like "1us" or "1.000002s". */ String Timestamp::unparse_interval() const { diff --git a/userlevel/click.cc b/userlevel/click.cc index 0c577c36bd..16bf129b3b 100644 --- a/userlevel/click.cc +++ b/userlevel/click.cc @@ -641,7 +641,7 @@ particular purpose.\n"); struct rusage before, after; getrusage(RUSAGE_SELF, &before); - Timestamp before_time = Timestamp::now_real_time(); + Timestamp before_time = Timestamp::now_unwarped(); Timestamp after_time = Timestamp::uninitialized_t(); // run driver @@ -666,7 +666,7 @@ particular purpose.\n"); } else if (!quit_immediately && warnings) errh->warning("%s: configuration has no elements, exiting", filename_landmark(router_file, file_is_expr)); - after_time.assign_now_real_time(); + after_time.assign_now_unwarped(); getrusage(RUSAGE_SELF, &after); // report time if (report_time) {