summaryrefslogtreecommitdiffhomepage
path: root/src/nxt_time.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/nxt_time.c365
1 files changed, 365 insertions, 0 deletions
diff --git a/src/nxt_time.c b/src/nxt_time.c
new file mode 100644
index 00000000..dfead51c
--- /dev/null
+++ b/src/nxt_time.c
@@ -0,0 +1,365 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_main.h>
+
+
+/* OS-specific real, monotonic, and local times and timezone update. */
+
+
+/* Real time. */
+
+#if (NXT_HAVE_CLOCK_REALTIME_COARSE)
+
+/*
+ * Linux clock_gettime() resides on the vDSO page. Linux 2.6.32
+ * clock_gettime(CLOCK_REALTIME_COARSE) uses only cached values and does
+ * not read TSC or HPET so it has the kernel jiffy precision (1ms by default)
+ * and it is several times faster than clock_gettime(CLOCK_REALTIME).
+ */
+
+void
+nxt_realtime(nxt_realtime_t *now)
+{
+ struct timespec ts;
+
+ (void) clock_gettime(CLOCK_REALTIME_COARSE, &ts);
+
+ now->sec = (nxt_time_t) ts.tv_sec;
+ now->nsec = ts.tv_nsec;
+}
+
+
+#elif (NXT_HAVE_CLOCK_REALTIME_FAST)
+
+/*
+ * FreeBSD 7.0 specific clock_gettime(CLOCK_REALTIME_FAST) may be
+ * 5-30 times faster than clock_gettime(CLOCK_REALTIME) depending
+ * on kern.timecounter.hardware. The clock has a precision of 1/HZ
+ * seconds (HZ is 1000 on modern platforms, thus 1ms precision).
+ * FreeBSD 9.2 clock_gettime() resides on the vDSO page and reads
+ * TSC. clock_gettime(CLOCK_REALTIME_FAST) is the same as
+ * clock_gettime(CLOCK_REALTIME).
+ */
+
+void
+nxt_realtime(nxt_realtime_t *now)
+{
+ struct timespec ts;
+
+ (void) clock_gettime(CLOCK_REALTIME_FAST, &ts);
+
+ now->sec = (nxt_time_t) ts.tv_sec;
+ now->nsec = ts.tv_nsec;
+}
+
+
+#elif (NXT_HAVE_CLOCK_REALTIME && !(NXT_HPUX))
+
+/*
+ * clock_gettime(CLOCK_REALTIME) is supported by Linux, FreeBSD 3.0,
+ * Solaris 8, NetBSD 1.3, and AIX. HP-UX supports it too, however,
+ * it is implemented through a call to gettimeofday(). Linux
+ * clock_gettime(CLOCK_REALTIME) resides on the vDSO page and reads
+ * TSC or HPET. FreeBSD 9.2 clock_gettime(CLOCK_REALTIME) resides
+ * on the vDSO page and reads TSC.
+ */
+
+void
+nxt_realtime(nxt_realtime_t *now)
+{
+ struct timespec ts;
+
+ (void) clock_gettime(CLOCK_REALTIME, &ts);
+
+ now->sec = (nxt_time_t) ts.tv_sec;
+ now->nsec = ts.tv_nsec;
+}
+
+
+#else
+
+/* MacOSX, HP-UX. */
+
+void
+nxt_realtime(nxt_realtime_t *now)
+{
+ struct timeval tv;
+
+ (void) gettimeofday(&tv, NULL);
+
+ now->sec = (nxt_time_t) tv.tv_sec;
+ now->nsec = tv.tv_usec * 1000;
+}
+
+#endif
+
+
+/* Monotonic time. */
+
+#if (NXT_HAVE_CLOCK_MONOTONIC_COARSE)
+
+/*
+ * Linux clock_gettime() resides on the vDSO page. Linux 2.6.32
+ * clock_gettime(CLOCK_MONOTONIC_COARSE) uses only cached values and does
+ * not read TSC or HPET so it has the kernel jiffy precision (1ms by default)
+ * and it is several times faster than clock_gettime(CLOCK_MONOTONIC).
+ */
+
+void
+nxt_monotonic_time(nxt_monotonic_time_t *now)
+{
+ struct timespec ts;
+
+ (void) clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
+
+ now->monotonic = (nxt_nsec_t) ts.tv_sec * 1000000000 + ts.tv_nsec;
+}
+
+
+#elif (NXT_HAVE_CLOCK_MONOTONIC_FAST)
+
+/*
+ * FreeBSD 7.0 specific clock_gettime(CLOCK_MONOTONIC_FAST) may be
+ * 5-30 times faster than clock_gettime(CLOCK_MONOTONIC) depending
+ * on kern.timecounter.hardware. The clock has a precision of 1/HZ
+ * seconds (HZ is 1000 on modern platforms, thus 1ms precision).
+ * FreeBSD 9.2 clock_gettime() resides on the vDSO page and reads
+ * TSC. clock_gettime(CLOCK_MONOTONIC_FAST) is the same as
+ * clock_gettime(CLOCK_MONOTONIC).
+ */
+
+void
+nxt_monotonic_time(nxt_monotonic_time_t *now)
+{
+ struct timespec ts;
+
+ (void) clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
+
+ now->monotonic = (nxt_nsec_t) ts.tv_sec * 1000000000 + ts.tv_nsec;
+}
+
+
+#elif (NXT_HAVE_HG_GETHRTIME)
+
+/*
+ * HP-UX 11.31 provides fast hg_gethrtime() which uses a chunk of memory
+ * shared between userspace application and the kernel, and was introduced
+ * by Project Mercury ("HG").
+ */
+
+void
+nxt_monotonic_time(nxt_monotonic_time_t *now)
+{
+ now->monotonic = (nxt_nsec_t) hg_gethrtime();
+}
+
+
+#elif (NXT_SOLARIS || NXT_HPUX)
+
+/*
+ * Solaris gethrtime(), clock_gettime(CLOCK_REALTIME), and gettimeofday()
+ * use a fast systrap whereas clock_gettime(CLOCK_MONOTONIC) and other
+ * clock_gettime()s use normal systrap. However, the difference is
+ * negligible on x86_64.
+ *
+ * HP-UX lacks clock_gettime(CLOCK_MONOTONIC) but has lightweight
+ * system call gethrtime().
+ */
+
+void
+nxt_monotonic_time(nxt_monotonic_time_t *now)
+{
+ now->monotonic = (nxt_nsec_t) gethrtime();
+}
+
+
+#elif (NXT_HAVE_CLOCK_MONOTONIC)
+
+/*
+ * clock_gettime(CLOCK_MONOTONIC) is supported by Linux, FreeBSD 5.0,
+ * Solaris 8, NetBSD 1.6, and AIX. Linux clock_gettime(CLOCK_MONOTONIC)
+ * resides on the vDSO page and reads TSC or HPET. FreeBSD 9.2
+ * clock_gettime(CLOCK_MONOTONIC) resides on the vDSO page and reads TSC.
+ */
+
+void
+nxt_monotonic_time(nxt_monotonic_time_t *now)
+{
+ struct timespec ts;
+
+ (void) clock_gettime(CLOCK_MONOTONIC, &ts);
+
+ now->monotonic = (nxt_nsec_t) ts.tv_sec * 1000000000 + ts.tv_nsec;
+}
+
+
+#elif (NXT_MACOSX)
+
+/*
+ * MacOSX does not support clock_gettime(), but mach_absolute_time() returns
+ * monotonic ticks. To get nanoseconds the ticks should be multiplied then
+ * divided by numerator/denominator returned by mach_timebase_info(), however
+ * on modern MacOSX they are 1/1. On PowerPC MacOSX these values were
+ * 1000000000/33333335 or 1000000000/25000000, on iOS 4+ they were 125/3,
+ * and on iOS 3 they were 1000000000/24000000.
+ */
+
+void
+nxt_monotonic_time(nxt_monotonic_time_t *now)
+{
+ now->monotonic = mach_absolute_time();
+}
+
+
+#else
+
+void
+nxt_monotonic_time(nxt_monotonic_time_t *now)
+{
+ nxt_nsec_t current;
+ nxt_nsec_int_t delta;
+ struct timeval tv;
+
+ (void) gettimeofday(&tv, NULL);
+
+ now->realtime.sec = (nxt_time_t) tv.tv_sec;
+ now->realtime.nsec = tv.tv_usec * 1000;
+
+ /*
+ * Monotonic time emulation using gettimeofday()
+ * for platforms which lack monotonic time.
+ */
+
+ current = (nxt_nsec_t) tv.tv_sec * 1000000000 + tv.tv_usec * 1000;
+ delta = current - now->previous;
+ now->previous = current;
+
+ if (delta > 0) {
+ now->monotonic += delta;
+
+ } else {
+ /* The time went backward. */
+ now->monotonic++;
+ }
+
+ /*
+ * Eliminate subsequent gettimeofday() call
+ * in nxt_thread_realtime_update().
+ */
+ now->update = now->monotonic + 1;
+}
+
+#endif
+
+
+/* Local time. */
+
+#if (NXT_HAVE_LOCALTIME_R)
+
+void
+nxt_localtime(nxt_time_t s, struct tm *tm)
+{
+ time_t _s;
+
+ _s = (time_t) s;
+ (void) localtime_r(&_s, tm);
+}
+
+
+#else
+
+void
+nxt_localtime(nxt_time_t s, struct tm *tm)
+{
+ time_t _s;
+ struct tm *_tm;
+
+ _s = (time_t) s;
+ _tm = localtime(&_s);
+ *tm = *_tm;
+}
+
+#endif
+
+
+/* Timezone update. */
+
+#if (NXT_LINUX)
+
+/*
+ * Linux glibc does not test /etc/localtime change
+ * in localtime_r(), but tests in localtime().
+ */
+
+void
+nxt_timezone_update(void)
+{
+ time_t s;
+
+ s = time(NULL);
+ (void) localtime(&s);
+}
+
+
+#elif (NXT_FREEBSD)
+
+/*
+ * FreeBSD libc does not test /etc/localtime change, but it can be
+ * worked around by calling tzset() with TZ and then without TZ
+ * to update timezone. This trick should work since FreeBSD 2.1.0.
+ */
+
+void
+nxt_timezone_update(void)
+{
+ if (getenv("TZ") != NULL) {
+ return;
+ }
+
+ /* The libc uses /etc/localtime if TZ is not set. */
+
+ (void) putenv((char *) "TZ=UTC");
+ tzset();
+
+ (void) unsetenv("TZ");
+ tzset();
+}
+
+
+#elif (NXT_SOLARIS)
+
+/*
+ * Solaris 10, patch 142909-17 introduced tzreload(8):
+ *
+ * The tzreload command notifies active (running) processes to reread
+ * timezone information. The timezone information is cached in each
+ * process, absent a tzreload command, is never reread until a process
+ * is restarted. In response to a tzreload command, active processes
+ * reread the current timezone information at the next call to ctime(3C)
+ * and mktime(3C). By default, the tzreload notification is sent to
+ * the processes within the current zone.
+ */
+
+void
+nxt_timezone_update(void)
+{
+ time_t s;
+
+ s = time(NULL);
+ (void) ctime(&s);
+}
+
+
+#else
+
+void
+nxt_timezone_update(void)
+{
+ return;
+}
+
+#endif