diff --git a/Doc/library/time.rst b/Doc/library/time.rst --- a/Doc/library/time.rst +++ b/Doc/library/time.rst @@ -225,6 +225,14 @@ The module defines the following functio The earliest date for which it can generate a time is platform-dependent. +.. function:: monotonic() + + Monotonic clock. The reference point of the returned value is undefined so + only the difference of consecutive calls is valid. + + .. versionadded: 3.3 + + .. function:: sleep(secs) Suspend execution for the given number of seconds. The argument may be a diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -331,16 +331,32 @@ class TimeTestCase(unittest.TestCase): pass self.assertEqual(time.strftime('%Z', tt), tzname) + @unittest.skipUnless(hasattr(time, 'monotonic'), + 'need time.monotonic()') + def test_monotonic(self): + t1 = time.monotonic() + t2 = time.monotonic() + self.assertGreaterEqual(t2, t1) + + t1 = time.monotonic() + time.sleep(0.1) + t2 = time.monotonic() + dt = t2 - t1 + self.assertGreater(t2, t1) + self.assertAlmostEqual(dt, 0.1, delta=0.2) + def test_wallclock(self): t1 = time.wallclock() t2 = time.wallclock() + # may fail if the system clock is changed self.assertGreaterEqual(t2, t1) t1 = time.wallclock() time.sleep(0.1) t2 = time.wallclock() + dt = t2 - t1 + # may fail if the system clock is changed self.assertGreater(t2, t1) - dt = t2 - t1 self.assertAlmostEqual(dt, 0.1, delta=0.2) diff --git a/Modules/timemodule.c b/Modules/timemodule.c --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -66,21 +66,28 @@ Fractions of a second may be present if /* Win32 has better clock replacement; we have our own version, due to Mark Hammond and Tim Peters */ static PyObject * -time_clock(PyObject *self, PyObject *unused) +win32_clock(int fallback) { static LARGE_INTEGER ctrStart; static double divisor = 0.0; LARGE_INTEGER now; double diff; - if (divisor == 0.0) { + if (divisor == 0.0) + { LARGE_INTEGER freq; + QueryPerformanceCounter(&ctrStart); - if (!QueryPerformanceFrequency(&freq) || freq.QuadPart == 0) { - /* Unlikely to happen - this works on all intel - machines at least! Revert to clock() */ - return PyFloat_FromDouble(((double)clock()) / - CLOCKS_PER_SEC); + if (!QueryPerformanceFrequency(&freq) || freq.QuadPart == 0) + { + /* Unlikely to happen - this works on all intel machines + at least! */ + if (fallback) + /* Revert to clock() */ + return PyFloat_FromDouble(((double)clock()) / + CLOCKS_PER_SEC); + else + return PyErr_SetFromWindowsErr(0); } divisor = (double)freq.QuadPart; } @@ -89,6 +96,12 @@ time_clock(PyObject *self, PyObject *unu return PyFloat_FromDouble(diff / divisor); } +static PyObject * +time_clock(PyObject *self, PyObject *unused) +{ + return win32_clock(1); +} + #elif defined(HAVE_CLOCK) #ifndef CLOCKS_PER_SEC @@ -741,7 +754,7 @@ static PyObject * time_wallclock(PyObject *self, PyObject *unused) { #if defined(MS_WINDOWS) && !defined(__BORLANDC__) - return time_clock(self, NULL); + return win32_clock(1); #elif defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) static int clk_index = 0; clockid_t clk_ids[] = { @@ -783,6 +796,50 @@ required, i.e. when \"processor time\" i of the returned value is undefined so only the difference of consecutive\n\ calls is valid."); +#if (defined(MS_WINDOWS) && !defined(__BORLANDC__)) \ + || (defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)) +# define HAVE_PYTIME_MONOTONIC +#endif + +#ifdef HAVE_PYTIME_MONOTONIC +static PyObject * +time_monotonic(PyObject *self, PyObject *unused) +{ +#if defined(MS_WINDOWS) && !defined(__BORLANDC__) + return win32_clock(0); +#else + static int clk_index = 0; + clockid_t clk_ids[] = { +#ifdef CLOCK_MONOTONIC_RAW + CLOCK_MONOTONIC_RAW, +#endif + CLOCK_MONOTONIC + }; + int ret; + struct timespec tp; + + while (0 <= clk_index) { + clockid_t clk_id = clk_ids[clk_index]; + ret = clock_gettime(clk_id, &tp); + if (ret == 0) + return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9); + + clk_index++; + if (Py_ARRAY_LENGTH(clk_ids) <= clk_index) + clk_index = -1; + } + PyErr_SetFromErrno(PyExc_IOError); + return NULL; +#endif +} + +PyDoc_STRVAR(monotonic_doc, +"monotonic() -> float\n\ +\n\ +Monotonic clock. The reference point of the returned value is undefined so\n\ +only the difference of consecutive calls is valid."); +#endif + static void PyInit_timezone(PyObject *m) { /* This code moved from PyInit_time wholesale to allow calling it from @@ -911,6 +968,9 @@ static PyMethodDef time_methods[] = { #ifdef HAVE_MKTIME {"mktime", time_mktime, METH_O, mktime_doc}, #endif +#ifdef HAVE_PYTIME_MONOTONIC + {"monotonic", time_monotonic, METH_NOARGS, monotonic_doc}, +#endif #ifdef HAVE_STRFTIME {"strftime", time_strftime, METH_VARARGS, strftime_doc}, #endif