summaryrefslogtreecommitdiff
path: root/Python/pytime.c
diff options
context:
space:
mode:
Diffstat (limited to 'Python/pytime.c')
-rw-r--r--Python/pytime.c286
1 files changed, 220 insertions, 66 deletions
diff --git a/Python/pytime.c b/Python/pytime.c
index de6a41fe00..578569d4dd 100644
--- a/Python/pytime.c
+++ b/Python/pytime.c
@@ -3,29 +3,26 @@
#include <windows.h>
#endif
-#if defined(__APPLE__) && defined(HAVE_GETTIMEOFDAY) && defined(HAVE_FTIME)
- /*
- * _PyTime_gettimeofday falls back to ftime when getttimeofday fails because the latter
- * might fail on some platforms. This fallback is unwanted on MacOSX because
- * that makes it impossible to use a binary build on OSX 10.4 on earlier
- * releases of the OS. Therefore claim we don't support ftime.
- */
-# undef HAVE_FTIME
+#if defined(__APPLE__)
+#include <mach/mach_time.h> /* mach_absolute_time(), mach_timebase_info() */
#endif
-#if defined(HAVE_FTIME) && !defined(MS_WINDOWS)
-#include <sys/timeb.h>
-extern int ftime(struct timeb *);
-#endif
+#define SEC_TO_MS 1000
+#define MS_TO_US 1000
+#define US_TO_NS 1000
-static void
-pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info)
+#define SEC_TO_US (SEC_TO_MS * MS_TO_US)
+
+static int
+pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise)
{
#ifdef MS_WINDOWS
FILETIME system_time;
ULARGE_INTEGER large;
ULONGLONG microseconds;
+ assert(info == NULL || raise);
+
GetSystemTimeAsFileTime(&system_time);
large.u.LowPart = system_time.dwLowDateTime;
large.u.HighPart = system_time.dwHighDateTime;
@@ -33,84 +30,215 @@ pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info)
the 1st january 1601 and the 1st january 1970 (369 years + 89 leap
days). */
microseconds = large.QuadPart / 10 - 11644473600000000;
- tp->tv_sec = microseconds / 1000000;
- tp->tv_usec = microseconds % 1000000;
+ tp->tv_sec = microseconds / SEC_TO_US;
+ tp->tv_usec = microseconds % SEC_TO_US;
if (info) {
DWORD timeAdjustment, timeIncrement;
- BOOL isTimeAdjustmentDisabled;
+ BOOL isTimeAdjustmentDisabled, ok;
info->implementation = "GetSystemTimeAsFileTime()";
info->monotonic = 0;
- (void) GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement,
- &isTimeAdjustmentDisabled);
+ ok = GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement,
+ &isTimeAdjustmentDisabled);
+ if (!ok) {
+ PyErr_SetFromWindowsErr(0);
+ return -1;
+ }
info->resolution = timeIncrement * 1e-7;
info->adjustable = 1;
}
-#else
- /* There are three ways to get the time:
- (1) gettimeofday() -- resolution in microseconds
- (2) ftime() -- resolution in milliseconds
- (3) time() -- resolution in seconds
- In all cases the return value in a timeval struct.
- Since on some systems (e.g. SCO ODT 3.0) gettimeofday() may
- fail, so we fall back on ftime() or time().
- Note: clock resolution does not imply clock accuracy! */
-
-#ifdef HAVE_GETTIMEOFDAY
+
+#else /* MS_WINDOWS */
int err;
+#ifdef HAVE_CLOCK_GETTIME
+ struct timespec ts;
+#endif
+
+ assert(info == NULL || raise);
+
+#ifdef HAVE_CLOCK_GETTIME
+ err = clock_gettime(CLOCK_REALTIME, &ts);
+ if (err) {
+ if (raise)
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+ tp->tv_sec = ts.tv_sec;
+ tp->tv_usec = ts.tv_nsec / US_TO_NS;
+
+ if (info) {
+ struct timespec res;
+ info->implementation = "clock_gettime(CLOCK_REALTIME)";
+ info->monotonic = 0;
+ info->adjustable = 1;
+ if (clock_getres(CLOCK_REALTIME, &res) == 0)
+ info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
+ else
+ info->resolution = 1e-9;
+ }
+#else /* HAVE_CLOCK_GETTIME */
+
+ /* test gettimeofday() */
#ifdef GETTIMEOFDAY_NO_TZ
err = gettimeofday(tp);
#else
err = gettimeofday(tp, (struct timezone *)NULL);
#endif
- if (err == 0) {
- if (info) {
- info->implementation = "gettimeofday()";
- info->resolution = 1e-6;
- info->monotonic = 0;
- info->adjustable = 1;
- }
- return;
- }
-#endif /* HAVE_GETTIMEOFDAY */
-
-#if defined(HAVE_FTIME)
- {
- struct timeb t;
- ftime(&t);
- tp->tv_sec = t.time;
- tp->tv_usec = t.millitm * 1000;
- if (info) {
- info->implementation = "ftime()";
- info->resolution = 1e-3;
- info->monotonic = 0;
- info->adjustable = 1;
- }
+ if (err) {
+ if (raise)
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
}
-#else /* !HAVE_FTIME */
- tp->tv_sec = time(NULL);
- tp->tv_usec = 0;
+
if (info) {
- info->implementation = "time()";
- info->resolution = 1.0;
+ info->implementation = "gettimeofday()";
+ info->resolution = 1e-6;
info->monotonic = 0;
info->adjustable = 1;
}
-#endif /* !HAVE_FTIME */
-
-#endif /* MS_WINDOWS */
+#endif /* !HAVE_CLOCK_GETTIME */
+#endif /* !MS_WINDOWS */
+ assert(0 <= tp->tv_usec && tp->tv_usec < SEC_TO_US);
+ return 0;
}
void
_PyTime_gettimeofday(_PyTime_timeval *tp)
{
- pygettimeofday(tp, NULL);
+ if (pygettimeofday(tp, NULL, 0) < 0) {
+ /* cannot happen, _PyTime_Init() checks that pygettimeofday() works */
+ assert(0);
+ tp->tv_sec = 0;
+ tp->tv_usec = 0;
+ }
}
-void
+int
_PyTime_gettimeofday_info(_PyTime_timeval *tp, _Py_clock_info_t *info)
{
- pygettimeofday(tp, info);
+ return pygettimeofday(tp, info, 1);
+}
+
+static int
+pymonotonic(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise)
+{
+#ifdef Py_DEBUG
+ static _PyTime_timeval last = {0, -1};
+#endif
+#if defined(MS_WINDOWS)
+ ULONGLONG result;
+
+ assert(info == NULL || raise);
+
+ result = GetTickCount64();
+
+ tp->tv_sec = result / SEC_TO_MS;
+ tp->tv_usec = (result % SEC_TO_MS) * MS_TO_US;
+
+ if (info) {
+ DWORD timeAdjustment, timeIncrement;
+ BOOL isTimeAdjustmentDisabled, ok;
+ info->implementation = "GetTickCount64()";
+ info->monotonic = 1;
+ ok = GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement,
+ &isTimeAdjustmentDisabled);
+ if (!ok) {
+ PyErr_SetFromWindowsErr(0);
+ return -1;
+ }
+ info->resolution = timeIncrement * 1e-7;
+ info->adjustable = 0;
+ }
+
+#elif defined(__APPLE__)
+ static mach_timebase_info_data_t timebase;
+ uint64_t time;
+
+ if (timebase.denom == 0) {
+ /* According to the Technical Q&A QA1398, mach_timebase_info() cannot
+ fail: https://developer.apple.com/library/mac/#qa/qa1398/ */
+ (void)mach_timebase_info(&timebase);
+ }
+
+ time = mach_absolute_time();
+
+ /* nanoseconds => microseconds */
+ time /= US_TO_NS;
+ /* apply timebase factor */
+ time *= timebase.numer;
+ time /= timebase.denom;
+ tp->tv_sec = time / SEC_TO_US;
+ tp->tv_usec = time % SEC_TO_US;
+
+ if (info) {
+ info->implementation = "mach_absolute_time()";
+ info->resolution = (double)timebase.numer / timebase.denom * 1e-9;
+ info->monotonic = 1;
+ info->adjustable = 0;
+ }
+
+#else
+ struct timespec ts;
+#ifdef CLOCK_HIGHRES
+ const clockid_t clk_id = CLOCK_HIGHRES;
+ const char *implementation = "clock_gettime(CLOCK_HIGHRES)";
+#else
+ const clockid_t clk_id = CLOCK_MONOTONIC;
+ const char *implementation = "clock_gettime(CLOCK_MONOTONIC)";
+#endif
+
+ assert(info == NULL || raise);
+
+ if (clock_gettime(clk_id, &ts) != 0) {
+ if (raise) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+ tp->tv_sec = 0;
+ tp->tv_usec = 0;
+ return -1;
+ }
+
+ if (info) {
+ struct timespec res;
+ info->monotonic = 1;
+ info->implementation = implementation;
+ info->adjustable = 0;
+ if (clock_getres(clk_id, &res) != 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+ info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
+ }
+ tp->tv_sec = ts.tv_sec;
+ tp->tv_usec = ts.tv_nsec / US_TO_NS;
+#endif
+ assert(0 <= tp->tv_usec && tp->tv_usec < SEC_TO_US);
+#ifdef Py_DEBUG
+ /* monotonic clock cannot go backward */
+ assert(last.tv_usec == -1
+ || tp->tv_sec > last.tv_sec
+ || (tp->tv_sec == last.tv_sec && tp->tv_usec >= last.tv_usec));
+ last = *tp;
+#endif
+ return 0;
+}
+
+void
+_PyTime_monotonic(_PyTime_timeval *tp)
+{
+ if (pymonotonic(tp, NULL, 0) < 0) {
+ /* cannot happen, _PyTime_Init() checks that pymonotonic() works */
+ assert(0);
+ tp->tv_sec = 0;
+ tp->tv_usec = 0;
+ }
+}
+
+int
+_PyTime_monotonic_info(_PyTime_timeval *tp, _Py_clock_info_t *info)
+{
+ return pymonotonic(tp, info, 1);
}
static void
@@ -246,7 +374,33 @@ _PyTime_ObjectToTimeval(PyObject *obj, time_t *sec, long *usec,
}
void
-_PyTime_Init()
+_PyTime_AddDouble(_PyTime_timeval *tv, double interval, _PyTime_round_t round)
{
- /* Do nothing. Needed to force linking. */
+ _PyTime_timeval tv2;
+ double frac;
+
+ frac = fmod(interval, 1.0);
+ interval = floor(interval);
+ tv2.tv_sec = (long)interval;
+ tv2.tv_usec = (long)(frac*1e6);
+
+ tv->tv_sec += tv2.tv_sec;
+ tv->tv_usec += tv2.tv_usec;
+ tv->tv_sec += (time_t)(tv->tv_usec / SEC_TO_US);
+ tv->tv_usec %= SEC_TO_US;
+}
+
+int
+_PyTime_Init(void)
+{
+ _PyTime_timeval tv;
+
+ /* ensure that the system clock works */
+ if (_PyTime_gettimeofday_info(&tv, NULL) < 0)
+ return -1;
+
+ /* ensure that the operating system provides a monotonic clock */
+ if (_PyTime_monotonic_info(&tv, NULL) < 0)
+ return -1;
+ return 0;
}