* Rename time.steady() to time.monotonic() * On Windows, time.monotonic() uses GetTickCount/GetTickCount64() instead of QueryPerformanceCounter() * time.monotonic() uses CLOCK_HIGHRES if available * Add time.get_clock_info(), time.perf_counter() and time.process_time() functions
This commit is contained in:
parent
ca6e40f12a
commit
ec89539ccc
|
@ -155,6 +155,30 @@ The module defines the following functions and data items:
|
||||||
.. versionadded:: 3.3
|
.. versionadded:: 3.3
|
||||||
|
|
||||||
|
|
||||||
|
.. class:: clock_info
|
||||||
|
|
||||||
|
Clock information object created by :func:`get_clock_info`.
|
||||||
|
|
||||||
|
.. attribute:: implementation
|
||||||
|
|
||||||
|
name of the underlying C function used to get the clock value
|
||||||
|
|
||||||
|
.. attribute:: is_monotonic
|
||||||
|
|
||||||
|
``True`` if the clock cannot go backward, ``False`` otherwise
|
||||||
|
|
||||||
|
.. attribute:: is_adjusted
|
||||||
|
|
||||||
|
``True`` if the clock can be adjusted (e.g. by a NTP daemon),
|
||||||
|
``False`` otherwise
|
||||||
|
|
||||||
|
.. attribute:: resolution
|
||||||
|
|
||||||
|
Resolution of the clock in seconds (:class:`float`)
|
||||||
|
|
||||||
|
.. versionadded:: 3.3
|
||||||
|
|
||||||
|
|
||||||
.. function:: clock_settime(clk_id, time)
|
.. function:: clock_settime(clk_id, time)
|
||||||
|
|
||||||
Set the time of the specified clock *clk_id*.
|
Set the time of the specified clock *clk_id*.
|
||||||
|
@ -236,6 +260,22 @@ The module defines the following functions and data items:
|
||||||
Nonzero if a DST timezone is defined.
|
Nonzero if a DST timezone is defined.
|
||||||
|
|
||||||
|
|
||||||
|
.. function:: get_clock_info(name)
|
||||||
|
|
||||||
|
Get information on the specified clock as a :class:`clock_info` object.
|
||||||
|
|
||||||
|
Supported clock names:
|
||||||
|
|
||||||
|
|
||||||
|
* ``'clock'``: :func:`time.clock`
|
||||||
|
* ``'monotonic'``: :func:`time.monotonic`
|
||||||
|
* ``'perf_counter'``: :func:`time.perf_counter`
|
||||||
|
* ``'process_time'``: :func:`time.process_time`
|
||||||
|
* ``'time'``: :func:`time.time`
|
||||||
|
|
||||||
|
.. versionadded:: 3.3
|
||||||
|
|
||||||
|
|
||||||
.. function:: gmtime([secs])
|
.. function:: gmtime([secs])
|
||||||
|
|
||||||
Convert a time expressed in seconds since the epoch to a :class:`struct_time` in
|
Convert a time expressed in seconds since the epoch to a :class:`struct_time` in
|
||||||
|
@ -265,20 +305,43 @@ 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:: steady(strict=False)
|
.. function:: monotonic()
|
||||||
|
|
||||||
.. index::
|
Monotonic clock, i.e. cannot go backward. It is not affected by system
|
||||||
single: benchmarking
|
clock updates. The reference point of the returned value is undefined, so
|
||||||
|
that only the difference between the results of consecutive calls is valid
|
||||||
|
and is a number of seconds.
|
||||||
|
|
||||||
Return the current time as a floating point number expressed in seconds.
|
On Windows versions older than Vista, :func:`monotonic` detects
|
||||||
This clock advances at a steady rate relative to real time and it may not be
|
:c:func:`GetTickCount` integer overflow (32 bits, roll-over after 49.7
|
||||||
adjusted. The reference point of the returned value is undefined so only the
|
days). It increases an internal epoch (reference time by) 2\ :sup:`32` each
|
||||||
difference of consecutive calls is valid.
|
time that an overflow is detected. The epoch is stored in the process-local
|
||||||
|
state and so the value of :func:`monotonic` may be different in two Python
|
||||||
|
processes running for more than 49 days. On more recent versions of Windows
|
||||||
|
and on other operating systems, :func:`monotonic` is system-wide.
|
||||||
|
|
||||||
If available, a monotonic clock is used. By default,
|
Availability: Windows, Mac OS X, Linux, FreeBSD, OpenBSD, Solaris.
|
||||||
the function falls back to another clock if the monotonic clock failed or is
|
|
||||||
not available. If *strict* is True, raise an :exc:`OSError` on error or
|
.. versionadded:: 3.3
|
||||||
:exc:`NotImplementedError` if no monotonic clock is available.
|
|
||||||
|
|
||||||
|
.. function:: perf_counter()
|
||||||
|
|
||||||
|
Performance counter with the highest available resolution to measure a short
|
||||||
|
duration. It does include time elapsed during sleep and is system-wide.
|
||||||
|
The reference point of the returned value is undefined, so that only the
|
||||||
|
difference between the results of consecutive calls is valid and is a number
|
||||||
|
of seconds.
|
||||||
|
|
||||||
|
.. versionadded:: 3.3
|
||||||
|
|
||||||
|
|
||||||
|
.. function:: process_time()
|
||||||
|
|
||||||
|
Sum of the system and user CPU time of the current process. It does not
|
||||||
|
include time elapsed during sleep. It is process-wide by definition. The
|
||||||
|
reference point of the returned value is undefined, so that only the
|
||||||
|
difference between the results of consecutive calls is valid.
|
||||||
|
|
||||||
.. versionadded:: 3.3
|
.. versionadded:: 3.3
|
||||||
|
|
||||||
|
|
|
@ -1059,12 +1059,20 @@ sys
|
||||||
time
|
time
|
||||||
----
|
----
|
||||||
|
|
||||||
The :mod:`time` module has new functions:
|
The :pep:`418` added new functions to the :mod:`time` module:
|
||||||
|
|
||||||
* :func:`~time.clock_getres` and :func:`~time.clock_gettime` functions and
|
* :func:`~time.get_clock_info`: Get information on a clock.
|
||||||
``CLOCK_xxx`` constants.
|
* :func:`~time.monotonic`: Monotonic clock (cannot go backward), not affected
|
||||||
* :func:`~time.steady`.
|
by system clock updates.
|
||||||
|
* :func:`~time.perf_counter`: Performance counter with the highest available
|
||||||
|
resolution to measure a short duration.
|
||||||
|
* :func:`~time.process_time`: Sum of the system and user CPU time of the
|
||||||
|
current process.
|
||||||
|
|
||||||
|
Other new functions:
|
||||||
|
|
||||||
|
* :func:`~time.clock_getres`, :func:`~time.clock_gettime` and
|
||||||
|
:func:`~time.clock_settime` functions with ``CLOCK_xxx`` constants.
|
||||||
(Contributed by Victor Stinner in :issue:`10278`)
|
(Contributed by Victor Stinner in :issue:`10278`)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -22,11 +22,25 @@ typedef struct {
|
||||||
} _PyTime_timeval;
|
} _PyTime_timeval;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Structure used by time.get_clock_info() */
|
||||||
|
typedef struct {
|
||||||
|
const char *implementation;
|
||||||
|
int is_monotonic;
|
||||||
|
int is_adjusted;
|
||||||
|
double resolution;
|
||||||
|
} _Py_clock_info_t;
|
||||||
|
|
||||||
/* Similar to POSIX gettimeofday but cannot fail. If system gettimeofday
|
/* Similar to POSIX gettimeofday but cannot fail. If system gettimeofday
|
||||||
* fails or is not available, fall back to lower resolution clocks.
|
* fails or is not available, fall back to lower resolution clocks.
|
||||||
*/
|
*/
|
||||||
PyAPI_FUNC(void) _PyTime_gettimeofday(_PyTime_timeval *tp);
|
PyAPI_FUNC(void) _PyTime_gettimeofday(_PyTime_timeval *tp);
|
||||||
|
|
||||||
|
/* Similar to _PyTime_gettimeofday() but retrieve also information on the
|
||||||
|
* clock used to get the current time. */
|
||||||
|
PyAPI_FUNC(void) _PyTime_gettimeofday_info(
|
||||||
|
_PyTime_timeval *tp,
|
||||||
|
_Py_clock_info_t *info);
|
||||||
|
|
||||||
#define _PyTime_ADD_SECONDS(tv, interval) \
|
#define _PyTime_ADD_SECONDS(tv, interval) \
|
||||||
do { \
|
do { \
|
||||||
tv.tv_usec += (long) (((long) interval - interval) * 1000000); \
|
tv.tv_usec += (long) (((long) interval - interval) * 1000000); \
|
||||||
|
|
|
@ -6,7 +6,10 @@ 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
|
||||||
from time import steady as time
|
try:
|
||||||
|
from time import monotonic as time
|
||||||
|
except ImportError:
|
||||||
|
from time import time
|
||||||
|
|
||||||
__all__ = ['Empty', 'Full', 'Queue', 'PriorityQueue', 'LifoQueue']
|
__all__ = ['Empty', 'Full', 'Queue', 'PriorityQueue', 'LifoQueue']
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,10 @@ import locale
|
||||||
import sysconfig
|
import sysconfig
|
||||||
import sys
|
import sys
|
||||||
import platform
|
import platform
|
||||||
|
try:
|
||||||
|
import threading
|
||||||
|
except ImportError:
|
||||||
|
threading = None
|
||||||
|
|
||||||
# Max year is only limited by the size of C int.
|
# Max year is only limited by the size of C int.
|
||||||
SIZEOF_INT = sysconfig.get_config_var('SIZEOF_INT') or 4
|
SIZEOF_INT = sysconfig.get_config_var('SIZEOF_INT') or 4
|
||||||
|
@ -23,9 +27,20 @@ class TimeTestCase(unittest.TestCase):
|
||||||
time.timezone
|
time.timezone
|
||||||
time.tzname
|
time.tzname
|
||||||
|
|
||||||
|
def test_time(self):
|
||||||
|
time.time()
|
||||||
|
info = time.get_clock_info('time')
|
||||||
|
self.assertEqual(info.is_monotonic, False)
|
||||||
|
if sys.platform != 'win32':
|
||||||
|
self.assertEqual(info.is_adjusted, True)
|
||||||
|
|
||||||
def test_clock(self):
|
def test_clock(self):
|
||||||
time.clock()
|
time.clock()
|
||||||
|
|
||||||
|
info = time.get_clock_info('clock')
|
||||||
|
self.assertEqual(info.is_monotonic, True)
|
||||||
|
self.assertEqual(info.is_adjusted, False)
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(time, 'clock_gettime'),
|
@unittest.skipUnless(hasattr(time, 'clock_gettime'),
|
||||||
'need time.clock_gettime()')
|
'need time.clock_gettime()')
|
||||||
def test_clock_realtime(self):
|
def test_clock_realtime(self):
|
||||||
|
@ -56,7 +71,9 @@ class TimeTestCase(unittest.TestCase):
|
||||||
except PermissionError:
|
except PermissionError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.assertRaises(OSError, time.clock_settime, time.CLOCK_MONOTONIC, 0)
|
if hasattr(time, 'CLOCK_MONOTONIC'):
|
||||||
|
self.assertRaises(OSError,
|
||||||
|
time.clock_settime, time.CLOCK_MONOTONIC, 0)
|
||||||
|
|
||||||
def test_conversions(self):
|
def test_conversions(self):
|
||||||
self.assertEqual(time.ctime(self.t),
|
self.assertEqual(time.ctime(self.t),
|
||||||
|
@ -342,23 +359,69 @@ class TimeTestCase(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
self.assertEqual(time.strftime('%Z', tt), tzname)
|
self.assertEqual(time.strftime('%Z', tt), tzname)
|
||||||
|
|
||||||
def test_steady(self):
|
@unittest.skipUnless(hasattr(time, 'monotonic'),
|
||||||
t1 = time.steady()
|
'need time.monotonic')
|
||||||
|
def test_monotonic(self):
|
||||||
|
t1 = time.monotonic()
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
t2 = time.steady()
|
t2 = time.monotonic()
|
||||||
dt = t2 - t1
|
dt = t2 - t1
|
||||||
# may fail if the system clock was changed
|
|
||||||
self.assertGreater(t2, t1)
|
self.assertGreater(t2, t1)
|
||||||
self.assertAlmostEqual(dt, 0.1, delta=0.2)
|
self.assertAlmostEqual(dt, 0.1, delta=0.2)
|
||||||
|
|
||||||
def test_steady_strict(self):
|
info = time.get_clock_info('monotonic')
|
||||||
|
self.assertEqual(info.is_monotonic, True)
|
||||||
|
if sys.platform == 'linux':
|
||||||
|
self.assertEqual(info.is_adjusted, True)
|
||||||
|
else:
|
||||||
|
self.assertEqual(info.is_adjusted, False)
|
||||||
|
|
||||||
|
def test_perf_counter(self):
|
||||||
|
time.perf_counter()
|
||||||
|
|
||||||
|
def test_process_time(self):
|
||||||
|
start = time.process_time()
|
||||||
|
time.sleep(0.1)
|
||||||
|
stop = time.process_time()
|
||||||
|
self.assertLess(stop - start, 0.01)
|
||||||
|
|
||||||
|
info = time.get_clock_info('process_time')
|
||||||
|
self.assertEqual(info.is_monotonic, True)
|
||||||
|
self.assertEqual(info.is_adjusted, False)
|
||||||
|
|
||||||
|
@unittest.skipUnless(threading,
|
||||||
|
'need threading')
|
||||||
|
def test_process_time_threads(self):
|
||||||
|
class BusyThread(threading.Thread):
|
||||||
|
def run(self):
|
||||||
|
while not self.stop:
|
||||||
|
pass
|
||||||
|
|
||||||
|
thread = BusyThread()
|
||||||
|
thread.stop = False
|
||||||
|
t1 = time.process_time()
|
||||||
|
thread.start()
|
||||||
|
time.sleep(0.2)
|
||||||
|
t2 = time.process_time()
|
||||||
|
thread.stop = True
|
||||||
|
thread.join()
|
||||||
|
self.assertGreater(t2 - t1, 0.1)
|
||||||
|
|
||||||
|
@unittest.skipUnless(hasattr(time, 'monotonic'),
|
||||||
|
'need time.monotonic')
|
||||||
|
@unittest.skipUnless(hasattr(time, 'clock_settime'),
|
||||||
|
'need time.clock_settime')
|
||||||
|
def test_monotonic_settime(self):
|
||||||
|
t1 = time.monotonic()
|
||||||
|
realtime = time.clock_gettime(time.CLOCK_REALTIME)
|
||||||
|
# jump backward with an offset of 1 hour
|
||||||
try:
|
try:
|
||||||
t1 = time.steady(strict=True)
|
time.clock_settime(time.CLOCK_REALTIME, realtime - 3600)
|
||||||
except OSError as err:
|
except PermissionError as err:
|
||||||
self.skipTest("the monotonic clock failed: %s" % err)
|
self.skipTest(err)
|
||||||
except NotImplementedError:
|
t2 = time.monotonic()
|
||||||
self.skipTest("no monotonic clock available")
|
time.clock_settime(time.CLOCK_REALTIME, realtime)
|
||||||
t2 = time.steady(strict=True)
|
# monotonic must not be affected by system clock updates
|
||||||
self.assertGreaterEqual(t2, t1)
|
self.assertGreaterEqual(t2, t1)
|
||||||
|
|
||||||
def test_localtime_failure(self):
|
def test_localtime_failure(self):
|
||||||
|
@ -378,6 +441,26 @@ class TimeTestCase(unittest.TestCase):
|
||||||
self.assertRaises(OSError, time.localtime, invalid_time_t)
|
self.assertRaises(OSError, time.localtime, invalid_time_t)
|
||||||
self.assertRaises(OSError, time.ctime, invalid_time_t)
|
self.assertRaises(OSError, time.ctime, invalid_time_t)
|
||||||
|
|
||||||
|
def test_get_clock_info(self):
|
||||||
|
clocks = ['clock', 'perf_counter', 'process_time', 'time']
|
||||||
|
if hasattr(time, 'monotonic'):
|
||||||
|
clocks.append('monotonic')
|
||||||
|
|
||||||
|
for name in clocks:
|
||||||
|
info = time.get_clock_info(name)
|
||||||
|
#self.assertIsInstance(info, dict)
|
||||||
|
self.assertIsInstance(info.implementation, str)
|
||||||
|
self.assertNotEqual(info.implementation, '')
|
||||||
|
self.assertIsInstance(info.is_monotonic, bool)
|
||||||
|
self.assertIsInstance(info.resolution, float)
|
||||||
|
# 0.0 < resolution <= 1.0
|
||||||
|
self.assertGreater(info.resolution, 0.0)
|
||||||
|
self.assertLessEqual(info.resolution, 1.0)
|
||||||
|
self.assertIsInstance(info.is_adjusted, bool)
|
||||||
|
|
||||||
|
self.assertRaises(ValueError, time.get_clock_info, 'xxx')
|
||||||
|
|
||||||
|
|
||||||
class TestLocale(unittest.TestCase):
|
class TestLocale(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.oldloc = locale.setlocale(locale.LC_ALL)
|
self.oldloc = locale.setlocale(locale.LC_ALL)
|
||||||
|
|
|
@ -3,7 +3,11 @@
|
||||||
import sys as _sys
|
import sys as _sys
|
||||||
import _thread
|
import _thread
|
||||||
|
|
||||||
from time import steady as _time, sleep as _sleep
|
from time import sleep as _sleep
|
||||||
|
try:
|
||||||
|
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
|
||||||
|
|
||||||
|
|
|
@ -81,6 +81,10 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #14428: Implement the PEP 418. Add time.get_clock_info(),
|
||||||
|
time.perf_counter() and time.process_time() functions, and rename
|
||||||
|
time.steady() to time.monotonic().
|
||||||
|
|
||||||
- Issue #14646: importlib.util.module_for_loader() now sets __loader__ and
|
- Issue #14646: importlib.util.module_for_loader() now sets __loader__ and
|
||||||
__package__ (when possible).
|
__package__ (when possible).
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,17 @@
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_SYS_TIMES_H
|
||||||
|
#include <sys/times.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_SYS_TYPES_H
|
#ifdef HAVE_SYS_TYPES_H
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#endif /* HAVE_SYS_TYPES_H */
|
#endif
|
||||||
|
|
||||||
|
#if defined(HAVE_SYS_RESOURCE_H)
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef QUICKWIN
|
#ifdef QUICKWIN
|
||||||
#include <io.h>
|
#include <io.h>
|
||||||
|
@ -45,12 +53,16 @@
|
||||||
|
|
||||||
/* Forward declarations */
|
/* Forward declarations */
|
||||||
static int floatsleep(double);
|
static int floatsleep(double);
|
||||||
static PyObject* floattime(void);
|
static PyObject* floattime(_Py_clock_info_t *info);
|
||||||
|
|
||||||
|
#ifdef MS_WINDOWS
|
||||||
|
static OSVERSIONINFOEX winver;
|
||||||
|
#endif
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
time_time(PyObject *self, PyObject *unused)
|
time_time(PyObject *self, PyObject *unused)
|
||||||
{
|
{
|
||||||
return floattime();
|
return floattime(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
PyDoc_STRVAR(time_doc,
|
PyDoc_STRVAR(time_doc,
|
||||||
|
@ -70,7 +82,7 @@ Fractions of a second may be present if the system clock provides them.");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
pyclock(void)
|
floatclock(_Py_clock_info_t *info)
|
||||||
{
|
{
|
||||||
clock_t value;
|
clock_t value;
|
||||||
value = clock();
|
value = clock();
|
||||||
|
@ -80,15 +92,22 @@ pyclock(void)
|
||||||
"or its value cannot be represented");
|
"or its value cannot be represented");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
if (info) {
|
||||||
|
info->implementation = "clock()";
|
||||||
|
info->resolution = 1.0 / (double)CLOCKS_PER_SEC;
|
||||||
|
info->is_monotonic = 1;
|
||||||
|
info->is_adjusted = 0;
|
||||||
|
}
|
||||||
return PyFloat_FromDouble((double)value / CLOCKS_PER_SEC);
|
return PyFloat_FromDouble((double)value / CLOCKS_PER_SEC);
|
||||||
}
|
}
|
||||||
#endif /* HAVE_CLOCK */
|
#endif /* HAVE_CLOCK */
|
||||||
|
|
||||||
#if defined(MS_WINDOWS) && !defined(__BORLANDC__)
|
#if defined(MS_WINDOWS) && !defined(__BORLANDC__)
|
||||||
|
#define WIN32_PERF_COUNTER
|
||||||
/* 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 int
|
||||||
win32_clock(int fallback)
|
win_perf_counter(_Py_clock_info_t *info, PyObject **result)
|
||||||
{
|
{
|
||||||
static LONGLONG cpu_frequency = 0;
|
static LONGLONG cpu_frequency = 0;
|
||||||
static LONGLONG ctrStart;
|
static LONGLONG ctrStart;
|
||||||
|
@ -102,28 +121,41 @@ win32_clock(int fallback)
|
||||||
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() */
|
||||||
if (fallback)
|
*result = NULL;
|
||||||
return pyclock();
|
return -1;
|
||||||
else
|
|
||||||
return PyErr_SetFromWindowsErr(0);
|
|
||||||
}
|
}
|
||||||
cpu_frequency = freq.QuadPart;
|
cpu_frequency = freq.QuadPart;
|
||||||
}
|
}
|
||||||
QueryPerformanceCounter(&now);
|
QueryPerformanceCounter(&now);
|
||||||
diff = (double)(now.QuadPart - ctrStart);
|
diff = (double)(now.QuadPart - ctrStart);
|
||||||
return PyFloat_FromDouble(diff / (double)cpu_frequency);
|
if (info) {
|
||||||
|
info->implementation = "QueryPerformanceCounter()";
|
||||||
|
info->resolution = 1.0 / (double)cpu_frequency;
|
||||||
|
info->is_monotonic = 1;
|
||||||
|
info->is_adjusted = 0;
|
||||||
|
}
|
||||||
|
*result = PyFloat_FromDouble(diff / (double)cpu_frequency);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if (defined(MS_WINDOWS) && !defined(__BORLANDC__)) || defined(HAVE_CLOCK)
|
#if defined(WIN32_PERF_COUNTER) || defined(HAVE_CLOCK)
|
||||||
|
#define PYCLOCK
|
||||||
|
static PyObject*
|
||||||
|
pyclock(_Py_clock_info_t *info)
|
||||||
|
{
|
||||||
|
#ifdef WIN32_PERF_COUNTER
|
||||||
|
PyObject *res;
|
||||||
|
if (win_perf_counter(info, &res) == 0)
|
||||||
|
return res;
|
||||||
|
#endif
|
||||||
|
return floatclock(info);
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
time_clock(PyObject *self, PyObject *unused)
|
time_clock(PyObject *self, PyObject *unused)
|
||||||
{
|
{
|
||||||
#if defined(MS_WINDOWS) && !defined(__BORLANDC__)
|
return pyclock(NULL);
|
||||||
return win32_clock(1);
|
|
||||||
#else
|
|
||||||
return pyclock();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PyDoc_STRVAR(clock_doc,
|
PyDoc_STRVAR(clock_doc,
|
||||||
|
@ -150,7 +182,6 @@ time_clock_gettime(PyObject *self, PyObject *args)
|
||||||
PyErr_SetFromErrno(PyExc_IOError);
|
PyErr_SetFromErrno(PyExc_IOError);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9);
|
return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -787,11 +818,74 @@ 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__) \
|
||||||
|
|| (defined(HAVE_CLOCK_GETTIME) \
|
||||||
|
&& (defined(CLOCK_HIGHRES) || defined(CLOCK_MONOTONIC)))
|
||||||
|
#define PYMONOTONIC
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PYMONOTONIC
|
||||||
static PyObject*
|
static PyObject*
|
||||||
steady_clock(int strict)
|
pymonotonic(_Py_clock_info_t *info)
|
||||||
{
|
{
|
||||||
#if defined(MS_WINDOWS) && !defined(__BORLANDC__)
|
#if defined(MS_WINDOWS)
|
||||||
return win32_clock(!strict);
|
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->is_monotonic = 1;
|
||||||
|
ok = GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement,
|
||||||
|
&isTimeAdjustmentDisabled);
|
||||||
|
if (!ok) {
|
||||||
|
PyErr_SetFromWindowsErr(0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
info->resolution = timeIncrement * 1e-7;
|
||||||
|
info->is_adjusted = 0;
|
||||||
|
}
|
||||||
|
return PyFloat_FromDouble(result);
|
||||||
|
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
static mach_timebase_info_data_t timebase;
|
static mach_timebase_info_data_t timebase;
|
||||||
uint64_t time;
|
uint64_t time;
|
||||||
|
@ -805,88 +899,338 @@ steady_clock(int strict)
|
||||||
|
|
||||||
time = mach_absolute_time();
|
time = mach_absolute_time();
|
||||||
secs = (double)time * timebase.numer / timebase.denom * 1e-9;
|
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->is_monotonic = 1;
|
||||||
|
info->is_adjusted = 0;
|
||||||
|
}
|
||||||
return PyFloat_FromDouble(secs);
|
return PyFloat_FromDouble(secs);
|
||||||
#elif defined(HAVE_CLOCK_GETTIME)
|
|
||||||
static int steady_clk_index = 0;
|
#elif defined(HAVE_CLOCK_GETTIME) && (defined(CLOCK_HIGHRES) || defined(CLOCK_MONOTONIC))
|
||||||
static int monotonic_clk_index = 0;
|
|
||||||
int *clk_index;
|
|
||||||
clockid_t steady_clk_ids[] = {
|
|
||||||
#ifdef CLOCK_MONOTONIC_RAW
|
|
||||||
CLOCK_MONOTONIC_RAW,
|
|
||||||
#endif
|
|
||||||
CLOCK_MONOTONIC,
|
|
||||||
CLOCK_REALTIME
|
|
||||||
};
|
|
||||||
clockid_t monotonic_clk_ids[] = {
|
|
||||||
#ifdef CLOCK_MONOTONIC_RAW
|
|
||||||
CLOCK_MONOTONIC_RAW,
|
|
||||||
#endif
|
|
||||||
CLOCK_MONOTONIC
|
|
||||||
};
|
|
||||||
clockid_t *clk_ids;
|
|
||||||
int clk_ids_len;
|
|
||||||
int ret;
|
|
||||||
struct timespec tp;
|
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 (strict) {
|
if (clock_gettime(clk_id, &tp) != 0) {
|
||||||
clk_index = &monotonic_clk_index;
|
|
||||||
clk_ids = monotonic_clk_ids;
|
|
||||||
clk_ids_len = Py_ARRAY_LENGTH(monotonic_clk_ids);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
clk_index = &steady_clk_index;
|
|
||||||
clk_ids = steady_clk_ids;
|
|
||||||
clk_ids_len = Py_ARRAY_LENGTH(steady_clk_ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (clk_ids_len <= *clk_index)
|
|
||||||
(*clk_index) = -1;
|
|
||||||
}
|
|
||||||
if (strict) {
|
|
||||||
PyErr_SetFromErrno(PyExc_OSError);
|
PyErr_SetFromErrno(PyExc_OSError);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return floattime();
|
|
||||||
|
if (info) {
|
||||||
|
struct timespec res;
|
||||||
|
info->is_monotonic = 1;
|
||||||
|
info->implementation = function;
|
||||||
|
#if (defined(linux) || defined(__linux) || defined(__linux__)) \
|
||||||
|
&& !defined(CLOCK_HIGHRES)
|
||||||
|
/* CLOCK_MONOTONIC is adjusted on Linux */
|
||||||
|
info->is_adjusted = 1;
|
||||||
#else
|
#else
|
||||||
if (strict) {
|
info->is_adjusted = 0;
|
||||||
PyErr_SetString(PyExc_NotImplementedError,
|
#endif
|
||||||
"no steady clock available on your platform");
|
if (clock_getres(clk_id, &res) == 0)
|
||||||
return NULL;
|
info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
|
||||||
|
else
|
||||||
|
info->resolution = 1e-9;
|
||||||
}
|
}
|
||||||
return floattime();
|
return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
time_steady(PyObject *self, PyObject *args, PyObject *kwargs)
|
time_monotonic(PyObject *self, PyObject *unused)
|
||||||
{
|
{
|
||||||
static char *kwlist[] = {"strict", NULL};
|
return pymonotonic(NULL);
|
||||||
int strict = 0;
|
|
||||||
|
|
||||||
if (!PyArg_ParseTupleAndKeywords(
|
|
||||||
args, kwargs, "|i:steady", kwlist,
|
|
||||||
&strict))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return steady_clock(strict);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PyDoc_STRVAR(steady_doc,
|
PyDoc_STRVAR(monotonic_doc,
|
||||||
"steady(strict=False) -> float\n\
|
"monotonic() -> float\n\
|
||||||
\n\
|
\n\
|
||||||
Return the current time as a floating point number expressed in seconds.\n\
|
Monotonic clock, cannot go backward.");
|
||||||
This clock advances at a steady rate relative to real time and it may not\n\
|
#endif /* PYMONOTONIC */
|
||||||
be adjusted. The reference point of the returned value is undefined so only\n\
|
|
||||||
the difference of consecutive calls is valid.");
|
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
perf_counter(_Py_clock_info_t *info)
|
||||||
|
{
|
||||||
|
#if defined(WIN32_PERF_COUNTER) || defined(PYMONOTONIC)
|
||||||
|
PyObject *res;
|
||||||
|
#endif
|
||||||
|
#if defined(WIN32_PERF_COUNTER)
|
||||||
|
static int use_perf_counter = 1;
|
||||||
|
#endif
|
||||||
|
#ifdef PYMONOTONIC
|
||||||
|
static int use_monotonic = 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WIN32_PERF_COUNTER
|
||||||
|
if (use_perf_counter) {
|
||||||
|
if (win_perf_counter(info, &res) == 0)
|
||||||
|
return res;
|
||||||
|
use_perf_counter = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PYMONOTONIC
|
||||||
|
if (use_monotonic) {
|
||||||
|
res = pymonotonic(info);
|
||||||
|
if (res != NULL)
|
||||||
|
return res;
|
||||||
|
use_monotonic = 0;
|
||||||
|
PyErr_Clear();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return floattime(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
time_perf_counter(PyObject *self, PyObject *unused)
|
||||||
|
{
|
||||||
|
return perf_counter(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(perf_counter_doc,
|
||||||
|
"perf_counter() -> float\n\
|
||||||
|
\n\
|
||||||
|
Performance counter for benchmarking.");
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
py_process_time(_Py_clock_info_t *info)
|
||||||
|
{
|
||||||
|
#if defined(MS_WINDOWS)
|
||||||
|
HANDLE process;
|
||||||
|
FILETIME creation_time, exit_time, kernel_time, user_time;
|
||||||
|
ULARGE_INTEGER large;
|
||||||
|
double total;
|
||||||
|
BOOL ok;
|
||||||
|
|
||||||
|
process = GetCurrentProcess();
|
||||||
|
ok = GetProcessTimes(process, &creation_time, &exit_time, &kernel_time, &user_time);
|
||||||
|
if (!ok)
|
||||||
|
return PyErr_SetFromWindowsErr(0);
|
||||||
|
|
||||||
|
large.u.LowPart = kernel_time.dwLowDateTime;
|
||||||
|
large.u.HighPart = kernel_time.dwHighDateTime;
|
||||||
|
total = (double)large.QuadPart;
|
||||||
|
large.u.LowPart = user_time.dwLowDateTime;
|
||||||
|
large.u.HighPart = user_time.dwHighDateTime;
|
||||||
|
total += (double)large.QuadPart;
|
||||||
|
if (info) {
|
||||||
|
info->implementation = "GetProcessTimes()";
|
||||||
|
info->resolution = 1e-7;
|
||||||
|
info->is_monotonic = 1;
|
||||||
|
info->is_adjusted = 0;
|
||||||
|
}
|
||||||
|
return PyFloat_FromDouble(total * 1e-7);
|
||||||
|
#else
|
||||||
|
|
||||||
|
#if defined(HAVE_SYS_RESOURCE_H)
|
||||||
|
struct rusage ru;
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_TIMES
|
||||||
|
struct tms t;
|
||||||
|
static long ticks_per_second = -1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(HAVE_CLOCK_GETTIME) \
|
||||||
|
&& (defined(CLOCK_PROCESS_CPUTIME_ID) || defined(CLOCK_PROF))
|
||||||
|
struct timespec tp;
|
||||||
|
#ifdef CLOCK_PROF
|
||||||
|
const clockid_t clk_id = CLOCK_PROF;
|
||||||
|
const char *function = "clock_gettime(CLOCK_PROF)";
|
||||||
|
#else
|
||||||
|
const clockid_t clk_id = CLOCK_PROCESS_CPUTIME_ID;
|
||||||
|
const char *function = "clock_gettime(CLOCK_PROCESS_CPUTIME_ID)";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (clock_gettime(clk_id, &tp) == 0) {
|
||||||
|
if (info) {
|
||||||
|
struct timespec res;
|
||||||
|
info->implementation = function;
|
||||||
|
info->is_monotonic = 1;
|
||||||
|
info->is_adjusted = 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
|
||||||
|
|
||||||
|
#if defined(HAVE_SYS_RESOURCE_H)
|
||||||
|
if (getrusage(RUSAGE_SELF, &ru) == 0) {
|
||||||
|
double total;
|
||||||
|
total = ru.ru_utime.tv_sec + ru.ru_utime.tv_usec * 1e-6;
|
||||||
|
total += ru.ru_stime.tv_sec + ru.ru_stime.tv_usec * 1e-6;
|
||||||
|
if (info) {
|
||||||
|
info->implementation = "getrusage(RUSAGE_SELF)";
|
||||||
|
info->is_monotonic = 1;
|
||||||
|
info->is_adjusted = 0;
|
||||||
|
info->resolution = 1e-6;
|
||||||
|
}
|
||||||
|
return PyFloat_FromDouble(total);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_TIMES
|
||||||
|
if (times(&t) != (clock_t)-1) {
|
||||||
|
double total;
|
||||||
|
|
||||||
|
if (ticks_per_second == -1) {
|
||||||
|
#if defined(HAVE_SYSCONF) && defined(_SC_CLK_TCK)
|
||||||
|
ticks_per_second = sysconf(_SC_CLK_TCK);
|
||||||
|
if (ticks_per_second < 1)
|
||||||
|
ticks_per_second = -1;
|
||||||
|
#elif defined(HZ)
|
||||||
|
ticks_per_second = HZ;
|
||||||
|
#else
|
||||||
|
ticks_per_second = 60; /* magic fallback value; may be bogus */
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ticks_per_second != -1) {
|
||||||
|
total = (double)t.tms_utime / ticks_per_second;
|
||||||
|
total += (double)t.tms_stime / ticks_per_second;
|
||||||
|
if (info) {
|
||||||
|
info->implementation = "times()";
|
||||||
|
info->is_monotonic = 1;
|
||||||
|
info->is_adjusted = 0;
|
||||||
|
info->resolution = 1.0 / ticks_per_second;
|
||||||
|
}
|
||||||
|
return PyFloat_FromDouble(total);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return floatclock(info);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
time_process_time(PyObject *self, PyObject *unused)
|
||||||
|
{
|
||||||
|
return py_process_time(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(process_time_doc,
|
||||||
|
"process_time() -> float\n\
|
||||||
|
\n\
|
||||||
|
Process time for profiling: sum of the kernel and user-space CPU time.");
|
||||||
|
|
||||||
|
|
||||||
|
static PyTypeObject ClockInfoType;
|
||||||
|
|
||||||
|
PyDoc_STRVAR(ClockInfo_docstring,
|
||||||
|
"Clock information");
|
||||||
|
|
||||||
|
static PyStructSequence_Field ClockInfo_fields[] = {
|
||||||
|
{"implementation", "name of the underlying C function "
|
||||||
|
"used to get the clock value"},
|
||||||
|
{"is_monotonic", "True if the clock cannot go backward, False otherwise"},
|
||||||
|
{"is_adjusted", "True if the clock can be adjusted "
|
||||||
|
"(e.g. by a NTP daemon), False otherwise"},
|
||||||
|
{"resolution", "resolution of the clock in seconds"},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyStructSequence_Desc ClockInfo_desc = {
|
||||||
|
"time.clock_info",
|
||||||
|
ClockInfo_docstring,
|
||||||
|
ClockInfo_fields,
|
||||||
|
4,
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
time_get_clock_info(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
char *name;
|
||||||
|
PyObject *obj;
|
||||||
|
_Py_clock_info_t info;
|
||||||
|
PyObject *result;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "s:get_clock_info", &name))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
#ifdef Py_DEBUG
|
||||||
|
info.implementation = NULL;
|
||||||
|
info.is_monotonic = -1;
|
||||||
|
info.is_adjusted = -1;
|
||||||
|
info.resolution = -1.0;
|
||||||
|
#else
|
||||||
|
info.implementation = "";
|
||||||
|
info.is_monotonic = 0;
|
||||||
|
info.is_adjusted = 0;
|
||||||
|
info.resolution = 1.0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (strcmp(name, "time") == 0)
|
||||||
|
obj = floattime(&info);
|
||||||
|
#ifdef PYCLOCK
|
||||||
|
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)
|
||||||
|
obj = py_process_time(&info);
|
||||||
|
else {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "unknown clock");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (obj == NULL)
|
||||||
|
return NULL;
|
||||||
|
Py_DECREF(obj);
|
||||||
|
|
||||||
|
result = PyStructSequence_New(&ClockInfoType);
|
||||||
|
if (result == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
assert(info.implementation != NULL);
|
||||||
|
obj = PyUnicode_FromString(info.implementation);
|
||||||
|
if (obj == NULL)
|
||||||
|
goto error;
|
||||||
|
PyStructSequence_SET_ITEM(result, 0, obj);
|
||||||
|
|
||||||
|
assert(info.is_monotonic != -1);
|
||||||
|
obj = PyBool_FromLong(info.is_monotonic);
|
||||||
|
if (obj == NULL)
|
||||||
|
goto error;
|
||||||
|
PyStructSequence_SET_ITEM(result, 1, obj);
|
||||||
|
|
||||||
|
assert(info.is_adjusted != -1);
|
||||||
|
obj = PyBool_FromLong(info.is_adjusted);
|
||||||
|
if (obj == NULL)
|
||||||
|
goto error;
|
||||||
|
PyStructSequence_SET_ITEM(result, 2, obj);
|
||||||
|
|
||||||
|
assert(info.resolution > 0.0);
|
||||||
|
assert(info.resolution <= 1.0);
|
||||||
|
obj = PyFloat_FromDouble(info.resolution);
|
||||||
|
if (obj == NULL)
|
||||||
|
goto error;
|
||||||
|
PyStructSequence_SET_ITEM(result, 3, obj);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
error:
|
||||||
|
Py_DECREF(result);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(get_clock_info_doc,
|
||||||
|
"get_clock_info(name: str) -> dict\n\
|
||||||
|
\n\
|
||||||
|
Get information of the specified clock.");
|
||||||
|
|
||||||
static void
|
static void
|
||||||
PyInit_timezone(PyObject *m) {
|
PyInit_timezone(PyObject *m) {
|
||||||
|
@ -977,10 +1321,8 @@ PyInit_timezone(PyObject *m) {
|
||||||
#endif /* __CYGWIN__ */
|
#endif /* __CYGWIN__ */
|
||||||
#endif /* !HAVE_TZNAME || __GLIBC__ || __CYGWIN__*/
|
#endif /* !HAVE_TZNAME || __GLIBC__ || __CYGWIN__*/
|
||||||
|
|
||||||
#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_CLOCK_GETRES)
|
#if defined(HAVE_CLOCK_GETTIME)
|
||||||
#ifdef CLOCK_REALTIME
|
|
||||||
PyModule_AddIntMacro(m, CLOCK_REALTIME);
|
PyModule_AddIntMacro(m, CLOCK_REALTIME);
|
||||||
#endif
|
|
||||||
#ifdef CLOCK_MONOTONIC
|
#ifdef CLOCK_MONOTONIC
|
||||||
PyModule_AddIntMacro(m, CLOCK_MONOTONIC);
|
PyModule_AddIntMacro(m, CLOCK_MONOTONIC);
|
||||||
#endif
|
#endif
|
||||||
|
@ -1002,7 +1344,7 @@ PyInit_timezone(PyObject *m) {
|
||||||
|
|
||||||
static PyMethodDef time_methods[] = {
|
static PyMethodDef time_methods[] = {
|
||||||
{"time", time_time, METH_NOARGS, time_doc},
|
{"time", time_time, METH_NOARGS, time_doc},
|
||||||
#if (defined(MS_WINDOWS) && !defined(__BORLANDC__)) || defined(HAVE_CLOCK)
|
#ifdef PYCLOCK
|
||||||
{"clock", time_clock, METH_NOARGS, clock_doc},
|
{"clock", time_clock, METH_NOARGS, clock_doc},
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_CLOCK_GETTIME
|
#ifdef HAVE_CLOCK_GETTIME
|
||||||
|
@ -1018,8 +1360,6 @@ 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
|
||||||
{"steady", (PyCFunction)time_steady, METH_VARARGS|METH_KEYWORDS,
|
|
||||||
steady_doc},
|
|
||||||
#ifdef HAVE_STRFTIME
|
#ifdef HAVE_STRFTIME
|
||||||
{"strftime", time_strftime, METH_VARARGS, strftime_doc},
|
{"strftime", time_strftime, METH_VARARGS, strftime_doc},
|
||||||
#endif
|
#endif
|
||||||
|
@ -1027,6 +1367,12 @@ 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},
|
||||||
|
#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},
|
||||||
{NULL, NULL} /* sentinel */
|
{NULL, NULL} /* sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1104,6 +1450,20 @@ PyInit_time(void)
|
||||||
if (!initialized) {
|
if (!initialized) {
|
||||||
PyStructSequence_InitType(&StructTimeType,
|
PyStructSequence_InitType(&StructTimeType,
|
||||||
&struct_time_type_desc);
|
&struct_time_type_desc);
|
||||||
|
|
||||||
|
/* initialize ClockInfoType */
|
||||||
|
PyStructSequence_InitType(&ClockInfoType, &ClockInfo_desc);
|
||||||
|
Py_INCREF(&ClockInfoType);
|
||||||
|
PyModule_AddObject(m, "clock_info", (PyObject*)&ClockInfoType);
|
||||||
|
|
||||||
|
#ifdef MS_WINDOWS
|
||||||
|
winver.dwOSVersionInfoSize = sizeof(winver);
|
||||||
|
if (!GetVersionEx((OSVERSIONINFO*)&winver)) {
|
||||||
|
Py_DECREF(m);
|
||||||
|
PyErr_SetFromWindowsErr(0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
Py_INCREF(&StructTimeType);
|
Py_INCREF(&StructTimeType);
|
||||||
PyModule_AddObject(m, "struct_time", (PyObject*) &StructTimeType);
|
PyModule_AddObject(m, "struct_time", (PyObject*) &StructTimeType);
|
||||||
|
@ -1112,7 +1472,7 @@ PyInit_time(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
floattime(void)
|
floattime(_Py_clock_info_t *info)
|
||||||
{
|
{
|
||||||
_PyTime_timeval t;
|
_PyTime_timeval t;
|
||||||
#ifdef HAVE_CLOCK_GETTIME
|
#ifdef HAVE_CLOCK_GETTIME
|
||||||
|
@ -1123,10 +1483,21 @@ floattime(void)
|
||||||
because it would require to link Python to the rt (real-time)
|
because it would require to link Python to the rt (real-time)
|
||||||
library, at least on Linux */
|
library, at least on Linux */
|
||||||
ret = clock_gettime(CLOCK_REALTIME, &tp);
|
ret = clock_gettime(CLOCK_REALTIME, &tp);
|
||||||
if (ret == 0)
|
if (ret == 0) {
|
||||||
|
if (info) {
|
||||||
|
struct timespec res;
|
||||||
|
info->implementation = "clock_gettime(CLOCK_REALTIME)";
|
||||||
|
info->is_monotonic = 0;
|
||||||
|
info->is_adjusted = 1;
|
||||||
|
if (clock_getres(CLOCK_REALTIME, &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);
|
return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
_PyTime_gettimeofday(&t);
|
_PyTime_gettimeofday_info(&t, info);
|
||||||
return PyFloat_FromDouble((double)t.tv_sec + t.tv_usec * 1e-6);
|
return PyFloat_FromDouble((double)t.tv_sec + t.tv_usec * 1e-6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
extern int ftime(struct timeb *);
|
extern int ftime(struct timeb *);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void
|
static void
|
||||||
_PyTime_gettimeofday(_PyTime_timeval *tp)
|
pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info)
|
||||||
{
|
{
|
||||||
#ifdef MS_WINDOWS
|
#ifdef MS_WINDOWS
|
||||||
FILETIME system_time;
|
FILETIME system_time;
|
||||||
|
@ -35,6 +35,20 @@ _PyTime_gettimeofday(_PyTime_timeval *tp)
|
||||||
microseconds = large.QuadPart / 10 - 11644473600000000;
|
microseconds = large.QuadPart / 10 - 11644473600000000;
|
||||||
tp->tv_sec = microseconds / 1000000;
|
tp->tv_sec = microseconds / 1000000;
|
||||||
tp->tv_usec = microseconds % 1000000;
|
tp->tv_usec = microseconds % 1000000;
|
||||||
|
if (info) {
|
||||||
|
DWORD timeAdjustment, timeIncrement;
|
||||||
|
BOOL isTimeAdjustmentDisabled;
|
||||||
|
|
||||||
|
info->implementation = "GetSystemTimeAsFileTime()";
|
||||||
|
info->is_monotonic = 0;
|
||||||
|
(void) GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement,
|
||||||
|
&isTimeAdjustmentDisabled);
|
||||||
|
info->resolution = timeIncrement * 1e-7;
|
||||||
|
if (isTimeAdjustmentDisabled)
|
||||||
|
info->is_adjusted = 0;
|
||||||
|
else
|
||||||
|
info->is_adjusted = 1;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
/* There are three ways to get the time:
|
/* There are three ways to get the time:
|
||||||
(1) gettimeofday() -- resolution in microseconds
|
(1) gettimeofday() -- resolution in microseconds
|
||||||
|
@ -46,14 +60,22 @@ _PyTime_gettimeofday(_PyTime_timeval *tp)
|
||||||
Note: clock resolution does not imply clock accuracy! */
|
Note: clock resolution does not imply clock accuracy! */
|
||||||
|
|
||||||
#ifdef HAVE_GETTIMEOFDAY
|
#ifdef HAVE_GETTIMEOFDAY
|
||||||
|
int err;
|
||||||
#ifdef GETTIMEOFDAY_NO_TZ
|
#ifdef GETTIMEOFDAY_NO_TZ
|
||||||
if (gettimeofday(tp) == 0)
|
err = gettimeofday(tp);
|
||||||
|
#else
|
||||||
|
err = gettimeofday(tp, (struct timezone *)NULL);
|
||||||
|
#endif
|
||||||
|
if (err == 0) {
|
||||||
|
if (info) {
|
||||||
|
info->implementation = "gettimeofday()";
|
||||||
|
info->resolution = 1e-6;
|
||||||
|
info->is_monotonic = 0;
|
||||||
|
info->is_adjusted = 1;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
#else /* !GETTIMEOFDAY_NO_TZ */
|
}
|
||||||
if (gettimeofday(tp, (struct timezone *)NULL) == 0)
|
#endif /* HAVE_GETTIMEOFDAY */
|
||||||
return;
|
|
||||||
#endif /* !GETTIMEOFDAY_NO_TZ */
|
|
||||||
#endif /* !HAVE_GETTIMEOFDAY */
|
|
||||||
|
|
||||||
#if defined(HAVE_FTIME)
|
#if defined(HAVE_FTIME)
|
||||||
{
|
{
|
||||||
|
@ -61,15 +83,39 @@ _PyTime_gettimeofday(_PyTime_timeval *tp)
|
||||||
ftime(&t);
|
ftime(&t);
|
||||||
tp->tv_sec = t.time;
|
tp->tv_sec = t.time;
|
||||||
tp->tv_usec = t.millitm * 1000;
|
tp->tv_usec = t.millitm * 1000;
|
||||||
|
if (info) {
|
||||||
|
info->implementation = "ftime()";
|
||||||
|
info->resolution = 1e-3;
|
||||||
|
info->is_monotonic = 0;
|
||||||
|
info->is_adjusted = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#else /* !HAVE_FTIME */
|
#else /* !HAVE_FTIME */
|
||||||
tp->tv_sec = time(NULL);
|
tp->tv_sec = time(NULL);
|
||||||
tp->tv_usec = 0;
|
tp->tv_usec = 0;
|
||||||
|
if (info) {
|
||||||
|
info->implementation = "time()";
|
||||||
|
info->resolution = 1.0;
|
||||||
|
info->is_monotonic = 0;
|
||||||
|
info->is_adjusted = 1;
|
||||||
|
}
|
||||||
#endif /* !HAVE_FTIME */
|
#endif /* !HAVE_FTIME */
|
||||||
|
|
||||||
#endif /* MS_WINDOWS */
|
#endif /* MS_WINDOWS */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_PyTime_gettimeofday(_PyTime_timeval *tp)
|
||||||
|
{
|
||||||
|
pygettimeofday(tp, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_PyTime_gettimeofday_info(_PyTime_timeval *tp, _Py_clock_info_t *info)
|
||||||
|
{
|
||||||
|
pygettimeofday(tp, info);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
error_time_t_overflow(void)
|
error_time_t_overflow(void)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue