Don't die from assert() in timer_stop() if time goes backwards.
[darkstat] / now.c
1 /* darkstat 3
2 * copyright (c) 2012 Emil Mikulic.
3 *
4 * now.c: a cache of the current time.
5 *
6 * Permission to use, copy, modify, and distribute this file for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 #include "err.h"
19 #include "now.h"
20 #include "str.h"
21
22 #include <assert.h>
23 #include <string.h>
24 #include <time.h>
25
26 #ifdef __MACH__
27 /* Fake up clock_gettime() on OS X. */
28 # include <sys/time.h>
29 # include <inttypes.h>
30 # include <mach/mach.h>
31 # include <mach/mach_time.h>
32
33 typedef int clockid_t;
34 #define CLOCK_REALTIME 0
35 #define CLOCK_MONOTONIC 1
36
37 static uint64_t mono_first = 0;
38
39 int clock_gettime(clockid_t clk_id, struct timespec *tp) {
40 if (clk_id == CLOCK_REALTIME) {
41 struct timeval tv;
42 gettimeofday(&tv, NULL);
43 tp->tv_sec = tv.tv_sec;
44 tp->tv_nsec = tv.tv_usec * 1000;
45 return 0;
46 }
47 if (clk_id == CLOCK_MONOTONIC) {
48 uint64_t t = mach_absolute_time();
49 mach_timebase_info_data_t timebase;
50 mach_timebase_info(&timebase);
51 if (!mono_first) {
52 mono_first = t;
53 }
54 uint64_t tdiff = (t - mono_first) * timebase.numer / timebase.denom;
55 tp->tv_sec = tdiff / 1000000000;
56 tp->tv_nsec = tdiff % 1000000000;
57 return 0;
58 }
59 return -1;
60 }
61 #endif /* __MACH__ */
62
63 static struct timespec clock_real, clock_mono;
64 static int now_initialized = 0;
65
66 time_t now_real(void) {
67 assert(now_initialized);
68 return clock_real.tv_sec;
69 }
70
71 time_t now_mono(void) {
72 assert(now_initialized);
73 return clock_mono.tv_sec;
74 }
75
76 static int before(const struct timespec *a, const struct timespec *b) {
77 if (a->tv_sec < b->tv_sec)
78 return 1;
79 if (a->tv_sec == b->tv_sec && a->tv_nsec < b->tv_nsec)
80 return 1;
81 return 0;
82 }
83
84 static void warn_backwards(const char *name,
85 const struct timespec * const t0,
86 const struct timespec * const t1) {
87 verbosef("%s clock went backwards from %lld.%09lld to %lld.%09lld",
88 name,
89 (lld)t0->tv_sec,
90 (lld)t0->tv_nsec,
91 (lld)t1->tv_sec,
92 (lld)t1->tv_nsec);
93 }
94
95 static void clock_update(const clockid_t clk_id,
96 struct timespec *dest,
97 const char *name) {
98 struct timespec t;
99
100 clock_gettime(clk_id, &t);
101 if (now_initialized && before(&t, dest)) {
102 warn_backwards(name, &t, dest);
103 }
104 memcpy(dest, &t, sizeof(t));
105 }
106
107 static void all_clocks_update(void) {
108 clock_update(CLOCK_REALTIME, &clock_real, "realtime");
109 clock_update(CLOCK_MONOTONIC, &clock_mono, "monotonic");
110 }
111
112 void now_init(void) {
113 assert(!now_initialized);
114 all_clocks_update();
115 now_initialized = 1;
116 }
117
118 void now_update(void) {
119 assert(now_initialized);
120 all_clocks_update();
121 }
122
123 time_t mono_to_real(const time_t t) {
124 assert(now_initialized);
125 return t - clock_mono.tv_sec + clock_real.tv_sec;
126 }
127
128 time_t real_to_mono(const time_t t) {
129 assert(now_initialized);
130 return t - clock_real.tv_sec + clock_mono.tv_sec;
131 }
132
133 void timer_start(struct timespec *t) {
134 clock_gettime(CLOCK_MONOTONIC, t);
135 }
136
137 static int64_t ts_diff(const struct timespec * const a,
138 const struct timespec * const b) {
139 return (int64_t)(a->tv_sec - b->tv_sec) * 1000000000 +
140 a->tv_nsec - b->tv_nsec;
141 }
142
143 void timer_stop(const struct timespec * const t0,
144 const int64_t nsec,
145 const char *warning) {
146 struct timespec t1;
147 int64_t diff;
148
149 clock_gettime(CLOCK_MONOTONIC, &t1);
150 if (before(&t1, t0)) {
151 warn_backwards("monotonic timer", t0, &t1);
152 return;
153 }
154 diff = ts_diff(&t1, t0);
155 if (diff > nsec) {
156 warnx("%s (took %lld nsec, over threshold of %lld nsec)",
157 warning,
158 (lld)diff,
159 (lld)nsec);
160 }
161 }
162
163 /* vim:set ts=3 sw=3 tw=80 et: */