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