Commit 8b30201f authored by Victor Stinner's avatar Victor Stinner

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

parent d1cd99b5
...@@ -226,6 +226,14 @@ The module defines the following functions and data items: ...@@ -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
......
...@@ -331,16 +331,32 @@ class TimeTestCase(unittest.TestCase): ...@@ -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):
......
...@@ -466,6 +466,8 @@ Core and Builtins ...@@ -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.
......
...@@ -91,39 +91,44 @@ pyclock(void) ...@@ -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 * ...@@ -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\ ...@@ -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[] = { ...@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment