diff options
Diffstat (limited to 'lib/qtime.c')
-rw-r--r-- | lib/qtime.c | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/lib/qtime.c b/lib/qtime.c new file mode 100644 index 00000000..cdcf09f9 --- /dev/null +++ b/lib/qtime.c @@ -0,0 +1,128 @@ +/* Quagga Realtime Clock handling -- functions + * Copyright (C) 2009 Chris Hall (GMCH), Highwayman + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <sys/times.h> +#include <errno.h> + +#include "zassert.h" +#include "qtime.h" + +/*============================================================================== + * This is a collection of functions and (in qtime.h) macros and inline + * functions which support system time and a monotonic clock. + */ + +/*============================================================================== + * Replacement for CLOCK_MONOTONIC. + * + * With thanks to Joakim Tjernlund for reminding everyone of the return value + * from times() ! + * + * times() is defined to return a value which is the time since some fixed time + * before the application started (or when the application started). This time + * is measured in units of sysconf(_SC_CLK_TCK) ticks per second. + * + * The only tricky bit is that the value returned (of type clock_t) is a + * signed integer, which may wrap round. It is not defined exactly how it + * does this... but it is generally assumed that this_sample - last_sample will + * give the time between the samples. + * + * The qtime_t value is in nano-seconds. + * + * The result from times() is in units of sysconf(_SC_CLK_TCK). + * + * NB: it is assumed that qt_craft_monotonic will be called often enough to + * ensure that it is not fooled by the clock wrapping round. + * + * Assuming that clock_t is a signed 32-bit integer, which is kept +ve, + * then the clock wraps round in 2^31 ticks which is: + * + * at 1,000 ticks/sec: > 24 days ! + * at 1,000,000 ticks/sec: > 35 minutes + * + * So this should be a safe assumption -- particularly as 60, 100, 250 and + * 1000 ticks per second appear to be the popular options. + * + * For safety, this asserts that the value is <= 1,000,000. + */ + +#ifdef GNU_LINUX +#define TIMES_TAKES_NULL 1 +#else +#undef TIMES_TAKES_NULL +#endif + +static uint64_t monotonic = 0 ; /* monotonic clock in _SC_CLK_TCK's */ +static uint64_t last_times_sample = 0 ; /* last value returned by times() */ + +static int64_t times_clk_tcks = 0 ; /* sysconf(_SC_CLK_TCK) */ +static qtime_t times_scale_q = 0 ; /* 10**9 / times_clk_tcks */ +static qtime_t times_scale_r = 0 ; /* 10**9 % times_clk_tcks */ + +qtime_t +qt_craft_monotonic(void) { + struct tms dummy ; + uint64_t this_times_sample ; + + /* No errors are defined for times(), but a return of -1 is defined */ + /* to indicate an error condition, with errno saying what it is ! */ + /* */ + /* The following deals carefully with this -- cannot afford for the */ + /* clock either to jump or to get stuck ! */ + +#ifdef TIMES_TAKES_NULL + this_times_sample = times(NULL) ; /* assume this saves effort ! */ +#else + this_times_sample = times(&dummy) ; +#endif + + if (this_times_sample == (clock_t)-1) + { + errno = 0 ; + this_times_sample = times(&dummy) ; + if (errno != 0) + zabort_errno("times() failed") ; + } ; + + + if (times_clk_tcks == 0) /* Is zero until it's initialized */ + { + lldiv_t qr ; + confirm(sizeof(qtime_t) <= sizeof(long long int)) ; + + times_clk_tcks = sysconf(_SC_CLK_TCK) ; + passert((times_clk_tcks > 0) && (times_clk_tcks <= 1000000)) ; + + qr = lldiv(QTIME_SECOND, times_clk_tcks) ; + times_scale_q = qr.quot ; + times_scale_r = qr.rem ; + + last_times_sample = this_times_sample ; + } ; + + monotonic += (this_times_sample - last_times_sample) ; + + if (times_scale_r == 0) + return monotonic * times_scale_q ; + else + return (monotonic * times_scale_q) + + ((monotonic * times_scale_r) / times_clk_tcks) ; +} ; |