summaryrefslogtreecommitdiffstats
path: root/lib/qtime.c
blob: cdcf09f91e1454c0b2fe106f4fbacf017a267f61 (plain)
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
/* 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) ;
} ;