src/cxx_supportlib/Utils/SystemTime.h in passenger-5.0.26 vs src/cxx_supportlib/Utils/SystemTime.h in passenger-5.0.27
- old
+ new
@@ -1,8 +1,8 @@
/*
* Phusion Passenger - https://www.phusionpassenger.com/
- * Copyright (c) 2010 Phusion Holding B.V.
+ * Copyright (c) 2010-2016 Phusion Holding B.V.
*
* "Passenger", "Phusion Passenger" and "Union Station" are registered
* trademarks of Phusion Holding B.V.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -25,47 +25,234 @@
*/
#ifndef _PASSENGER_SYSTEM_TIME_H_
#define _PASSENGER_SYSTEM_TIME_H_
#include <boost/thread.hpp>
+#include <boost/predef.h>
+#include <oxt/macros.hpp>
#include <oxt/system_calls.hpp>
-#include "../Exceptions.h"
+#include <sys/time.h>
+#include <cerrno>
+#include <time.h>
+#include <unistd.h>
+#include <Exceptions.h>
+#if BOOST_OS_MACOS
+ #include <mach/mach.h>
+ #include <mach/mach_time.h>
+ #include <cstring>
+#elif BOOST_OS_AIX
+ #include <sys/systemcfg.h>
+#elif defined(_POSIX_MONOTONIC_CLOCK) || defined(CLOCK_MONOTONIC)
+ #define SYSTEM_TIME_HAVE_MONOTONIC_CLOCK
+ #ifdef CLOCK_MONOTONIC_COARSE
+ #define SYSTEM_TIME_HAVE_CLOCK_MONOTONIC_COARSE
+ #endif
+ #ifdef CLOCK_MONOTONIC_FAST
+ #define SYSTEM_TIME_HAVE_CLOCK_MONOTONIC_FAST
+ #endif
+#endif
+
namespace Passenger {
+using namespace std;
using namespace oxt;
namespace SystemTimeData {
+ extern bool initialized;
extern bool hasForcedValue;
extern time_t forcedValue;
- extern bool hasForcedMsecValue;
- extern unsigned long long forcedMsecValue;
extern bool hasForcedUsecValue;
extern unsigned long long forcedUsecValue;
+
+ #if BOOST_OS_MACOS
+ extern mach_timebase_info_data_t timeInfo;
+ #elif defined(SYSTEM_TIME_HAVE_MONOTONIC_CLOCK)
+ #ifdef SYSTEM_TIME_HAVE_CLOCK_MONOTONIC_COARSE
+ extern unsigned long long monotonicCoarseResolutionNs;
+ #endif
+ #ifdef SYSTEM_TIME_HAVE_CLOCK_MONOTONIC_FAST
+ extern unsigned long long monotonicFastResolutionNs;
+ #endif
+ extern unsigned long long monotonicResolutionNs;
+ #endif
}
+typedef unsigned long long MonotonicTimeUsec;
+
/**
* This class allows one to obtain the system time, similar to time() and
* gettimeofday(). Unlike time(), it is possible to force a certain time
* to be returned, which is useful for testing code that depends on the
* system time.
*
- * get() provides seconds resolution while getMsec() provides milliseconds
+ * get() provides seconds resolution while getUsec() provides microseconds
* resolution. Both clocks can be independently forced to a certain value
- * through force() and forceMsec().
+ * through force() and forceUsec().
+ *
+ * In addition, getMonotonicUsec() returns the monotonic clock in
+ * microseconds. This can also be forced to a certain value using forceUsec().
+ *
+ * Before using any SystemTime functions, you should call
+ * SystemTime::initialize(). If you don't do that, then initialize() will be
+ * called for you, but since initialize() isn't thread-safe you should
+ * call it at the beginning of your program.
*/
class SystemTime {
public:
+ enum Granularity {
+ GRAN_1SEC = 1000000000, // 1 millisecond granularity
+ GRAN_10MSEC = 10000000, // 10 milliseconds granularity
+ GRAN_1MSEC = 1000000, // 1 millisecond granularity
+ GRAN_1USEC = 1000 // 1 microsecond granularty
+ };
+
+private:
+ static void initializeIfNeeded() {
+ if (OXT_UNLIKELY(!SystemTimeData::initialized)) {
+ initialize();
+ }
+ }
+
+ template<Granularity granularityNs>
+ static MonotonicTimeUsec _getMonotonicUsec() {
+ if (OXT_UNLIKELY(SystemTimeData::hasForcedUsecValue)) {
+ return SystemTimeData::hasForcedValue;
+ }
+
+ #if BOOST_OS_MACOS
+ initializeIfNeeded();
+ if (SystemTimeData::timeInfo.numer == 0
+ && SystemTimeData::timeInfo.denom == 0)
+ {
+ return getUsec();
+ } else {
+ return mach_absolute_time()
+ * SystemTimeData::timeInfo.numer
+ / SystemTimeData::timeInfo.denom
+ / 1000;
+ }
+
+ #elif BOOST_OS_SOLARIS
+ return gethrtime() / 1000ull;
+
+ #elif BOOST_OS_AIX
+ timebasestruct_t t;
+ read_wall_time(&t, TIMEBASE_SZ);
+ time_base_to_time(&t, TIMEBASE_SZ);
+ return t.tb_high * 1000000ull + t.tb_low / 1000ull;
+
+ #elif defined(SYSTEM_TIME_HAVE_MONOTONIC_CLOCK)
+ clockid_t clockId = (clockid_t) -1;
+ struct timespec ts;
+ int ret;
+
+ initializeIfNeeded();
+
+ // We choose a different monotonic clock
+ // based on the resolution we need. In general,
+ // coarser resolutions are faster, for example
+ // because (on Linux) they are implemented
+ // through VDSOs instead of system calls.
+ //
+ // Benchmarks and properties as of 10 March 2016:
+ //
+ // FreeBSD 10.2 (200000 iterations):
+ // CLOCK_MONOTONIC 1m 9s 11 nanosec resolution
+ // CLOCK_MONOTONIC_PRECISE 1m 9s 11 nanosec resolution
+ // CLOCK_MONOTONIC_FAST 2s 11 nanosec resolution
+ // gettimeofday 1m 9s
+ //
+ // Linux 3.13.0 (100000000 iterations):
+ // CLOCK_MONOTONIC 1.5s 1 nanosec resolution
+ // CLOCK_MONOTONIC_COARSE 0.45s 4 millisec resolution
+ // gettimeofday 1.5s
+
+ #ifdef SYSTEM_TIME_HAVE_CLOCK_MONOTONIC_COARSE
+ if (clockId == -1
+ && SystemTimeData::monotonicCoarseResolutionNs != 0
+ && SystemTimeData::monotonicCoarseResolutionNs <= granularityNs)
+ {
+ clockId = CLOCK_MONOTONIC_COARSE;
+ }
+ #endif
+ #ifdef SYSTEM_TIME_HAVE_CLOCK_MONOTONIC_FAST
+ if (clockId == -1
+ && SystemTimeData::monotonicFastResolutionNs != 0
+ && SystemTimeData::monotonicFastResolutionNs <= granularityNs)
+ {
+ clockId = CLOCK_MONOTONIC_FAST;
+ }
+ #endif
+ if (clockId == -1
+ && SystemTimeData::monotonicResolutionNs != 0
+ && SystemTimeData::monotonicResolutionNs <= granularityNs)
+ {
+ clockId = CLOCK_MONOTONIC;
+ }
+
+ if (clockId == (clockid_t) -1) {
+ return getUsec();
+ } else {
+ do {
+ ret = clock_gettime(clockId, &ts);
+ } while (ret == -1 && errno == EINTR);
+ if (ret == -1) {
+ int e = errno;
+ throw TimeRetrievalException(
+ "Unable to retrieve the system time",
+ e);
+ }
+ return ts.tv_sec * 1000000ull + ts.tv_nsec / 1000ull;
+ }
+
+ #else
+ return getUsec();
+ #endif
+ }
+
+public:
+ static void initialize() {
+ SystemTimeData::initialized = true;
+ #if BOOST_OS_MACOS
+ if (mach_timebase_info(&SystemTimeData::timeInfo) != KERN_SUCCESS) {
+ memset(&SystemTimeData::timeInfo, 0, sizeof(SystemTimeData::timeInfo));
+ }
+ #elif defined(SYSTEM_TIME_HAVE_MONOTONIC_CLOCK)
+ struct timespec ts;
+
+ #ifdef CLOCK_MONOTONIC_COARSE
+ if (clock_getres(CLOCK_MONOTONIC_COARSE, &ts) == 0) {
+ SystemTimeData::monotonicCoarseResolutionNs =
+ ts.tv_sec * 1000000000ull +
+ ts.tv_nsec;
+ }
+ #endif
+ #ifdef CLOCK_MONOTONIC_FAST
+ if (clock_getres(CLOCK_MONOTONIC_FAST, &ts) == 0) {
+ SystemTimeData::monotonicFastResolutionNs =
+ ts.tv_sec * 1000000000ull +
+ ts.tv_nsec;
+ }
+ #endif
+ if (clock_getres(CLOCK_MONOTONIC, &ts) == 0) {
+ SystemTimeData::monotonicResolutionNs =
+ ts.tv_sec * 1000000000ull +
+ ts.tv_nsec;
+ }
+ #endif
+ }
+
/**
* Returns the time since the Epoch, measured in seconds. Or, if a time
* was forced with force(), then the forced time is returned instead.
*
* @throws TimeRetrievalException Something went wrong while retrieving the time.
* @throws boost::thread_interrupted
*/
static time_t get() {
- if (SystemTimeData::hasForcedValue) {
+ if (OXT_UNLIKELY(SystemTimeData::hasForcedValue)) {
return SystemTimeData::forcedValue;
} else {
time_t ret = syscalls::time(NULL);
if (ret == -1) {
int e = errno;
@@ -76,20 +263,19 @@
return ret;
}
}
/**
- * Returns the time since the Epoch, measured in milliseconds. Or, if a
- * time was forced with forceMsec(), then the forced time is returned instead.
+ * Returns the time since the Epoch, measured in microseconds. Or, if a
+ * time was forced with forceUsec(), then the forced time is returned instead.
*
- * @param real Whether to get the real time, even if a value was forced.
* @throws TimeRetrievalException Something went wrong while retrieving the time.
* @throws boost::thread_interrupted
*/
- static unsigned long long getMsec(bool real = false) {
- if (SystemTimeData::hasForcedMsecValue && !real) {
- return SystemTimeData::forcedMsecValue;
+ static unsigned long long getUsec() {
+ if (OXT_UNLIKELY(SystemTimeData::hasForcedUsecValue)) {
+ return SystemTimeData::forcedUsecValue;
} else {
struct timeval t;
int ret;
do {
@@ -99,39 +285,45 @@
int e = errno;
throw TimeRetrievalException(
"Unable to retrieve the system time",
e);
}
- return (unsigned long long) t.tv_sec * 1000 + t.tv_usec / 1000;
+ return (unsigned long long) t.tv_sec * 1000000 + t.tv_usec;
}
}
/**
- * Returns the time since the Epoch, measured in microseconds. Or, if a
- * time was forced with forceUsec(), then the forced time is returned instead.
+ * Returns the time since an unspecified point in the last, measured in
+ * microseconds, using the monotonic clock.
*
+ * The monotonic clock is not subject to clock drift, even if the user
+ * changes the wall clock time. It is ideal for measuring time between
+ * two intervals.
+ *
+ * The returned time is guaranteed to have a granularity of 1 microsecond
+ * or better. In general, querying with coarser granularities is faster.
+ * If you want a coarser granularity, use `getMonotonicUsecWithGranularity()`
+ * instead.
+ *
+ * If the monotonic clock is not available (e.g. because the operating
+ * system doesn't support it), then this function returns the regular
+ * wall clock time instead (using `getUsec()`). If the monotonic clock
+ * is available, but an error occurred querying it, then a
+ * `TimeRetrievalException` is thrown.
+ *
+ * If the time was forced with forceUsed(), then the forced time is returned
+ * instead.
+ *
* @throws TimeRetrievalException Something went wrong while retrieving the time.
- * @throws boost::thread_interrupted
*/
- static unsigned long long getUsec() {
- if (SystemTimeData::hasForcedUsecValue) {
- return SystemTimeData::forcedUsecValue;
- } else {
- struct timeval t;
- int ret;
+ static MonotonicTimeUsec getMonotonicUsec() {
+ return _getMonotonicUsec<GRAN_1USEC>();
+ }
- do {
- ret = gettimeofday(&t, NULL);
- } while (ret == -1 && errno == EINTR);
- if (ret == -1) {
- int e = errno;
- throw TimeRetrievalException(
- "Unable to retrieve the system time",
- e);
- }
- return (unsigned long long) t.tv_sec * 1000000 + t.tv_usec;
- }
+ template<Granularity granularity>
+ static MonotonicTimeUsec getMonotonicUsecWithGranularity() {
+ return _getMonotonicUsec<granularity>();
}
/**
* Force get() to return the given value.
*/
@@ -139,28 +331,19 @@
SystemTimeData::hasForcedValue = true;
SystemTimeData::forcedValue = value;
}
/**
- * Force getMsec() to return the given value.
- */
- static void forceMsec(unsigned long long value) {
- SystemTimeData::hasForcedMsecValue = true;
- SystemTimeData::forcedMsecValue = value;
- }
-
- /**
* Force getUsec() to return the given value.
*/
static void forceUsec(unsigned long long value) {
SystemTimeData::hasForcedUsecValue = true;
SystemTimeData::forcedUsecValue = value;
}
static void forceAll(unsigned long long usec) {
force(usec / 1000000);
- forceMsec(usec / 1000);
forceUsec(usec);
}
/**
* Release the previously forced seconds value, so that get()
@@ -169,31 +352,22 @@
static void release() {
SystemTimeData::hasForcedValue = false;
}
/**
- * Release the previously forced msec value, so that getMsec()
- * returns the system time once again.
- */
- static void releaseMsec() {
- SystemTimeData::hasForcedMsecValue = false;
- }
-
- /**
* Release the previously forced usec value, so that getUsec()
* returns the system time once again.
*/
static void releaseUsec() {
SystemTimeData::hasForcedUsecValue = false;
}
/**
- * Release all previously forced values, so that get(), getMsec()
- * and getUsec() return the system time once again.
+ * Release all previously forced values, so that get() and
+ * getUsec() return the system time once again.
*/
static void releaseAll() {
SystemTimeData::hasForcedValue = false;
- SystemTimeData::hasForcedMsecValue = false;
SystemTimeData::hasForcedUsecValue = false;
}
};
} // namespace Passenger