1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
|
/* Quagga realtime and monotonic 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.
*
* TODO: introduce mutex for crafted monotonic time, and initialisation
* routine for that: which can preset the various variables... but
* unless is guaranteed to be called, must leave the on-the-fly
* initialisation... could also start a watchdog at that point.
*/
/*==============================================================================
* 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 can overflow. It is not defined exactly how it
* does this... This code assumes that the system will wrap around in some
* obvious way. The base of the time for this clock may be when the *system*
* started... so when it overflows may depend on how long the system has been
* up... which suggests that some sensible wrap around is likely (?).
*
* The qtime_t value is in nano-seconds.
*
* The result from times() is in units of sysconf(_SC_CLK_TCK) ticks per second.
*
* If clock_t is a signed 32-bit integer, which is kept +ve, then the clock
* overflows/wraps round in 2^31 ticks which is:
*
* at 100 ticks/sec: > 248 days
* at 1,000 ticks/sec: > 24 days
* at 10,000 ticks/sec: > 59 hours
*
* For safety, this asserts that sysconf(_SC_CLK_TCK) <= 1,000,000 for
* sizeof(clock_t) > 4, but <= 1,000 for sizeof(clock_t) == 4.
*
* (It appears that 60, 100, 250 and 1,000 ticks/sec. are popular options.)
*
* If sizeof(clock_t) > 4, it is assumed large enough never to wrap around.
*
* When clock_t is a 32-bit integer must be at least ready for wrap around.
* There are two cases:
*
* * +ve wrap around. new < old value, and new >= 0
*
* step = (INT32_MAX - old + 1) + new
*
* * -ve wrap around. new < old value, and new < 0 (and old > 0)
*
* step = (INT32_MAX - old + 1) - (INT32_MIN - new)
*
* In any event, a step > 24 hours is taken to means that something has gone
* very, very badly wrong.
*
* NB: it is assumed that qt_craft_monotonic will be called often enough to
* ensure that the check on the step size will not be triggered !
*
* NB: it is assumed that times() does not simply stick if it overflows.
*
* TODO: Add a watchdog to monitor the behaviour of this clock ?
*/
CONFIRM((sizeof(clock_t) >= 4) && (sizeof(clock_t) <= 8)) ;
#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 int64_t last_times_sample = 0 ; /* last value returned by times() */
static uint64_t step_limit = 0 ; /* for sanity check */
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_mono_t
qt_craft_monotonic(void) {
struct tms dummy ;
int64_t this_times_sample ;
uint64_t step ;
/* Set up times_scale_q & times_scale_q if not yet done. */
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 <= (sizeof(clock_t) > 4) ? 1000000
: 1000)) ;
qr = lldiv(QTIME_SECOND, times_clk_tcks) ;
times_scale_q = qr.quot ;
times_scale_r = qr.rem ;
step_limit = (uint64_t)24 * 60 * 60 * times_clk_tcks ;
} ;
/* 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 == -1) /* deal with theoretical error */
{
errno = 0 ;
this_times_sample = times(&dummy) ;
if (errno != 0)
zabort_errno("times() failed") ;
} ;
/* Calculate the step and verify sensible. */
/* */
/* Watch out for huge jumps and/or time going backwards. */
/* For 32-bit clock_t, look out for wrap-around. */
if ((sizeof(clock_t) > 4) || (this_times_sample > last_times_sample))
/* time going backwards will appear as HUGE step forwards. */
step = (uint64_t)(this_times_sample - last_times_sample) ;
else
{
if (this_times_sample > 0)
/* both samples +ve => +ve wrap around. */
step = (uint64_t)( ((int64_t)INT32_MAX - last_times_sample + 1)
+ this_times_sample ) ;
else
/* this sample -ve and last sample +ve => -ve wrap round */
/* this sample -ve and last sample -ve => time gone backwards */
/* (which appears as a HUGE step forwards). */
step = (uint64_t)( ((int64_t)INT32_MAX - last_times_sample + 1)
- ((int64_t)INT32_MIN - this_times_sample) ) ;
} ;
/* TODO: better error messaging for large clock jumps. */
if (step > step_limit)
zabort("Sudden large monotonic clock jump") ;
/* Advance the monotonic clock in sysconf(_SC_CLK_TCK) units. */
monotonic += step ;
/* Remember what we got, for next time. */
last_times_sample = this_times_sample ;
/* Scale to qtime_t units. */
if (times_scale_r == 0)
return monotonic * times_scale_q ;
else
return (monotonic * times_scale_q) +
((monotonic * times_scale_r) / times_clk_tcks) ;
} ;
/*------------------------------------------------------------------------------
* Get crafted monotonic time -- in seconds
*/
extern time_t
qt_craft_mono_secs(void)
{
qt_craft_monotonic() ; /* update the monotonic counter */
return monotonic / times_clk_tcks ;
} ;
|