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.
This commit is contained in:
Victor Stinner 2014-09-02 23:18:25 +02:00
parent 9bb758cee7
commit ae58649721
17 changed files with 226 additions and 176 deletions

View File

@ -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()

View File

@ -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
-------

View File

@ -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);

View File

@ -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']

View File

@ -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"]

View File

@ -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",

View File

@ -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

View File

@ -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"]

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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));

View File

@ -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");

View File

@ -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); \
} \
} \

View File

@ -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},

View File

@ -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;
}