1
2 /*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) NGINX, Inc.
5 */
6
7 #include <nxt_main.h>
8
9
10 /* OS-specific real, monotonic, and local times and timezone update. */
11
12
13 /* Real time. */
14
15 #if (NXT_HAVE_CLOCK_REALTIME_COARSE)
16
17 /*
18 * Linux clock_gettime() resides on the vDSO page. Linux 2.6.32
19 * clock_gettime(CLOCK_REALTIME_COARSE) uses only cached values and does
20 * not read TSC or HPET so it has the kernel jiffy precision (1ms by default)
21 * and it is several times faster than clock_gettime(CLOCK_REALTIME).
22 */
23
24 void
nxt_realtime(nxt_realtime_t * now)25 nxt_realtime(nxt_realtime_t *now)
26 {
27 struct timespec ts;
28
29 (void) clock_gettime(CLOCK_REALTIME_COARSE, &ts);
30
31 now->sec = (nxt_time_t) ts.tv_sec;
32 now->nsec = ts.tv_nsec;
33 }
34
35
36 #elif (NXT_HAVE_CLOCK_REALTIME_FAST)
37
38 /*
39 * FreeBSD 7.0 specific clock_gettime(CLOCK_REALTIME_FAST) may be
40 * 5-30 times faster than clock_gettime(CLOCK_REALTIME) depending
41 * on kern.timecounter.hardware. The clock has a precision of 1/HZ
42 * seconds (HZ is 1000 on modern platforms, thus 1ms precision).
43 * FreeBSD 9.2 clock_gettime() resides on the vDSO page and reads
44 * TSC. clock_gettime(CLOCK_REALTIME_FAST) is the same as
45 * clock_gettime(CLOCK_REALTIME).
46 */
47
48 void
nxt_realtime(nxt_realtime_t * now)49 nxt_realtime(nxt_realtime_t *now)
50 {
51 struct timespec ts;
52
53 (void) clock_gettime(CLOCK_REALTIME_FAST, &ts);
54
55 now->sec = (nxt_time_t) ts.tv_sec;
56 now->nsec = ts.tv_nsec;
57 }
58
59
60 #elif (NXT_HAVE_CLOCK_REALTIME && !(NXT_HPUX))
61
62 /*
63 * clock_gettime(CLOCK_REALTIME) is supported by Linux, FreeBSD 3.0,
64 * Solaris 8, NetBSD 1.3, and AIX. HP-UX supports it too, however,
65 * it is implemented through a call to gettimeofday(). Linux
66 * clock_gettime(CLOCK_REALTIME) resides on the vDSO page and reads
67 * TSC or HPET. FreeBSD 9.2 clock_gettime(CLOCK_REALTIME) resides
68 * on the vDSO page and reads TSC.
69 */
70
71 void
nxt_realtime(nxt_realtime_t * now)72 nxt_realtime(nxt_realtime_t *now)
73 {
74 struct timespec ts;
75
76 (void) clock_gettime(CLOCK_REALTIME, &ts);
77
78 now->sec = (nxt_time_t) ts.tv_sec;
79 now->nsec = ts.tv_nsec;
80 }
81
82
83 #else
84
85 /* MacOSX, HP-UX. */
86
87 void
nxt_realtime(nxt_realtime_t * now)88 nxt_realtime(nxt_realtime_t *now)
89 {
90 struct timeval tv;
91
92 (void) gettimeofday(&tv, NULL);
93
94 now->sec = (nxt_time_t) tv.tv_sec;
95 now->nsec = tv.tv_usec * 1000;
96 }
97
98 #endif
99
100
101 /* Monotonic time. */
102
103 #if (NXT_HAVE_CLOCK_MONOTONIC_COARSE)
104
105 /*
106 * Linux clock_gettime() resides on the vDSO page. Linux 2.6.32
107 * clock_gettime(CLOCK_MONOTONIC_COARSE) uses only cached values and does
108 * not read TSC or HPET so it has the kernel jiffy precision (1ms by default)
109 * and it is several times faster than clock_gettime(CLOCK_MONOTONIC).
110 */
111
112 void
nxt_monotonic_time(nxt_monotonic_time_t * now)113 nxt_monotonic_time(nxt_monotonic_time_t *now)
114 {
115 struct timespec ts;
116
117 (void) clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
118
119 now->monotonic = (nxt_nsec_t) ts.tv_sec * 1000000000 + ts.tv_nsec;
120 }
121
122
123 #elif (NXT_HAVE_CLOCK_MONOTONIC_FAST)
124
125 /*
126 * FreeBSD 7.0 specific clock_gettime(CLOCK_MONOTONIC_FAST) may be
127 * 5-30 times faster than clock_gettime(CLOCK_MONOTONIC) depending
128 * on kern.timecounter.hardware. The clock has a precision of 1/HZ
129 * seconds (HZ is 1000 on modern platforms, thus 1ms precision).
130 * FreeBSD 9.2 clock_gettime() resides on the vDSO page and reads
131 * TSC. clock_gettime(CLOCK_MONOTONIC_FAST) is the same as
132 * clock_gettime(CLOCK_MONOTONIC).
133 */
134
135 void
nxt_monotonic_time(nxt_monotonic_time_t * now)136 nxt_monotonic_time(nxt_monotonic_time_t *now)
137 {
138 struct timespec ts;
139
140 (void) clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
141
142 now->monotonic = (nxt_nsec_t) ts.tv_sec * 1000000000 + ts.tv_nsec;
143 }
144
145
146 #elif (NXT_HAVE_HG_GETHRTIME)
147
148 /*
149 * HP-UX 11.31 provides fast hg_gethrtime() which uses a chunk of memory
150 * shared between userspace application and the kernel, and was introduced
151 * by Project Mercury ("HG").
152 */
153
154 void
nxt_monotonic_time(nxt_monotonic_time_t * now)155 nxt_monotonic_time(nxt_monotonic_time_t *now)
156 {
157 now->monotonic = (nxt_nsec_t) hg_gethrtime();
158 }
159
160
161 #elif (NXT_SOLARIS || NXT_HPUX)
162
163 /*
164 * Solaris gethrtime(), clock_gettime(CLOCK_REALTIME), and gettimeofday()
165 * use a fast systrap whereas clock_gettime(CLOCK_MONOTONIC) and other
166 * clock_gettime()s use normal systrap. However, the difference is
167 * negligible on x86_64.
168 *
169 * HP-UX lacks clock_gettime(CLOCK_MONOTONIC) but has lightweight
170 * system call gethrtime().
171 */
172
173 void
nxt_monotonic_time(nxt_monotonic_time_t * now)174 nxt_monotonic_time(nxt_monotonic_time_t *now)
175 {
176 now->monotonic = (nxt_nsec_t) gethrtime();
177 }
178
179
180 #elif (NXT_HAVE_CLOCK_MONOTONIC)
181
182 /*
183 * clock_gettime(CLOCK_MONOTONIC) is supported by Linux, FreeBSD 5.0,
184 * Solaris 8, NetBSD 1.6, and AIX. Linux clock_gettime(CLOCK_MONOTONIC)
185 * resides on the vDSO page and reads TSC or HPET. FreeBSD 9.2
186 * clock_gettime(CLOCK_MONOTONIC) resides on the vDSO page and reads TSC.
187 */
188
189 void
nxt_monotonic_time(nxt_monotonic_time_t * now)190 nxt_monotonic_time(nxt_monotonic_time_t *now)
191 {
192 struct timespec ts;
193
194 (void) clock_gettime(CLOCK_MONOTONIC, &ts);
195
196 now->monotonic = (nxt_nsec_t) ts.tv_sec * 1000000000 + ts.tv_nsec;
197 }
198
199
200 #elif (NXT_MACOSX)
201
202 /*
203 * MacOSX does not support clock_gettime(), but mach_absolute_time() returns
204 * monotonic ticks. To get nanoseconds the ticks should be multiplied then
205 * divided by numerator/denominator returned by mach_timebase_info(), however
206 * on modern MacOSX they are 1/1. On PowerPC MacOSX these values were
207 * 1000000000/33333335 or 1000000000/25000000, on iOS 4+ they were 125/3,
208 * and on iOS 3 they were 1000000000/24000000.
209 */
210
211 void
nxt_monotonic_time(nxt_monotonic_time_t * now)212 nxt_monotonic_time(nxt_monotonic_time_t *now)
213 {
214 now->monotonic = mach_absolute_time();
215 }
216
217
218 #else
219
220 void
nxt_monotonic_time(nxt_monotonic_time_t * now)221 nxt_monotonic_time(nxt_monotonic_time_t *now)
222 {
223 nxt_nsec_t current;
224 nxt_nsec_int_t delta;
225 struct timeval tv;
226
227 (void) gettimeofday(&tv, NULL);
228
229 now->realtime.sec = (nxt_time_t) tv.tv_sec;
230 now->realtime.nsec = tv.tv_usec * 1000;
231
232 /*
233 * Monotonic time emulation using gettimeofday()
234 * for platforms which lack monotonic time.
235 */
236
237 current = (nxt_nsec_t) tv.tv_sec * 1000000000 + tv.tv_usec * 1000;
238 delta = current - now->previous;
239 now->previous = current;
240
241 if (delta > 0) {
242 now->monotonic += delta;
243
244 } else {
245 /* The time went backward. */
246 now->monotonic++;
247 }
248
249 /*
250 * Eliminate subsequent gettimeofday() call
251 * in nxt_thread_realtime_update().
252 */
253 now->update = now->monotonic + 1;
254 }
255
256 #endif
257
258
259 /* Local time. */
260
261 #if (NXT_HAVE_LOCALTIME_R)
262
263 void
nxt_localtime(nxt_time_t s,struct tm * tm)264 nxt_localtime(nxt_time_t s, struct tm *tm)
265 {
266 time_t _s;
267
268 _s = (time_t) s;
269 (void) localtime_r(&_s, tm);
270 }
271
272
273 #else
274
275 void
nxt_localtime(nxt_time_t s,struct tm * tm)276 nxt_localtime(nxt_time_t s, struct tm *tm)
277 {
278 time_t _s;
279 struct tm *_tm;
280
281 _s = (time_t) s;
282 _tm = localtime(&_s);
283 *tm = *_tm;
284 }
285
286 #endif
287
288
289 /* Timezone update. */
290
291 #if (NXT_LINUX)
292
293 /*
294 * Linux glibc does not test /etc/localtime change
295 * in localtime_r(), but tests in localtime().
296 */
297
298 void
nxt_timezone_update(void)299 nxt_timezone_update(void)
300 {
301 time_t s;
302
303 s = time(NULL);
304 (void) localtime(&s);
305 }
306
307
308 #elif (NXT_FREEBSD)
309
310 /*
311 * FreeBSD libc does not test /etc/localtime change, but it can be
312 * worked around by calling tzset() with TZ and then without TZ
313 * to update timezone. This trick should work since FreeBSD 2.1.0.
314 */
315
316 void
nxt_timezone_update(void)317 nxt_timezone_update(void)
318 {
319 if (getenv("TZ") != NULL) {
320 return;
321 }
322
323 /* The libc uses /etc/localtime if TZ is not set. */
324
325 (void) putenv((char *) "TZ=UTC");
326 tzset();
327
328 (void) unsetenv("TZ");
329 tzset();
330 }
331
332
333 #elif (NXT_SOLARIS)
334
335 /*
336 * Solaris 10, patch 142909-17 introduced tzreload(8):
337 *
338 * The tzreload command notifies active (running) processes to reread
339 * timezone information. The timezone information is cached in each
340 * process, absent a tzreload command, is never reread until a process
341 * is restarted. In response to a tzreload command, active processes
342 * reread the current timezone information at the next call to ctime(3C)
343 * and mktime(3C). By default, the tzreload notification is sent to
344 * the processes within the current zone.
345 */
346
347 void
nxt_timezone_update(void)348 nxt_timezone_update(void)
349 {
350 time_t s;
351
352 s = time(NULL);
353 (void) ctime(&s);
354 }
355
356
357 #else
358
359 void
nxt_timezone_update(void)360 nxt_timezone_update(void)
361 {
362 return;
363 }
364
365 #endif
366