f0751a849ea478ebc8be7541225e6ec9b8004122
[darkstat-debian] / 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
21 #include <assert.h>
22 #include <string.h>
23 #include <time.h>
24
25 #ifdef __MACH__
26 /* Fake up clock_gettime() on OS X. */
27 # include <sys/time.h>
28 # include <inttypes.h>
29 # include <mach/mach.h>
30 # include <mach/mach_time.h>
31
32 typedef int clockid_t;
33 #define CLOCK_REALTIME 0
34 #define CLOCK_MONOTONIC 1
35
36 static uint64_t mono_first = 0;
37
38 int clock_gettime(clockid_t clk_id, struct timespec *tp) {
39 if (clk_id == CLOCK_REALTIME) {
40 struct timeval tv;
41 gettimeofday(&tv, NULL);
42 tp->tv_sec = tv.tv_sec;
43 tp->tv_nsec = tv.tv_usec * 1000;
44 return 0;
45 }
46 if (clk_id == CLOCK_MONOTONIC) {
47 uint64_t t = mach_absolute_time();
48 mach_timebase_info_data_t timebase;
49 mach_timebase_info(&timebase);
50 if (!mono_first) {
51 mono_first = t;
52 }
53 uint64_t tdiff = (t - mono_first) * timebase.numer / timebase.denom;
54 tp->tv_sec = tdiff / 1000000000;
55 tp->tv_nsec = tdiff % 1000000000;
56 return 0;
57 }
58 return -1;
59 }
60 #endif /* __MACH__ */
61
62 static struct timespec clock_real, clock_mono;
63 static int now_initialized = 0;
64
65 long now_real(void) {
66 assert(now_initialized);
67 return clock_real.tv_sec;
68 }
69
70 long now_mono(void) {
71 assert(now_initialized);
72 return clock_mono.tv_sec;
73 }
74
75 static int before(const struct timespec *a, const struct timespec *b) {
76 if (a->tv_sec < b->tv_sec)
77 return 1;
78 if (a->tv_sec == b->tv_sec && a->tv_nsec < b->tv_nsec)
79 return 1;
80 return 0;
81 }
82
83 static void clock_update(const clockid_t clk_id,
84 struct timespec *dest,
85 const char *name) {
86 struct timespec t;
87
88 clock_gettime(clk_id, &t);
89 if (now_initialized && before(&t, dest)) {
90 verbosef("%s clock went backwards from %ld.%09ld to %ld.%09ld",
91 name,
92 (long)dest->tv_sec,
93 (long)dest->tv_nsec,
94 (long)t.tv_sec,
95 (long)t.tv_nsec);
96 }
97 memcpy(dest, &t, sizeof(t));
98 }
99
100 static void all_clocks_update(void) {
101 clock_update(CLOCK_REALTIME, &clock_real, "realtime");
102 clock_update(CLOCK_MONOTONIC, &clock_mono, "monotonic");
103 }
104
105 void now_init(void) {
106 assert(!now_initialized);
107 all_clocks_update();
108 now_initialized = 1;
109 }
110
111 void now_update(void) {
112 assert(now_initialized);
113 all_clocks_update();
114 }
115
116 long mono_to_real(const long t) {
117 assert(now_initialized);
118 return t - clock_mono.tv_sec + clock_real.tv_sec;
119 }
120
121 long real_to_mono(const long t) {
122 assert(now_initialized);
123 return t - clock_real.tv_sec + clock_mono.tv_sec;
124 }
125
126 void timer_start(struct timespec *t) {
127 clock_gettime(CLOCK_MONOTONIC, t);
128 }
129
130 static int64_t ts_diff(const struct timespec * const a,
131 const struct timespec * const b) {
132 return (int64_t)(a->tv_sec - b->tv_sec) * 1000000000 +
133 a->tv_nsec - b->tv_nsec;
134 }
135
136 void timer_stop(const struct timespec * const t,
137 const int64_t nsec,
138 const char *warning) {
139 struct timespec t2;
140 int64_t diff;
141
142 clock_gettime(CLOCK_MONOTONIC, &t2);
143 diff = ts_diff(&t2, t);
144 assert(diff > 0);
145 if (diff > nsec)
146 warnx("%s (took %lld nsec, over threshold of %lld nsec)",
147 warning,
148 (long long)diff,
149 (long long)nsec);
150 }
151
152 /* vim:set ts=3 sw=3 tw=80 et: */