Issue #13846: Add time.monotonic(), monotonic clock.

This commit is contained in:
Victor Stinner 2012-02-07 23:29:46 +01:00
parent d1cd99b533
commit 8b30201f7d
4 changed files with 94 additions and 16 deletions

View file

@ -226,6 +226,14 @@ The module defines the following functions and data items:
The earliest date for which it can generate a time is platform-dependent. 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) .. function:: sleep(secs)
Suspend execution for the given number of seconds. The argument may be a Suspend execution for the given number of seconds. The argument may be a

View file

@ -331,16 +331,32 @@ class TimeTestCase(unittest.TestCase):
pass pass
self.assertEqual(time.strftime('%Z', tt), tzname) 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): def test_wallclock(self):
t1 = time.wallclock() t1 = time.wallclock()
t2 = time.wallclock() t2 = time.wallclock()
# may fail if the system clock was changed
self.assertGreaterEqual(t2, t1) self.assertGreaterEqual(t2, t1)
t1 = time.wallclock() t1 = time.wallclock()
time.sleep(0.1) time.sleep(0.1)
t2 = time.wallclock() t2 = time.wallclock()
self.assertGreater(t2, t1)
dt = t2 - t1 dt = t2 - t1
# may fail if the system clock was changed
self.assertGreater(t2, t1)
self.assertAlmostEqual(dt, 0.1, delta=0.2) self.assertAlmostEqual(dt, 0.1, delta=0.2)
def test_localtime_failure(self): def test_localtime_failure(self):

View file

@ -466,6 +466,8 @@ Core and Builtins
Library Library
------- -------
- Issue #13846: Add time.monotonic(), monotonic clock.
- Issue #10811: Fix recursive usage of cursors. Instead of crashing, - Issue #10811: Fix recursive usage of cursors. Instead of crashing,
raise a ProgrammingError now. raise a ProgrammingError now.

View file

@ -91,39 +91,44 @@ pyclock(void)
/* Win32 has better clock replacement; we have our own version, due to Mark /* Win32 has better clock replacement; we have our own version, due to Mark
Hammond and Tim Peters */ Hammond and Tim Peters */
static PyObject * static PyObject *
time_clock(PyObject *self, PyObject *unused) win32_clock(int fallback)
{ {
static LARGE_INTEGER ctrStart; static LONGLONG cpu_frequency = 0;
static double divisor = 0.0; static LONGLONG ctrStart;
LARGE_INTEGER now; LARGE_INTEGER now;
double diff; double diff;
if (divisor == 0.0) { if (cpu_frequency == 0) {
LARGE_INTEGER freq; LARGE_INTEGER freq;
QueryPerformanceCounter(&ctrStart); QueryPerformanceCounter(&now);
ctrStart = now.QuadPart;
if (!QueryPerformanceFrequency(&freq) || freq.QuadPart == 0) { if (!QueryPerformanceFrequency(&freq) || freq.QuadPart == 0) {
/* Unlikely to happen - this works on all intel /* Unlikely to happen - this works on all intel
machines at least! Revert to clock() */ machines at least! Revert to clock() */
return pyclock(); if (fallback)
return pyclock();
else
return PyErr_SetFromWindowsErr(0);
} }
divisor = (double)freq.QuadPart; cpu_frequency = freq.QuadPart;
} }
QueryPerformanceCounter(&now); QueryPerformanceCounter(&now);
diff = (double)(now.QuadPart - ctrStart.QuadPart); diff = (double)(now.QuadPart - ctrStart);
return PyFloat_FromDouble(diff / divisor); return PyFloat_FromDouble(diff / (double)cpu_frequency);
} }
#endif
#elif defined(HAVE_CLOCK) #if (defined(MS_WINDOWS) && !defined(__BORLANDC__)) || defined(HAVE_CLOCK)
static PyObject * static PyObject *
time_clock(PyObject *self, PyObject *unused) time_clock(PyObject *self, PyObject *unused)
{ {
#if defined(MS_WINDOWS) && !defined(__BORLANDC__)
return win32_clock(1);
#else
return pyclock(); return pyclock();
#endif
} }
#endif /* HAVE_CLOCK */
#ifdef HAVE_CLOCK
PyDoc_STRVAR(clock_doc, PyDoc_STRVAR(clock_doc,
"clock() -> floating point number\n\ "clock() -> floating point number\n\
\n\ \n\
@ -767,7 +772,7 @@ static PyObject *
time_wallclock(PyObject *self, PyObject *unused) time_wallclock(PyObject *self, PyObject *unused)
{ {
#if defined(MS_WINDOWS) && !defined(__BORLANDC__) #if defined(MS_WINDOWS) && !defined(__BORLANDC__)
return time_clock(self, NULL); return win32_clock(1);
#elif defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) #elif defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
static int clk_index = 0; static int clk_index = 0;
clockid_t clk_ids[] = { clockid_t clk_ids[] = {
@ -809,6 +814,50 @@ required, i.e. when \"processor time\" is inappropriate. The reference point\n\
of the returned value is undefined so only the difference of consecutive\n\ of the returned value is undefined so only the difference of consecutive\n\
calls is valid."); 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_OSError);
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 static void
PyInit_timezone(PyObject *m) { PyInit_timezone(PyObject *m) {
/* This code moved from PyInit_time wholesale to allow calling it from /* This code moved from PyInit_time wholesale to allow calling it from
@ -937,6 +986,9 @@ static PyMethodDef time_methods[] = {
#ifdef HAVE_MKTIME #ifdef HAVE_MKTIME
{"mktime", time_mktime, METH_O, mktime_doc}, {"mktime", time_mktime, METH_O, mktime_doc},
#endif #endif
#ifdef HAVE_PYTIME_MONOTONIC
{"monotonic", time_monotonic, METH_NOARGS, monotonic_doc},
#endif
#ifdef HAVE_STRFTIME #ifdef HAVE_STRFTIME
{"strftime", time_strftime, METH_VARARGS, strftime_doc}, {"strftime", time_strftime, METH_VARARGS, strftime_doc},
#endif #endif