summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/time.rst4
-rw-r--r--Doc/whatsnew/3.5.rst5
-rw-r--r--Include/pytime.h18
-rw-r--r--Lib/queue.py5
-rw-r--r--Lib/sched.py5
-rw-r--r--Lib/socketserver.py5
-rw-r--r--Lib/subprocess.py5
-rw-r--r--Lib/telnetlib.py5
-rw-r--r--Lib/test/test_selectors.py5
-rw-r--r--Lib/threading.py5
-rwxr-xr-xLib/trace.py5
-rw-r--r--Misc/NEWS5
-rw-r--r--Modules/_threadmodule.c4
-rw-r--r--Modules/gcmodule.c6
-rw-r--r--Modules/socketmodule.c4
-rw-r--r--Modules/timemodule.c141
-rw-r--r--Python/pytime.c175
17 files changed, 226 insertions, 176 deletions
diff --git a/Doc/library/time.rst b/Doc/library/time.rst
index 64b5e04..7c3e4f0 100644
--- a/Doc/library/time.rst
+++ b/Doc/library/time.rst
@@ -315,9 +315,9 @@ The module defines the following functions and data items:
processes running for more than 49 days. On more recent versions of Windows
and on other operating systems, :func:`monotonic` is system-wide.
- Availability: Windows, Mac OS X, Linux, FreeBSD, OpenBSD, Solaris.
-
.. versionadded:: 3.3
+ .. versionchanged:: 3.5
+ The function is now always available.
.. function:: perf_counter()
diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst
index 28c2447..43972b0 100644
--- a/Doc/whatsnew/3.5.rst
+++ b/Doc/whatsnew/3.5.rst
@@ -238,6 +238,11 @@ socket
:meth:`socket.socket.send`.
(contributed by Giampaolo Rodola' in :issue:`17552`)
+time
+----
+
+The :func:`time.monotonic` function is now always available (:issue`22043`).
+
wsgiref
-------
diff --git a/Include/pytime.h b/Include/pytime.h
index 0f969b3..7a14456 100644
--- a/Include/pytime.h
+++ b/Include/pytime.h
@@ -91,6 +91,24 @@ PyAPI_FUNC(int) _PyTime_ObjectToTimespec(
long *nsec,
_PyTime_round_t);
+/* Get the time of a monotonic clock, i.e. a clock that cannot go backwards.
+ The clock is not affected by system clock updates. The reference point of
+ the returned value is undefined, so that only the difference between the
+ results of consecutive calls is valid.
+
+ The function never fails. _PyTime_Init() ensures that a monotonic clock
+ is available and works. */
+PyAPI_FUNC(void) _PyTime_monotonic(
+ _PyTime_timeval *tp);
+
+/* Similar to _PyTime_monotonic(), fill also info (if set) with information of
+ the function used to get the time.
+
+ Return 0 on success, raise an exception and return -1 on error. */
+PyAPI_FUNC(int) _PyTime_monotonic_info(
+ _PyTime_timeval *tp,
+ _Py_clock_info_t *info);
+
/* Initialize time.
Return 0 on success, raise an exception and return -1 on error. */
PyAPI_FUNC(int) _PyTime_Init(void);
diff --git a/Lib/queue.py b/Lib/queue.py
index 3cee36b..572425e 100644
--- a/Lib/queue.py
+++ b/Lib/queue.py
@@ -6,10 +6,7 @@ except ImportError:
import dummy_threading as threading
from collections import deque
from heapq import heappush, heappop
-try:
- from time import monotonic as time
-except ImportError:
- from time import time
+from time import monotonic as time
__all__ = ['Empty', 'Full', 'Queue', 'PriorityQueue', 'LifoQueue']
diff --git a/Lib/sched.py b/Lib/sched.py
index 2e6b00a..409d126 100644
--- a/Lib/sched.py
+++ b/Lib/sched.py
@@ -35,10 +35,7 @@ try:
import threading
except ImportError:
import dummy_threading as threading
-try:
- from time import monotonic as _time
-except ImportError:
- from time import time as _time
+from time import monotonic as _time
__all__ = ["scheduler"]
diff --git a/Lib/socketserver.py b/Lib/socketserver.py
index b585640..3d58590 100644
--- a/Lib/socketserver.py
+++ b/Lib/socketserver.py
@@ -136,10 +136,7 @@ try:
import threading
except ImportError:
import dummy_threading as threading
-try:
- from time import monotonic as time
-except ImportError:
- from time import time as time
+from time import monotonic as time
__all__ = ["TCPServer","UDPServer","ForkingUDPServer","ForkingTCPServer",
"ThreadingUDPServer","ThreadingTCPServer","BaseRequestHandler",
diff --git a/Lib/subprocess.py b/Lib/subprocess.py
index 6dfa40b..bb7d0dc 100644
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -365,10 +365,7 @@ import signal
import builtins
import warnings
import errno
-try:
- from time import monotonic as _time
-except ImportError:
- from time import time as _time
+from time import monotonic as _time
# Exception classes used by this module.
class SubprocessError(Exception): pass
diff --git a/Lib/telnetlib.py b/Lib/telnetlib.py
index 0cacac8..eebb952 100644
--- a/Lib/telnetlib.py
+++ b/Lib/telnetlib.py
@@ -36,10 +36,7 @@ To do:
import sys
import socket
import selectors
-try:
- from time import monotonic as _time
-except ImportError:
- from time import time as _time
+from time import monotonic as _time
__all__ = ["Telnet"]
diff --git a/Lib/test/test_selectors.py b/Lib/test/test_selectors.py
index c2eba15..544ea7b 100644
--- a/Lib/test/test_selectors.py
+++ b/Lib/test/test_selectors.py
@@ -8,10 +8,7 @@ from test import support
from time import sleep
import unittest
import unittest.mock
-try:
- from time import monotonic as time
-except ImportError:
- from time import time as time
+from time import monotonic as time
try:
import resource
except ImportError:
diff --git a/Lib/threading.py b/Lib/threading.py
index 66620a9..2ce037e 100644
--- a/Lib/threading.py
+++ b/Lib/threading.py
@@ -3,10 +3,7 @@
import sys as _sys
import _thread
-try:
- from time import monotonic as _time
-except ImportError:
- from time import time as _time
+from time import monotonic as _time
from traceback import format_exc as _format_exc
from _weakrefset import WeakSet
from itertools import islice as _islice
diff --git a/Lib/trace.py b/Lib/trace.py
index 1c888ac..fe84973 100755
--- a/Lib/trace.py
+++ b/Lib/trace.py
@@ -59,10 +59,7 @@ import gc
import dis
import pickle
from warnings import warn as _warn
-try:
- from time import monotonic as _time
-except ImportError:
- from time import time as _time
+from time import monotonic as _time
try:
import threading
diff --git a/Misc/NEWS b/Misc/NEWS
index a02e8a6..46c35ed 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -129,6 +129,11 @@ Core and Builtins
Library
-------
+- Issue #22043: time.monotonic() is now always available.
+ ``threading.Lock.acquire()``, ``threading.RLock.acquire()`` and socket
+ operations now use a monotonic clock, instead of the system clock, when a
+ timeout is used.
+
- Issue #21527: Add a default number of workers to ThreadPoolExecutor equal
to 5 times the number of CPUs. Patch by Claudiu Popa.
diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c
index b68c177..8f59e03 100644
--- a/Modules/_threadmodule.c
+++ b/Modules/_threadmodule.c
@@ -57,7 +57,7 @@ acquire_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds)
if (microseconds > 0) {
- _PyTime_gettimeofday(&endtime);
+ _PyTime_monotonic(&endtime);
endtime.tv_sec += microseconds / (1000 * 1000);
endtime.tv_usec += microseconds % (1000 * 1000);
}
@@ -83,7 +83,7 @@ acquire_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds)
/* If we're using a timeout, recompute the timeout after processing
* signals, since those can take time. */
if (microseconds > 0) {
- _PyTime_gettimeofday(&curtime);
+ _PyTime_monotonic(&curtime);
microseconds = ((endtime.tv_sec - curtime.tv_sec) * 1000000 +
(endtime.tv_usec - curtime.tv_usec));
diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c
index 5e8e17b..142687b 100644
--- a/Modules/gcmodule.c
+++ b/Modules/gcmodule.c
@@ -25,7 +25,7 @@
#include "Python.h"
#include "frameobject.h" /* for PyFrame_ClearFreeList */
-#include "pytime.h" /* for _PyTime_gettimeofday, _PyTime_INTERVAL */
+#include "pytime.h" /* for _PyTime_monotonic, _PyTime_INTERVAL */
/* Get an object's GC head */
#define AS_GC(o) ((PyGC_Head *)(o)-1)
@@ -919,7 +919,7 @@ collect(int generation, Py_ssize_t *n_collected, Py_ssize_t *n_uncollectable,
for (i = 0; i < NUM_GENERATIONS; i++)
PySys_FormatStderr(" %zd",
gc_list_size(GEN_HEAD(i)));
- _PyTime_gettimeofday(&t1);
+ _PyTime_monotonic(&t1);
PySys_WriteStderr("\n");
}
@@ -1025,7 +1025,7 @@ collect(int generation, Py_ssize_t *n_collected, Py_ssize_t *n_uncollectable,
}
if (debug & DEBUG_STATS) {
_PyTime_timeval t2;
- _PyTime_gettimeofday(&t2);
+ _PyTime_monotonic(&t2);
if (m == 0 && n == 0)
PySys_WriteStderr("gc: done");
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
index abadd8a..db69d6e 100644
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -680,7 +680,7 @@ internal_select(PySocketSockObject *s, int writing)
double interval = s->sock_timeout; \
int has_timeout = s->sock_timeout > 0.0; \
if (has_timeout) { \
- _PyTime_gettimeofday(&now); \
+ _PyTime_monotonic(&now); \
deadline = now; \
_PyTime_ADD_SECONDS(deadline, s->sock_timeout); \
} \
@@ -691,7 +691,7 @@ internal_select(PySocketSockObject *s, int writing)
if (!has_timeout || \
(!CHECK_ERRNO(EWOULDBLOCK) && !CHECK_ERRNO(EAGAIN))) \
break; \
- _PyTime_gettimeofday(&now); \
+ _PyTime_monotonic(&now); \
interval = _PyTime_INTERVAL(now, deadline); \
} \
} \
diff --git a/Modules/timemodule.c b/Modules/timemodule.c
index c90d8f9..16f4f6d 100644
--- a/Modules/timemodule.c
+++ b/Modules/timemodule.c
@@ -37,10 +37,6 @@
#endif /* MS_WINDOWS */
#endif /* !__WATCOMC__ || __QNX__ */
-#if defined(__APPLE__)
-#include <mach/mach_time.h>
-#endif
-
/* Forward declarations */
static int floatsleep(double);
static PyObject* floattime(_Py_clock_info_t *info);
@@ -899,122 +895,15 @@ the local timezone used by methods such as localtime, but this behaviour\n\
should not be relied on.");
#endif /* HAVE_WORKING_TZSET */
-#if defined(MS_WINDOWS) || defined(__APPLE__) \
- || (defined(HAVE_CLOCK_GETTIME) \
- && (defined(CLOCK_HIGHRES) || defined(CLOCK_MONOTONIC)))
-#define PYMONOTONIC
-#endif
-
-#ifdef PYMONOTONIC
-static PyObject*
+static PyObject *
pymonotonic(_Py_clock_info_t *info)
{
-#if defined(MS_WINDOWS)
- static ULONGLONG (*GetTickCount64) (void) = NULL;
- static ULONGLONG (CALLBACK *Py_GetTickCount64)(void);
- static int has_getickcount64 = -1;
- double result;
-
- if (has_getickcount64 == -1) {
- /* GetTickCount64() was added to Windows Vista */
- if (winver.dwMajorVersion >= 6) {
- HINSTANCE hKernel32;
- hKernel32 = GetModuleHandleW(L"KERNEL32");
- *(FARPROC*)&Py_GetTickCount64 = GetProcAddress(hKernel32,
- "GetTickCount64");
- has_getickcount64 = (Py_GetTickCount64 != NULL);
- }
- else
- has_getickcount64 = 0;
- }
-
- if (has_getickcount64) {
- ULONGLONG ticks;
- ticks = Py_GetTickCount64();
- result = (double)ticks * 1e-3;
- }
- else {
- static DWORD last_ticks = 0;
- static DWORD n_overflow = 0;
- DWORD ticks;
-
- ticks = GetTickCount();
- if (ticks < last_ticks)
- n_overflow++;
- last_ticks = ticks;
-
- result = ldexp(n_overflow, 32);
- result += ticks;
- result *= 1e-3;
- }
-
- if (info) {
- DWORD timeAdjustment, timeIncrement;
- BOOL isTimeAdjustmentDisabled, ok;
- if (has_getickcount64)
- info->implementation = "GetTickCount64()";
- else
- info->implementation = "GetTickCount()";
- info->monotonic = 1;
- ok = GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement,
- &isTimeAdjustmentDisabled);
- if (!ok) {
- PyErr_SetFromWindowsErr(0);
- return NULL;
- }
- info->resolution = timeIncrement * 1e-7;
- info->adjustable = 0;
- }
- return PyFloat_FromDouble(result);
-
-#elif defined(__APPLE__)
- static mach_timebase_info_data_t timebase;
- uint64_t time;
- double secs;
-
- 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();
- secs = (double)time * timebase.numer / timebase.denom * 1e-9;
- if (info) {
- info->implementation = "mach_absolute_time()";
- info->resolution = (double)timebase.numer / timebase.denom * 1e-9;
- info->monotonic = 1;
- info->adjustable = 0;
- }
- return PyFloat_FromDouble(secs);
-
-#elif defined(HAVE_CLOCK_GETTIME) && (defined(CLOCK_HIGHRES) || defined(CLOCK_MONOTONIC))
- struct timespec tp;
-#ifdef CLOCK_HIGHRES
- const clockid_t clk_id = CLOCK_HIGHRES;
- const char *function = "clock_gettime(CLOCK_HIGHRES)";
-#else
- const clockid_t clk_id = CLOCK_MONOTONIC;
- const char *function = "clock_gettime(CLOCK_MONOTONIC)";
-#endif
-
- if (clock_gettime(clk_id, &tp) != 0) {
- PyErr_SetFromErrno(PyExc_OSError);
+ _PyTime_timeval tv;
+ if (_PyTime_monotonic_info(&tv, info) < 0) {
+ assert(info != NULL);
return NULL;
}
-
- if (info) {
- struct timespec res;
- info->monotonic = 1;
- info->implementation = function;
- info->adjustable = 0;
- if (clock_getres(clk_id, &res) == 0)
- info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
- else
- info->resolution = 1e-9;
- }
- return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9);
-#endif
+ return PyFloat_FromDouble((double)tv.tv_sec + tv.tv_usec * 1e-6);
}
static PyObject *
@@ -1027,7 +916,6 @@ PyDoc_STRVAR(monotonic_doc,
"monotonic() -> float\n\
\n\
Monotonic clock, cannot go backward.");
-#endif /* PYMONOTONIC */
static PyObject*
perf_counter(_Py_clock_info_t *info)
@@ -1035,20 +923,7 @@ perf_counter(_Py_clock_info_t *info)
#ifdef WIN32_PERF_COUNTER
return win_perf_counter(info);
#else
-
-#ifdef PYMONOTONIC
- static int use_monotonic = 1;
-
- if (use_monotonic) {
- PyObject *res = pymonotonic(info);
- if (res != NULL)
- return res;
- use_monotonic = 0;
- PyErr_Clear();
- }
-#endif
- return floattime(info);
-
+ return pymonotonic(info);
#endif
}
@@ -1216,10 +1091,8 @@ time_get_clock_info(PyObject *self, PyObject *args)
else if (strcmp(name, "clock") == 0)
obj = pyclock(&info);
#endif
-#ifdef PYMONOTONIC
else if (strcmp(name, "monotonic") == 0)
obj = pymonotonic(&info);
-#endif
else if (strcmp(name, "perf_counter") == 0)
obj = perf_counter(&info);
else if (strcmp(name, "process_time") == 0)
@@ -1411,9 +1284,7 @@ static PyMethodDef time_methods[] = {
#ifdef HAVE_WORKING_TZSET
{"tzset", time_tzset, METH_NOARGS, tzset_doc},
#endif
-#ifdef PYMONOTONIC
{"monotonic", time_monotonic, METH_NOARGS, monotonic_doc},
-#endif
{"process_time", time_process_time, METH_NOARGS, process_time_doc},
{"perf_counter", time_perf_counter, METH_NOARGS, perf_counter_doc},
{"get_clock_info", time_get_clock_info, METH_VARARGS, get_clock_info_doc},
diff --git a/Python/pytime.c b/Python/pytime.c
index 395a432..9964195 100644
--- a/Python/pytime.c
+++ b/Python/pytime.c
@@ -3,6 +3,14 @@
#include <windows.h>
#endif
+#if defined(__APPLE__)
+#include <mach/mach_time.h> /* mach_absolute_time(), mach_timebase_info() */
+#endif
+
+#ifdef MS_WINDOWS
+static OSVERSIONINFOEX winver;
+#endif
+
static int
pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise)
{
@@ -109,6 +117,160 @@ _PyTime_gettimeofday_info(_PyTime_timeval *tp, _Py_clock_info_t *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 = {-1, -1};
+#endif
+#if defined(MS_WINDOWS)
+ static ULONGLONG (*GetTickCount64) (void) = NULL;
+ static ULONGLONG (CALLBACK *Py_GetTickCount64)(void);
+ static int has_gettickcount64 = -1;
+ ULONGLONG result;
+
+ assert(info == NULL || raise);
+
+ if (has_gettickcount64 == -1) {
+ /* GetTickCount64() was added to Windows Vista */
+ has_gettickcount64 = (winver.dwMajorVersion >= 6);
+ if (has_gettickcount64) {
+ HINSTANCE hKernel32;
+ hKernel32 = GetModuleHandleW(L"KERNEL32");
+ *(FARPROC*)&Py_GetTickCount64 = GetProcAddress(hKernel32,
+ "GetTickCount64");
+ assert(Py_GetTickCount64 != NULL);
+ }
+ }
+
+ if (has_gettickcount64) {
+ result = Py_GetTickCount64();
+ }
+ else {
+ static DWORD last_ticks = 0;
+ static DWORD n_overflow = 0;
+ DWORD ticks;
+
+ ticks = GetTickCount();
+ if (ticks < last_ticks)
+ n_overflow++;
+ last_ticks = ticks;
+
+ result = (ULONGLONG)n_overflow << 32;
+ result += ticks;
+ }
+
+ tp->tv_sec = result / 1000;
+ tp->tv_usec = (result % 1000) * 1000;
+
+ if (info) {
+ DWORD timeAdjustment, timeIncrement;
+ BOOL isTimeAdjustmentDisabled, ok;
+ if (has_gettickcount64)
+ info->implementation = "GetTickCount64()";
+ else
+ info->implementation = "GetTickCount()";
+ 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 /= 1000;
+ /* apply timebase factor */
+ time *= timebase.numer;
+ time /= timebase.denom;
+ tp->tv_sec = time / (1000 * 1000);
+ tp->tv_usec = time % (1000 * 1000);
+
+ 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 / 1000;
+#endif
+ assert(0 <= tp->tv_usec && tp->tv_usec < 1000 * 1000);
+#ifdef Py_DEBUG
+ /* monotonic clock cannot go backward */
+ assert(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
error_time_t_overflow(void)
{
@@ -245,8 +407,21 @@ int
_PyTime_Init(void)
{
_PyTime_timeval tv;
+
+#ifdef MS_WINDOWS
+ winver.dwOSVersionInfoSize = sizeof(winver);
+ if (!GetVersionEx((OSVERSIONINFO*)&winver)) {
+ PyErr_SetFromWindowsErr(0);
+ return -1;
+ }
+#endif
+
/* 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;
}