Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8b30201

Browse files
author
Victor Stinner
committed
Issue #13846: Add time.monotonic(), monotonic clock.
1 parent d1cd99b commit 8b30201

4 files changed

Lines changed: 94 additions & 16 deletions

File tree

Doc/library/time.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,14 @@ The module defines the following functions and data items:
226226
The earliest date for which it can generate a time is platform-dependent.
227227

228228

229+
.. function:: monotonic()
230+
231+
Monotonic clock. The reference point of the returned value is undefined so
232+
only the difference of consecutive calls is valid.
233+
234+
.. versionadded: 3.3
235+
236+
229237
.. function:: sleep(secs)
230238

231239
Suspend execution for the given number of seconds. The argument may be a

Lib/test/test_time.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,16 +331,32 @@ def test_mktime_error(self):
331331
pass
332332
self.assertEqual(time.strftime('%Z', tt), tzname)
333333

334+
@unittest.skipUnless(hasattr(time, 'monotonic'),
335+
'need time.monotonic()')
336+
def test_monotonic(self):
337+
t1 = time.monotonic()
338+
t2 = time.monotonic()
339+
self.assertGreaterEqual(t2, t1)
340+
341+
t1 = time.monotonic()
342+
time.sleep(0.1)
343+
t2 = time.monotonic()
344+
dt = t2 - t1
345+
self.assertGreater(t2, t1)
346+
self.assertAlmostEqual(dt, 0.1, delta=0.2)
347+
334348
def test_wallclock(self):
335349
t1 = time.wallclock()
336350
t2 = time.wallclock()
351+
# may fail if the system clock was changed
337352
self.assertGreaterEqual(t2, t1)
338353

339354
t1 = time.wallclock()
340355
time.sleep(0.1)
341356
t2 = time.wallclock()
342-
self.assertGreater(t2, t1)
343357
dt = t2 - t1
358+
# may fail if the system clock was changed
359+
self.assertGreater(t2, t1)
344360
self.assertAlmostEqual(dt, 0.1, delta=0.2)
345361

346362
def test_localtime_failure(self):

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,8 @@ Core and Builtins
466466
Library
467467
-------
468468

469+
- Issue #13846: Add time.monotonic(), monotonic clock.
470+
469471
- Issue #10811: Fix recursive usage of cursors. Instead of crashing,
470472
raise a ProgrammingError now.
471473

Modules/timemodule.c

Lines changed: 67 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -91,39 +91,44 @@ pyclock(void)
9191
/* Win32 has better clock replacement; we have our own version, due to Mark
9292
Hammond and Tim Peters */
9393
static PyObject *
94-
time_clock(PyObject *self, PyObject *unused)
94+
win32_clock(int fallback)
9595
{
96-
static LARGE_INTEGER ctrStart;
97-
static double divisor = 0.0;
96+
static LONGLONG cpu_frequency = 0;
97+
static LONGLONG ctrStart;
9898
LARGE_INTEGER now;
9999
double diff;
100100

101-
if (divisor == 0.0) {
101+
if (cpu_frequency == 0) {
102102
LARGE_INTEGER freq;
103-
QueryPerformanceCounter(&ctrStart);
103+
QueryPerformanceCounter(&now);
104+
ctrStart = now.QuadPart;
104105
if (!QueryPerformanceFrequency(&freq) || freq.QuadPart == 0) {
105106
/* Unlikely to happen - this works on all intel
106107
machines at least! Revert to clock() */
107-
return pyclock();
108+
if (fallback)
109+
return pyclock();
110+
else
111+
return PyErr_SetFromWindowsErr(0);
108112
}
109-
divisor = (double)freq.QuadPart;
113+
cpu_frequency = freq.QuadPart;
110114
}
111115
QueryPerformanceCounter(&now);
112-
diff = (double)(now.QuadPart - ctrStart.QuadPart);
113-
return PyFloat_FromDouble(diff / divisor);
116+
diff = (double)(now.QuadPart - ctrStart);
117+
return PyFloat_FromDouble(diff / (double)cpu_frequency);
114118
}
119+
#endif
115120

116-
#elif defined(HAVE_CLOCK)
117-
121+
#if (defined(MS_WINDOWS) && !defined(__BORLANDC__)) || defined(HAVE_CLOCK)
118122
static PyObject *
119123
time_clock(PyObject *self, PyObject *unused)
120124
{
125+
#if defined(MS_WINDOWS) && !defined(__BORLANDC__)
126+
return win32_clock(1);
127+
#else
121128
return pyclock();
129+
#endif
122130
}
123-
#endif /* HAVE_CLOCK */
124-
125131

126-
#ifdef HAVE_CLOCK
127132
PyDoc_STRVAR(clock_doc,
128133
"clock() -> floating point number\n\
129134
\n\
@@ -767,7 +772,7 @@ static PyObject *
767772
time_wallclock(PyObject *self, PyObject *unused)
768773
{
769774
#if defined(MS_WINDOWS) && !defined(__BORLANDC__)
770-
return time_clock(self, NULL);
775+
return win32_clock(1);
771776
#elif defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
772777
static int clk_index = 0;
773778
clockid_t clk_ids[] = {
@@ -809,6 +814,50 @@ required, i.e. when \"processor time\" is inappropriate. The reference point\n\
809814
of the returned value is undefined so only the difference of consecutive\n\
810815
calls is valid.");
811816

817+
#if (defined(MS_WINDOWS) && !defined(__BORLANDC__)) \
818+
|| (defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC))
819+
# define HAVE_PYTIME_MONOTONIC
820+
#endif
821+
822+
#ifdef HAVE_PYTIME_MONOTONIC
823+
static PyObject *
824+
time_monotonic(PyObject *self, PyObject *unused)
825+
{
826+
#if defined(MS_WINDOWS) && !defined(__BORLANDC__)
827+
return win32_clock(0);
828+
#else
829+
static int clk_index = 0;
830+
clockid_t clk_ids[] = {
831+
#ifdef CLOCK_MONOTONIC_RAW
832+
CLOCK_MONOTONIC_RAW,
833+
#endif
834+
CLOCK_MONOTONIC
835+
};
836+
int ret;
837+
struct timespec tp;
838+
839+
while (0 <= clk_index) {
840+
clockid_t clk_id = clk_ids[clk_index];
841+
ret = clock_gettime(clk_id, &tp);
842+
if (ret == 0)
843+
return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9);
844+
845+
clk_index++;
846+
if (Py_ARRAY_LENGTH(clk_ids) <= clk_index)
847+
clk_index = -1;
848+
}
849+
PyErr_SetFromErrno(PyExc_OSError);
850+
return NULL;
851+
#endif
852+
}
853+
854+
PyDoc_STRVAR(monotonic_doc,
855+
"monotonic() -> float\n\
856+
\n\
857+
Monotonic clock. The reference point of the returned value is undefined so\n\
858+
only the difference of consecutive calls is valid.");
859+
#endif
860+
812861
static void
813862
PyInit_timezone(PyObject *m) {
814863
/* This code moved from PyInit_time wholesale to allow calling it from
@@ -937,6 +986,9 @@ static PyMethodDef time_methods[] = {
937986
#ifdef HAVE_MKTIME
938987
{"mktime", time_mktime, METH_O, mktime_doc},
939988
#endif
989+
#ifdef HAVE_PYTIME_MONOTONIC
990+
{"monotonic", time_monotonic, METH_NOARGS, monotonic_doc},
991+
#endif
940992
#ifdef HAVE_STRFTIME
941993
{"strftime", time_strftime, METH_VARARGS, strftime_doc},
942994
#endif

0 commit comments

Comments
 (0)