xref: /unit/src/nxt_time.c (revision 0:a63ceefd6ab0)
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