/src/unit/src/nxt_thread_time.c
Line | Count | Source (jump to first uncovered line) |
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 |
50 | | nxt_thread_time_update(nxt_thread_t *thr) |
51 | 2 | { |
52 | 2 | if (nxt_use_shared_time) { |
53 | 0 | nxt_thread_time_shared(&thr->time.now); |
54 | |
|
55 | 2 | } else { |
56 | 2 | nxt_monotonic_time(&thr->time.now); |
57 | 2 | } |
58 | 2 | } |
59 | | |
60 | | |
61 | | void |
62 | | nxt_thread_time_free(nxt_thread_t *thr) |
63 | 0 | { |
64 | 0 | nxt_uint_t i; |
65 | 0 | nxt_time_string_cache_t *tsc; |
66 | |
|
67 | 0 | tsc = thr->time.strings; |
68 | |
|
69 | 0 | if (tsc) { |
70 | 0 | thr->time.no_cache = 1; |
71 | |
|
72 | 0 | for (i = 0; i < thr->time.nstrings; i++) { |
73 | 0 | nxt_free(tsc[i].string.start); |
74 | 0 | } |
75 | |
|
76 | 0 | nxt_free(tsc); |
77 | 0 | thr->time.strings = NULL; |
78 | 0 | } |
79 | 0 | } |
80 | | |
81 | | |
82 | | void |
83 | | nxt_time_thread_start(nxt_msec_t interval) |
84 | 0 | { |
85 | 0 | nxt_thread_link_t *link; |
86 | 0 | nxt_thread_handle_t handle; |
87 | |
|
88 | 0 | link = nxt_zalloc(sizeof(nxt_thread_link_t)); |
89 | |
|
90 | 0 | if (nxt_fast_path(link != NULL)) { |
91 | 0 | link->start = nxt_time_thread; |
92 | 0 | link->work.data = (void *) (uintptr_t) interval; |
93 | |
|
94 | 0 | (void) nxt_thread_create(&handle, link); |
95 | 0 | } |
96 | 0 | } |
97 | | |
98 | | |
99 | | static void |
100 | | nxt_time_thread(void *data) |
101 | 0 | { |
102 | 0 | nxt_nsec_t interval, rest; |
103 | 0 | nxt_thread_t *thr; |
104 | 0 | nxt_monotonic_time_t now; |
105 | |
|
106 | 0 | interval = (uintptr_t) data; |
107 | 0 | interval *= 1000000; |
108 | |
|
109 | 0 | 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 | 0 | thr->time.signal = -1; |
115 | |
|
116 | 0 | nxt_log_debug(thr->log, "time thread"); |
117 | |
|
118 | 0 | nxt_memzero(&now, sizeof(nxt_monotonic_time_t)); |
119 | |
|
120 | 0 | nxt_monotonic_time(&now); |
121 | 0 | nxt_thread_realtime_update(thr, &now); |
122 | |
|
123 | 0 | nxt_shared_time = now; |
124 | 0 | nxt_use_shared_time = 1; |
125 | |
|
126 | 0 | for ( ;; ) { |
127 | 0 | rest = 1000000000 - now.realtime.nsec; |
128 | |
|
129 | 0 | nxt_nanosleep(nxt_min(interval, rest)); |
130 | |
|
131 | 0 | nxt_monotonic_time(&now); |
132 | 0 | nxt_thread_realtime_update(thr, &now); |
133 | |
|
134 | 0 | 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 | 0 | } |
148 | 0 | } |
149 | | |
150 | | |
151 | | static void |
152 | | nxt_thread_time_shared(nxt_monotonic_time_t *now) |
153 | 0 | { |
154 | 0 | nxt_uint_t n; |
155 | 0 | nxt_time_t t; |
156 | 0 | nxt_nsec_t m, u; |
157 | | |
158 | | /* Lock-free thread time update. */ |
159 | |
|
160 | 0 | for ( ;; ) { |
161 | 0 | *now = nxt_shared_time; |
162 | |
|
163 | 0 | t = nxt_shared_time.realtime.sec; |
164 | 0 | n = nxt_shared_time.realtime.nsec; |
165 | 0 | m = nxt_shared_time.monotonic; |
166 | 0 | u = nxt_shared_time.update; |
167 | |
|
168 | 0 | if (now->realtime.sec == t && now->realtime.nsec == n |
169 | 0 | && now->monotonic == m && now->update == u) |
170 | 0 | { |
171 | 0 | return; |
172 | 0 | } |
173 | 0 | } |
174 | 0 | } |
175 | | |
176 | | |
177 | | nxt_time_t |
178 | | nxt_thread_time(nxt_thread_t *thr) |
179 | 0 | { |
180 | 0 | nxt_thread_realtime_update(thr, &thr->time.now); |
181 | |
|
182 | 0 | return thr->time.now.realtime.sec; |
183 | 0 | } |
184 | | |
185 | | |
186 | | nxt_realtime_t * |
187 | | nxt_thread_realtime(nxt_thread_t *thr) |
188 | 0 | { |
189 | 0 | nxt_thread_realtime_update(thr, &thr->time.now); |
190 | |
|
191 | 0 | return &thr->time.now.realtime; |
192 | 0 | } |
193 | | |
194 | | |
195 | | static void |
196 | | nxt_thread_realtime_update(nxt_thread_t *thr, nxt_monotonic_time_t *now) |
197 | 0 | { |
198 | 0 | 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 | 0 | if (now->monotonic >= now->update) { |
217 | 0 | nxt_realtime(&now->realtime); |
218 | |
|
219 | 0 | delta = 1000000000 - now->realtime.nsec; |
220 | 0 | now->update = now->monotonic + delta; |
221 | 0 | } |
222 | 0 | } |
223 | | |
224 | | |
225 | | u_char * |
226 | | nxt_thread_time_string(nxt_thread_t *thr, nxt_time_string_t *ts, u_char *buf) |
227 | 0 | { |
228 | 0 | u_char *p; |
229 | 0 | struct tm *tm; |
230 | 0 | nxt_time_t s; |
231 | 0 | nxt_bool_t update; |
232 | 0 | nxt_atomic_uint_t slot; |
233 | 0 | nxt_time_string_cache_t *tsc; |
234 | |
|
235 | 0 | if (nxt_slow_path(thr == NULL || thr->time.no_cache)) { |
236 | 0 | return nxt_thread_time_string_no_cache(thr, ts, buf); |
237 | 0 | } |
238 | | |
239 | 0 | slot = nxt_thread_time_string_slot(ts); |
240 | |
|
241 | 0 | tsc = nxt_thread_time_string_cache(thr, slot); |
242 | 0 | if (tsc == NULL) { |
243 | 0 | return buf; |
244 | 0 | } |
245 | | |
246 | 0 | if (thr->time.signal < 0) { |
247 | | /* |
248 | | * Lazy real time update: |
249 | | * signal event support or multi-threaded mode. |
250 | | */ |
251 | 0 | nxt_thread_realtime_update(thr, &thr->time.now); |
252 | 0 | } |
253 | |
|
254 | 0 | s = thr->time.now.realtime.sec; |
255 | |
|
256 | 0 | 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 | 0 | if (nxt_slow_path(update)) { |
273 | |
|
274 | 0 | if (ts->timezone == NXT_THREAD_TIME_LOCAL) { |
275 | |
|
276 | 0 | tm = &thr->time.localtime; |
277 | |
|
278 | 0 | if (nxt_slow_path(s != thr->time.last_localtime)) { |
279 | |
|
280 | 0 | if (thr->time.signal < 0) { |
281 | | /* |
282 | | * Lazy local time update: |
283 | | * signal event support or multi-threaded mode. |
284 | | */ |
285 | 0 | nxt_localtime(s, &thr->time.localtime); |
286 | 0 | thr->time.last_localtime = s; |
287 | |
|
288 | 0 | } 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 | 0 | nxt_gmtime(s + nxt_timezone(tm), tm); |
307 | 0 | } |
308 | 0 | } |
309 | |
|
310 | 0 | } else { |
311 | 0 | tm = &thr->time.gmtime; |
312 | |
|
313 | 0 | if (nxt_slow_path(s != thr->time.last_gmtime)) { |
314 | 0 | nxt_gmtime(s, tm); |
315 | 0 | thr->time.last_gmtime = s; |
316 | 0 | } |
317 | |
|
318 | 0 | } |
319 | |
|
320 | 0 | p = tsc->string.start; |
321 | |
|
322 | 0 | if (nxt_slow_path(p == NULL)) { |
323 | |
|
324 | 0 | thr->time.no_cache = 1; |
325 | 0 | p = nxt_zalloc(ts->size); |
326 | 0 | thr->time.no_cache = 0; |
327 | |
|
328 | 0 | if (p == NULL) { |
329 | 0 | return buf; |
330 | 0 | } |
331 | | |
332 | 0 | tsc->string.start = p; |
333 | 0 | } |
334 | | |
335 | 0 | p = ts->handler(p, &thr->time.now.realtime, tm, ts->size, ts->format); |
336 | |
|
337 | 0 | tsc->string.length = p - tsc->string.start; |
338 | |
|
339 | 0 | if (nxt_slow_path(tsc->string.length == 0)) { |
340 | 0 | return buf; |
341 | 0 | } |
342 | | |
343 | 0 | tsc->last = s; |
344 | 0 | } |
345 | | |
346 | 0 | return nxt_cpymem(buf, tsc->string.start, tsc->string.length); |
347 | 0 | } |
348 | | |
349 | | |
350 | | static u_char * |
351 | | nxt_thread_time_string_no_cache(nxt_thread_t *thr, nxt_time_string_t *ts, |
352 | | u_char *buf) |
353 | 0 | { |
354 | 0 | struct tm tm; |
355 | 0 | nxt_realtime_t now; |
356 | |
|
357 | 0 | nxt_realtime(&now); |
358 | |
|
359 | 0 | if (ts->timezone == NXT_THREAD_TIME_LOCAL) { |
360 | |
|
361 | 0 | if (thr == NULL || thr->time.signal <= 0) { |
362 | | /* Non-signal context */ |
363 | 0 | nxt_localtime(now.sec, &tm); |
364 | |
|
365 | 0 | } else { |
366 | 0 | nxt_gmtime(now.sec + nxt_gmtoff, &tm); |
367 | 0 | } |
368 | |
|
369 | 0 | } else { |
370 | 0 | nxt_gmtime(now.sec, &tm); |
371 | 0 | } |
372 | |
|
373 | 0 | return ts->handler(buf, &now, &tm, ts->size, ts->format); |
374 | 0 | } |
375 | | |
376 | | |
377 | | static nxt_atomic_uint_t |
378 | | nxt_thread_time_string_slot(nxt_time_string_t *ts) |
379 | 0 | { |
380 | 0 | static nxt_atomic_t slot; |
381 | |
|
382 | 0 | 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 | 0 | if (nxt_atomic_cmp_set(&ts->slot, -1, -2)) { |
389 | 0 | ts->slot = nxt_atomic_fetch_add(&slot, 1); |
390 | | |
391 | | /* No "break" here since it adds only dispensable "jmp". */ |
392 | 0 | } |
393 | 0 | } |
394 | |
|
395 | 0 | return (nxt_atomic_uint_t) ts->slot; |
396 | 0 | } |
397 | | |
398 | | |
399 | | static nxt_time_string_cache_t * |
400 | | nxt_thread_time_string_cache(nxt_thread_t *thr, nxt_atomic_uint_t slot) |
401 | 0 | { |
402 | 0 | size_t size; |
403 | 0 | nxt_atomic_uint_t i, nstrings; |
404 | 0 | nxt_time_string_cache_t *tsc; |
405 | |
|
406 | 0 | if (nxt_fast_path(slot < thr->time.nstrings)) { |
407 | 0 | tsc = &thr->time.strings[slot]; |
408 | 0 | nxt_prefetch(tsc->string.start); |
409 | 0 | return tsc; |
410 | 0 | } |
411 | | |
412 | 0 | nstrings = slot + 1; |
413 | 0 | size = nstrings * sizeof(nxt_time_string_cache_t); |
414 | |
|
415 | 0 | thr->time.no_cache = 1; |
416 | 0 | tsc = nxt_realloc(thr->time.strings, size); |
417 | 0 | thr->time.no_cache = 0; |
418 | |
|
419 | 0 | if (tsc == NULL) { |
420 | 0 | return NULL; |
421 | 0 | } |
422 | | |
423 | 0 | for (i = thr->time.nstrings; i < nstrings; i++) { |
424 | 0 | tsc[i].last = -1; |
425 | 0 | tsc[i].string.start = NULL; |
426 | 0 | } |
427 | |
|
428 | 0 | thr->time.strings = tsc; |
429 | 0 | thr->time.nstrings = nstrings; |
430 | |
|
431 | 0 | return &tsc[slot]; |
432 | 0 | } |