1
2 /*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) NGINX, Inc.
5 */
6
7 #include <nxt_main.h>
8
9
10 /*
11 * Each thread keeps several time representations in its thread local
12 * storage:
13 * the monotonic time in nanoseconds since unspecified point in the past,
14 * the real time in seconds and nanoseconds since the Epoch,
15 * the local time and GMT time structs,
16 * and various user-defined text representations of local and GMT times.
17 *
18 * The monotonic time is used mainly by engine timers and is updated after
19 * a kernel operation which can block for unpredictable duration like event
20 * polling. Besides getting the monotonic time is generally faster than
21 * getting the real time, so the monotonic time is also used for milestones
22 * to update cached real time seconds and, if debug log enabled, milliseconds.
23 * As a result, the cached real time is updated at most one time per second
24 * or millisecond respectively. If there is a signal event support or in
25 * multi-threaded mode, then the cached real time and local time structs
26 * are updated only on demand. In single-threaded mode without the signal
27 * event support the cached real and local time are updated synchronously
28 * with the monotonic time update. GMT time structs and text representations
29 * are always updated only on demand.
30 */
31
32
33 static void nxt_time_thread(void *data);
34 static void nxt_thread_time_shared(nxt_monotonic_time_t *now);
35 static void nxt_thread_realtime_update(nxt_thread_t *thr,
36 nxt_monotonic_time_t *now);
37 static u_char *nxt_thread_time_string_no_cache(nxt_thread_t *thr,
38 nxt_time_string_t *ts, u_char *buf);
39 static nxt_atomic_uint_t nxt_thread_time_string_slot(nxt_time_string_t *ts);
40 static nxt_time_string_cache_t *nxt_thread_time_string_cache(nxt_thread_t *thr,
41 nxt_atomic_uint_t slot);
42
43
44 static nxt_atomic_int_t nxt_gmtoff;
45 static nxt_bool_t nxt_use_shared_time = 0;
46 static volatile nxt_monotonic_time_t nxt_shared_time;
47
48
49 void
nxt_thread_time_update(nxt_thread_t * thr)50 nxt_thread_time_update(nxt_thread_t *thr)
51 {
52 if (nxt_use_shared_time) {
53 nxt_thread_time_shared(&thr->time.now);
54
55 } else {
56 nxt_monotonic_time(&thr->time.now);
57 }
58 }
59
60
61 void
nxt_thread_time_free(nxt_thread_t * thr)62 nxt_thread_time_free(nxt_thread_t *thr)
63 {
64 nxt_uint_t i;
65 nxt_time_string_cache_t *tsc;
66
67 tsc = thr->time.strings;
68
69 if (tsc) {
70 thr->time.no_cache = 1;
71
72 for (i = 0; i < thr->time.nstrings; i++) {
73 nxt_free(tsc[i].string.start);
74 }
75
76 nxt_free(tsc);
77 thr->time.strings = NULL;
78 }
79 }
80
81
82 void
nxt_time_thread_start(nxt_msec_t interval)83 nxt_time_thread_start(nxt_msec_t interval)
84 {
85 nxt_thread_link_t *link;
86 nxt_thread_handle_t handle;
87
88 link = nxt_zalloc(sizeof(nxt_thread_link_t));
89
90 if (nxt_fast_path(link != NULL)) {
91 link->start = nxt_time_thread;
92 link->work.data = (void *) (uintptr_t) interval;
93
94 (void) nxt_thread_create(&handle, link);
95 }
96 }
97
98
99 static void
nxt_time_thread(void * data)100 nxt_time_thread(void *data)
101 {
102 nxt_nsec_t interval, rest;
103 nxt_thread_t *thr;
104 nxt_monotonic_time_t now;
105
106 interval = (uintptr_t) data;
107 interval *= 1000000;
108
109 thr = nxt_thread();
110 /*
111 * The time thread is never preempted by asynchronous signals, since
112 * the signals are processed synchronously by dedicated thread.
113 */
114 thr->time.signal = -1;
115
116 nxt_log_debug(thr->log, "time thread");
117
118 nxt_memzero(&now, sizeof(nxt_monotonic_time_t));
119
120 nxt_monotonic_time(&now);
121 nxt_thread_realtime_update(thr, &now);
122
123 nxt_shared_time = now;
124 nxt_use_shared_time = 1;
125
126 for ( ;; ) {
127 rest = 1000000000 - now.realtime.nsec;
128
129 nxt_nanosleep(nxt_min(interval, rest));
130
131 nxt_monotonic_time(&now);
132 nxt_thread_realtime_update(thr, &now);
133
134 nxt_shared_time = now;
135
136 #if 0
137 thr->time.now = now;
138 nxt_log_debug(thr->log, "time thread");
139 #endif
140
141 #if 0
142 if (nxt_exiting) {
143 nxt_use_shared_time = 0;
144 return;
145 }
146 #endif
147 }
148 }
149
150
151 static void
nxt_thread_time_shared(nxt_monotonic_time_t * now)152 nxt_thread_time_shared(nxt_monotonic_time_t *now)
153 {
154 nxt_uint_t n;
155 nxt_time_t t;
156 nxt_nsec_t m, u;
157
158 /* Lock-free thread time update. */
159
160 for ( ;; ) {
161 *now = nxt_shared_time;
162
163 t = nxt_shared_time.realtime.sec;
164 n = nxt_shared_time.realtime.nsec;
165 m = nxt_shared_time.monotonic;
166 u = nxt_shared_time.update;
167
168 if (now->realtime.sec == t && now->realtime.nsec == n
169 && now->monotonic == m && now->update == u)
170 {
171 return;
172 }
173 }
174 }
175
176
177 nxt_time_t
nxt_thread_time(nxt_thread_t * thr)178 nxt_thread_time(nxt_thread_t *thr)
179 {
180 nxt_thread_realtime_update(thr, &thr->time.now);
181
182 return thr->time.now.realtime.sec;
183 }
184
185
186 nxt_realtime_t *
nxt_thread_realtime(nxt_thread_t * thr)187 nxt_thread_realtime(nxt_thread_t *thr)
188 {
189 nxt_thread_realtime_update(thr, &thr->time.now);
190
191 return &thr->time.now.realtime;
192 }
193
194
195 static void
nxt_thread_realtime_update(nxt_thread_t * thr,nxt_monotonic_time_t * now)196 nxt_thread_realtime_update(nxt_thread_t *thr, nxt_monotonic_time_t *now)
197 {
198 nxt_nsec_t delta;
199
200 #if (NXT_DEBUG)
201
202 if (nxt_slow_path(thr->log->level == NXT_LOG_DEBUG || nxt_debug)) {
203
204 if (now->monotonic >= now->update) {
205 nxt_realtime(&now->realtime);
206
207 delta = 1000000 - now->realtime.nsec % 1000000;
208 now->update = now->monotonic + delta;
209 }
210
211 return;
212 }
213
214 #endif
215
216 if (now->monotonic >= now->update) {
217 nxt_realtime(&now->realtime);
218
219 delta = 1000000000 - now->realtime.nsec;
220 now->update = now->monotonic + delta;
221 }
222 }
223
224
225 u_char *
nxt_thread_time_string(nxt_thread_t * thr,nxt_time_string_t * ts,u_char * buf)226 nxt_thread_time_string(nxt_thread_t *thr, nxt_time_string_t *ts, u_char *buf)
227 {
228 u_char *p;
229 struct tm *tm;
230 nxt_time_t s;
231 nxt_bool_t update;
232 nxt_atomic_uint_t slot;
233 nxt_time_string_cache_t *tsc;
234
235 if (nxt_slow_path(thr == NULL || thr->time.no_cache)) {
236 return nxt_thread_time_string_no_cache(thr, ts, buf);
237 }
238
239 slot = nxt_thread_time_string_slot(ts);
240
241 tsc = nxt_thread_time_string_cache(thr, slot);
242 if (tsc == NULL) {
243 return buf;
244 }
245
246 if (thr->time.signal < 0) {
247 /*
248 * Lazy real time update:
249 * signal event support or multi-threaded mode.
250 */
251 nxt_thread_realtime_update(thr, &thr->time.now);
252 }
253
254 s = thr->time.now.realtime.sec;
255
256 update = (s != tsc->last);
257
258 #if (NXT_DEBUG)
259
260 if (ts->msec == NXT_THREAD_TIME_MSEC
261 && (nxt_slow_path(thr->log->level == NXT_LOG_DEBUG || nxt_debug)))
262 {
263 nxt_msec_t ms;
264
265 ms = thr->time.now.realtime.nsec / 1000000;
266 update |= (ms != tsc->last_msec);
267 tsc->last_msec = ms;
268 }
269
270 #endif
271
272 if (nxt_slow_path(update)) {
273
274 if (ts->timezone == NXT_THREAD_TIME_LOCAL) {
275
276 tm = &thr->time.localtime;
277
278 if (nxt_slow_path(s != thr->time.last_localtime)) {
279
280 if (thr->time.signal < 0) {
281 /*
282 * Lazy local time update:
283 * signal event support or multi-threaded mode.
284 */
285 nxt_localtime(s, &thr->time.localtime);
286 thr->time.last_localtime = s;
287
288 } else {
289 /*
290 * "thr->time.signal >= 0" means that a thread may be
291 * interrupted by a signal handler. Since localtime()
292 * cannot be safely called in a signal context, the
293 * thread's thr->time.localtime must be updated regularly
294 * by nxt_thread_time_update() in non-signal context.
295 * Stale timestamp means that nxt_thread_time_string()
296 * is being called in a signal context, so here is
297 * Async-Signal-Safe localtime() emulation using the
298 * latest cached GMT offset.
299 *
300 * The timestamp is not set here intentionally to update
301 * thr->time.localtime later in non-signal context. The
302 * real previously cached thr->localtime is used because
303 * Linux and Solaris strftime() depend on tm.tm_isdst
304 * and tm.tm_gmtoff fields.
305 */
306 nxt_gmtime(s + nxt_timezone(tm), tm);
307 }
308 }
309
310 } else {
311 tm = &thr->time.gmtime;
312
313 if (nxt_slow_path(s != thr->time.last_gmtime)) {
314 nxt_gmtime(s, tm);
315 thr->time.last_gmtime = s;
316 }
317
318 }
319
320 p = tsc->string.start;
321
322 if (nxt_slow_path(p == NULL)) {
323
324 thr->time.no_cache = 1;
325 p = nxt_zalloc(ts->size);
326 thr->time.no_cache = 0;
327
328 if (p == NULL) {
329 return buf;
330 }
331
332 tsc->string.start = p;
333 }
334
335 p = ts->handler(p, &thr->time.now.realtime, tm, ts->size, ts->format);
336
337 tsc->string.length = p - tsc->string.start;
338
339 if (nxt_slow_path(tsc->string.length == 0)) {
340 return buf;
341 }
342
343 tsc->last = s;
344 }
345
346 return nxt_cpymem(buf, tsc->string.start, tsc->string.length);
347 }
348
349
350 static u_char *
nxt_thread_time_string_no_cache(nxt_thread_t * thr,nxt_time_string_t * ts,u_char * buf)351 nxt_thread_time_string_no_cache(nxt_thread_t *thr, nxt_time_string_t *ts,
352 u_char *buf)
353 {
354 struct tm tm;
355 nxt_realtime_t now;
356
357 nxt_realtime(&now);
358
359 if (ts->timezone == NXT_THREAD_TIME_LOCAL) {
360
361 if (thr == NULL || thr->time.signal <= 0) {
362 /* Non-signal context */
363 nxt_localtime(now.sec, &tm);
364
365 } else {
366 nxt_gmtime(now.sec + nxt_gmtoff, &tm);
367 }
368
369 } else {
370 nxt_gmtime(now.sec, &tm);
371 }
372
373 return ts->handler(buf, &now, &tm, ts->size, ts->format);
374 }
375
376
377 static nxt_atomic_uint_t
nxt_thread_time_string_slot(nxt_time_string_t * ts)378 nxt_thread_time_string_slot(nxt_time_string_t *ts)
379 {
380 static nxt_atomic_t slot;
381
382 while (nxt_slow_path((nxt_atomic_int_t) ts->slot < 0)) {
383 /*
384 * Atomic allocation of a slot number.
385 * -1 means an uninitialized slot,
386 * -2 is the initializing lock to assure the single value for the slot.
387 */
388 if (nxt_atomic_cmp_set(&ts->slot, -1, -2)) {
389 ts->slot = nxt_atomic_fetch_add(&slot, 1);
390
391 /* No "break" here since it adds only dispensable "jmp". */
392 }
393 }
394
395 return (nxt_atomic_uint_t) ts->slot;
396 }
397
398
399 static nxt_time_string_cache_t *
nxt_thread_time_string_cache(nxt_thread_t * thr,nxt_atomic_uint_t slot)400 nxt_thread_time_string_cache(nxt_thread_t *thr, nxt_atomic_uint_t slot)
401 {
402 size_t size;
403 nxt_atomic_uint_t i, nstrings;
404 nxt_time_string_cache_t *tsc;
405
406 if (nxt_fast_path(slot < thr->time.nstrings)) {
407 tsc = &thr->time.strings[slot];
408 nxt_prefetch(tsc->string.start);
409 return tsc;
410 }
411
412 nstrings = slot + 1;
413 size = nstrings * sizeof(nxt_time_string_cache_t);
414
415 thr->time.no_cache = 1;
416 tsc = nxt_realloc(thr->time.strings, size);
417 thr->time.no_cache = 0;
418
419 if (tsc == NULL) {
420 return NULL;
421 }
422
423 for (i = thr->time.nstrings; i < nstrings; i++) {
424 tsc[i].last = -1;
425 tsc[i].string.start = NULL;
426 }
427
428 thr->time.strings = tsc;
429 thr->time.nstrings = nstrings;
430
431 return &tsc[slot];
432 }
433