xref: /unit/src/nxt_thread_time.c (revision 223:bf98efe2c55c)
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