mirror of https://github.com/python/cpython
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:
parent
9bb758cee7
commit
ae58649721
|
@ -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
|
processes running for more than 49 days. On more recent versions of Windows
|
||||||
and on other operating systems, :func:`monotonic` is system-wide.
|
and on other operating systems, :func:`monotonic` is system-wide.
|
||||||
|
|
||||||
Availability: Windows, Mac OS X, Linux, FreeBSD, OpenBSD, Solaris.
|
|
||||||
|
|
||||||
.. versionadded:: 3.3
|
.. versionadded:: 3.3
|
||||||
|
.. versionchanged:: 3.5
|
||||||
|
The function is now always available.
|
||||||
|
|
||||||
|
|
||||||
.. function:: perf_counter()
|
.. function:: perf_counter()
|
||||||
|
|
|
@ -238,6 +238,11 @@ socket
|
||||||
:meth:`socket.socket.send`.
|
:meth:`socket.socket.send`.
|
||||||
(contributed by Giampaolo Rodola' in :issue:`17552`)
|
(contributed by Giampaolo Rodola' in :issue:`17552`)
|
||||||
|
|
||||||
|
time
|
||||||
|
----
|
||||||
|
|
||||||
|
The :func:`time.monotonic` function is now always available (:issue`22043`).
|
||||||
|
|
||||||
wsgiref
|
wsgiref
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
|
|
@ -91,6 +91,24 @@ PyAPI_FUNC(int) _PyTime_ObjectToTimespec(
|
||||||
long *nsec,
|
long *nsec,
|
||||||
_PyTime_round_t);
|
_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.
|
/* Initialize time.
|
||||||
Return 0 on success, raise an exception and return -1 on error. */
|
Return 0 on success, raise an exception and return -1 on error. */
|
||||||
PyAPI_FUNC(int) _PyTime_Init(void);
|
PyAPI_FUNC(int) _PyTime_Init(void);
|
||||||
|
|
|
@ -6,10 +6,7 @@ except ImportError:
|
||||||
import dummy_threading as threading
|
import dummy_threading as threading
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from heapq import heappush, heappop
|
from heapq import heappush, heappop
|
||||||
try:
|
from time import monotonic as time
|
||||||
from time import monotonic as time
|
|
||||||
except ImportError:
|
|
||||||
from time import time
|
|
||||||
|
|
||||||
__all__ = ['Empty', 'Full', 'Queue', 'PriorityQueue', 'LifoQueue']
|
__all__ = ['Empty', 'Full', 'Queue', 'PriorityQueue', 'LifoQueue']
|
||||||
|
|
||||||
|
|
|
@ -35,10 +35,7 @@ try:
|
||||||
import threading
|
import threading
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import dummy_threading as threading
|
import dummy_threading as threading
|
||||||
try:
|
from time import monotonic as _time
|
||||||
from time import monotonic as _time
|
|
||||||
except ImportError:
|
|
||||||
from time import time as _time
|
|
||||||
|
|
||||||
__all__ = ["scheduler"]
|
__all__ = ["scheduler"]
|
||||||
|
|
||||||
|
|
|
@ -136,10 +136,7 @@ try:
|
||||||
import threading
|
import threading
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import dummy_threading as threading
|
import dummy_threading as threading
|
||||||
try:
|
from time import monotonic as time
|
||||||
from time import monotonic as time
|
|
||||||
except ImportError:
|
|
||||||
from time import time as time
|
|
||||||
|
|
||||||
__all__ = ["TCPServer","UDPServer","ForkingUDPServer","ForkingTCPServer",
|
__all__ = ["TCPServer","UDPServer","ForkingUDPServer","ForkingTCPServer",
|
||||||
"ThreadingUDPServer","ThreadingTCPServer","BaseRequestHandler",
|
"ThreadingUDPServer","ThreadingTCPServer","BaseRequestHandler",
|
||||||
|
|
|
@ -365,10 +365,7 @@ import signal
|
||||||
import builtins
|
import builtins
|
||||||
import warnings
|
import warnings
|
||||||
import errno
|
import errno
|
||||||
try:
|
from time import monotonic as _time
|
||||||
from time import monotonic as _time
|
|
||||||
except ImportError:
|
|
||||||
from time import time as _time
|
|
||||||
|
|
||||||
# Exception classes used by this module.
|
# Exception classes used by this module.
|
||||||
class SubprocessError(Exception): pass
|
class SubprocessError(Exception): pass
|
||||||
|
|
|
@ -36,10 +36,7 @@ To do:
|
||||||
import sys
|
import sys
|
||||||
import socket
|
import socket
|
||||||
import selectors
|
import selectors
|
||||||
try:
|
from time import monotonic as _time
|
||||||
from time import monotonic as _time
|
|
||||||
except ImportError:
|
|
||||||
from time import time as _time
|
|
||||||
|
|
||||||
__all__ = ["Telnet"]
|
__all__ = ["Telnet"]
|
||||||
|
|
||||||
|
|
|
@ -8,10 +8,7 @@ from test import support
|
||||||
from time import sleep
|
from time import sleep
|
||||||
import unittest
|
import unittest
|
||||||
import unittest.mock
|
import unittest.mock
|
||||||
try:
|
from time import monotonic as time
|
||||||
from time import monotonic as time
|
|
||||||
except ImportError:
|
|
||||||
from time import time as time
|
|
||||||
try:
|
try:
|
||||||
import resource
|
import resource
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
|
@ -3,10 +3,7 @@
|
||||||
import sys as _sys
|
import sys as _sys
|
||||||
import _thread
|
import _thread
|
||||||
|
|
||||||
try:
|
from time import monotonic as _time
|
||||||
from time import monotonic as _time
|
|
||||||
except ImportError:
|
|
||||||
from time import time as _time
|
|
||||||
from traceback import format_exc as _format_exc
|
from traceback import format_exc as _format_exc
|
||||||
from _weakrefset import WeakSet
|
from _weakrefset import WeakSet
|
||||||
from itertools import islice as _islice
|
from itertools import islice as _islice
|
||||||
|
|
|
@ -59,10 +59,7 @@ import gc
|
||||||
import dis
|
import dis
|
||||||
import pickle
|
import pickle
|
||||||
from warnings import warn as _warn
|
from warnings import warn as _warn
|
||||||
try:
|
from time import monotonic as _time
|
||||||
from time import monotonic as _time
|
|
||||||
except ImportError:
|
|
||||||
from time import time as _time
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import threading
|
import threading
|
||||||
|
|
|
@ -129,6 +129,11 @@ Core and Builtins
|
||||||
Library
|
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
|
- Issue #21527: Add a default number of workers to ThreadPoolExecutor equal
|
||||||
to 5 times the number of CPUs. Patch by Claudiu Popa.
|
to 5 times the number of CPUs. Patch by Claudiu Popa.
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@ acquire_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds)
|
||||||
|
|
||||||
|
|
||||||
if (microseconds > 0) {
|
if (microseconds > 0) {
|
||||||
_PyTime_gettimeofday(&endtime);
|
_PyTime_monotonic(&endtime);
|
||||||
endtime.tv_sec += microseconds / (1000 * 1000);
|
endtime.tv_sec += microseconds / (1000 * 1000);
|
||||||
endtime.tv_usec += 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
|
/* If we're using a timeout, recompute the timeout after processing
|
||||||
* signals, since those can take time. */
|
* signals, since those can take time. */
|
||||||
if (microseconds > 0) {
|
if (microseconds > 0) {
|
||||||
_PyTime_gettimeofday(&curtime);
|
_PyTime_monotonic(&curtime);
|
||||||
microseconds = ((endtime.tv_sec - curtime.tv_sec) * 1000000 +
|
microseconds = ((endtime.tv_sec - curtime.tv_sec) * 1000000 +
|
||||||
(endtime.tv_usec - curtime.tv_usec));
|
(endtime.tv_usec - curtime.tv_usec));
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
#include "frameobject.h" /* for PyFrame_ClearFreeList */
|
#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 */
|
/* Get an object's GC head */
|
||||||
#define AS_GC(o) ((PyGC_Head *)(o)-1)
|
#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++)
|
for (i = 0; i < NUM_GENERATIONS; i++)
|
||||||
PySys_FormatStderr(" %zd",
|
PySys_FormatStderr(" %zd",
|
||||||
gc_list_size(GEN_HEAD(i)));
|
gc_list_size(GEN_HEAD(i)));
|
||||||
_PyTime_gettimeofday(&t1);
|
_PyTime_monotonic(&t1);
|
||||||
|
|
||||||
PySys_WriteStderr("\n");
|
PySys_WriteStderr("\n");
|
||||||
}
|
}
|
||||||
|
@ -1025,7 +1025,7 @@ collect(int generation, Py_ssize_t *n_collected, Py_ssize_t *n_uncollectable,
|
||||||
}
|
}
|
||||||
if (debug & DEBUG_STATS) {
|
if (debug & DEBUG_STATS) {
|
||||||
_PyTime_timeval t2;
|
_PyTime_timeval t2;
|
||||||
_PyTime_gettimeofday(&t2);
|
_PyTime_monotonic(&t2);
|
||||||
|
|
||||||
if (m == 0 && n == 0)
|
if (m == 0 && n == 0)
|
||||||
PySys_WriteStderr("gc: done");
|
PySys_WriteStderr("gc: done");
|
||||||
|
|
|
@ -680,7 +680,7 @@ internal_select(PySocketSockObject *s, int writing)
|
||||||
double interval = s->sock_timeout; \
|
double interval = s->sock_timeout; \
|
||||||
int has_timeout = s->sock_timeout > 0.0; \
|
int has_timeout = s->sock_timeout > 0.0; \
|
||||||
if (has_timeout) { \
|
if (has_timeout) { \
|
||||||
_PyTime_gettimeofday(&now); \
|
_PyTime_monotonic(&now); \
|
||||||
deadline = now; \
|
deadline = now; \
|
||||||
_PyTime_ADD_SECONDS(deadline, s->sock_timeout); \
|
_PyTime_ADD_SECONDS(deadline, s->sock_timeout); \
|
||||||
} \
|
} \
|
||||||
|
@ -691,7 +691,7 @@ internal_select(PySocketSockObject *s, int writing)
|
||||||
if (!has_timeout || \
|
if (!has_timeout || \
|
||||||
(!CHECK_ERRNO(EWOULDBLOCK) && !CHECK_ERRNO(EAGAIN))) \
|
(!CHECK_ERRNO(EWOULDBLOCK) && !CHECK_ERRNO(EAGAIN))) \
|
||||||
break; \
|
break; \
|
||||||
_PyTime_gettimeofday(&now); \
|
_PyTime_monotonic(&now); \
|
||||||
interval = _PyTime_INTERVAL(now, deadline); \
|
interval = _PyTime_INTERVAL(now, deadline); \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
|
|
|
@ -37,10 +37,6 @@
|
||||||
#endif /* MS_WINDOWS */
|
#endif /* MS_WINDOWS */
|
||||||
#endif /* !__WATCOMC__ || __QNX__ */
|
#endif /* !__WATCOMC__ || __QNX__ */
|
||||||
|
|
||||||
#if defined(__APPLE__)
|
|
||||||
#include <mach/mach_time.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Forward declarations */
|
/* Forward declarations */
|
||||||
static int floatsleep(double);
|
static int floatsleep(double);
|
||||||
static PyObject* floattime(_Py_clock_info_t *info);
|
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.");
|
should not be relied on.");
|
||||||
#endif /* HAVE_WORKING_TZSET */
|
#endif /* HAVE_WORKING_TZSET */
|
||||||
|
|
||||||
#if defined(MS_WINDOWS) || defined(__APPLE__) \
|
static PyObject *
|
||||||
|| (defined(HAVE_CLOCK_GETTIME) \
|
|
||||||
&& (defined(CLOCK_HIGHRES) || defined(CLOCK_MONOTONIC)))
|
|
||||||
#define PYMONOTONIC
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef PYMONOTONIC
|
|
||||||
static PyObject*
|
|
||||||
pymonotonic(_Py_clock_info_t *info)
|
pymonotonic(_Py_clock_info_t *info)
|
||||||
{
|
{
|
||||||
#if defined(MS_WINDOWS)
|
_PyTime_timeval tv;
|
||||||
static ULONGLONG (*GetTickCount64) (void) = NULL;
|
if (_PyTime_monotonic_info(&tv, info) < 0) {
|
||||||
static ULONGLONG (CALLBACK *Py_GetTickCount64)(void);
|
assert(info != NULL);
|
||||||
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);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
return PyFloat_FromDouble((double)tv.tv_sec + tv.tv_usec * 1e-6);
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -1027,7 +916,6 @@ PyDoc_STRVAR(monotonic_doc,
|
||||||
"monotonic() -> float\n\
|
"monotonic() -> float\n\
|
||||||
\n\
|
\n\
|
||||||
Monotonic clock, cannot go backward.");
|
Monotonic clock, cannot go backward.");
|
||||||
#endif /* PYMONOTONIC */
|
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
perf_counter(_Py_clock_info_t *info)
|
perf_counter(_Py_clock_info_t *info)
|
||||||
|
@ -1035,20 +923,7 @@ perf_counter(_Py_clock_info_t *info)
|
||||||
#ifdef WIN32_PERF_COUNTER
|
#ifdef WIN32_PERF_COUNTER
|
||||||
return win_perf_counter(info);
|
return win_perf_counter(info);
|
||||||
#else
|
#else
|
||||||
|
return pymonotonic(info);
|
||||||
#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);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1216,10 +1091,8 @@ time_get_clock_info(PyObject *self, PyObject *args)
|
||||||
else if (strcmp(name, "clock") == 0)
|
else if (strcmp(name, "clock") == 0)
|
||||||
obj = pyclock(&info);
|
obj = pyclock(&info);
|
||||||
#endif
|
#endif
|
||||||
#ifdef PYMONOTONIC
|
|
||||||
else if (strcmp(name, "monotonic") == 0)
|
else if (strcmp(name, "monotonic") == 0)
|
||||||
obj = pymonotonic(&info);
|
obj = pymonotonic(&info);
|
||||||
#endif
|
|
||||||
else if (strcmp(name, "perf_counter") == 0)
|
else if (strcmp(name, "perf_counter") == 0)
|
||||||
obj = perf_counter(&info);
|
obj = perf_counter(&info);
|
||||||
else if (strcmp(name, "process_time") == 0)
|
else if (strcmp(name, "process_time") == 0)
|
||||||
|
@ -1411,9 +1284,7 @@ static PyMethodDef time_methods[] = {
|
||||||
#ifdef HAVE_WORKING_TZSET
|
#ifdef HAVE_WORKING_TZSET
|
||||||
{"tzset", time_tzset, METH_NOARGS, tzset_doc},
|
{"tzset", time_tzset, METH_NOARGS, tzset_doc},
|
||||||
#endif
|
#endif
|
||||||
#ifdef PYMONOTONIC
|
|
||||||
{"monotonic", time_monotonic, METH_NOARGS, monotonic_doc},
|
{"monotonic", time_monotonic, METH_NOARGS, monotonic_doc},
|
||||||
#endif
|
|
||||||
{"process_time", time_process_time, METH_NOARGS, process_time_doc},
|
{"process_time", time_process_time, METH_NOARGS, process_time_doc},
|
||||||
{"perf_counter", time_perf_counter, METH_NOARGS, perf_counter_doc},
|
{"perf_counter", time_perf_counter, METH_NOARGS, perf_counter_doc},
|
||||||
{"get_clock_info", time_get_clock_info, METH_VARARGS, get_clock_info_doc},
|
{"get_clock_info", time_get_clock_info, METH_VARARGS, get_clock_info_doc},
|
||||||
|
|
175
Python/pytime.c
175
Python/pytime.c
|
@ -3,6 +3,14 @@
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#endif
|
#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
|
static int
|
||||||
pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise)
|
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);
|
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
|
static void
|
||||||
error_time_t_overflow(void)
|
error_time_t_overflow(void)
|
||||||
{
|
{
|
||||||
|
@ -245,8 +407,21 @@ int
|
||||||
_PyTime_Init(void)
|
_PyTime_Init(void)
|
||||||
{
|
{
|
||||||
_PyTime_timeval tv;
|
_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 */
|
/* ensure that the system clock works */
|
||||||
if (_PyTime_gettimeofday_info(&tv, NULL) < 0)
|
if (_PyTime_gettimeofday_info(&tv, NULL) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
/* ensure that the operating system provides a monotonic clock */
|
||||||
|
if (_PyTime_monotonic_info(&tv, NULL) < 0)
|
||||||
|
return -1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue