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