diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2017-11-02 14:28:27 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-11-02 14:28:27 (GMT) |
commit | c29b585fd4b5a91d17fc5dd41d86edff28a30da3 (patch) | |
tree | aadcf238ccc1867d7adefc079081781424e3e62c /Modules/timemodule.c | |
parent | e314853d57450b2b9523157eebd405289a795a0e (diff) | |
download | cpython-c29b585fd4b5a91d17fc5dd41d86edff28a30da3.zip cpython-c29b585fd4b5a91d17fc5dd41d86edff28a30da3.tar.gz cpython-c29b585fd4b5a91d17fc5dd41d86edff28a30da3.tar.bz2 |
bpo-31784: Implement PEP 564: add time.time_ns() (#3989)
Add new time functions:
* time.clock_gettime_ns()
* time.clock_settime_ns()
* time.monotonic_ns()
* time.perf_counter_ns()
* time.process_time_ns()
* time.time_ns()
Add new _PyTime functions:
* _PyTime_FromTimespec()
* _PyTime_FromNanosecondsObject()
* _PyTime_FromTimeval()
Other changes:
* Add also os.times() tests to test_os.
* pytime_fromtimeval() and pytime_fromtimeval() now return
_PyTime_MAX or _PyTime_MIN on overflow, rather than undefined
behaviour
* _PyTime_FromNanoseconds() parameter type changes from long long to
_PyTime_t
Diffstat (limited to 'Modules/timemodule.c')
-rw-r--r-- | Modules/timemodule.c | 426 |
1 files changed, 309 insertions, 117 deletions
diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 347c828..37abeb9 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -34,57 +34,90 @@ #endif /* MS_WINDOWS */ #endif /* !__WATCOMC__ || __QNX__ */ +#define SEC_TO_NS (1000 * 1000 * 1000) + /* Forward declarations */ static int pysleep(_PyTime_t); -static PyObject* floattime(_Py_clock_info_t *info); + + +static PyObject* +_PyFloat_FromPyTime(_PyTime_t t) +{ + double d = _PyTime_AsSecondsDouble(t); + return PyFloat_FromDouble(d); +} + static PyObject * time_time(PyObject *self, PyObject *unused) { - return floattime(NULL); + _PyTime_t t = _PyTime_GetSystemClock(); + return _PyFloat_FromPyTime(t); } + PyDoc_STRVAR(time_doc, "time() -> floating point number\n\ \n\ Return the current time in seconds since the Epoch.\n\ Fractions of a second may be present if the system clock provides them."); +static PyObject * +time_time_ns(PyObject *self, PyObject *unused) +{ + _PyTime_t t = _PyTime_GetSystemClock(); + return _PyTime_AsNanosecondsObject(t); +} + +PyDoc_STRVAR(time_ns_doc, +"time_ns() -> int\n\ +\n\ +Return the current time in nanoseconds since the Epoch."); + #if defined(HAVE_CLOCK) #ifndef CLOCKS_PER_SEC -#ifdef CLK_TCK -#define CLOCKS_PER_SEC CLK_TCK -#else -#define CLOCKS_PER_SEC 1000000 -#endif +# ifdef CLK_TCK +# define CLOCKS_PER_SEC CLK_TCK +# else +# define CLOCKS_PER_SEC 1000000 +# endif #endif -static PyObject* -_PyFloat_FromPyTime(_PyTime_t t) +static int +_PyTime_GetClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info) { - double d = _PyTime_AsSecondsDouble(t); - return PyFloat_FromDouble(d); -} + static int initialized = 0; + clock_t ticks; -static PyObject * -floatclock(_Py_clock_info_t *info) -{ - clock_t value; - value = clock(); - if (value == (clock_t)-1) { - PyErr_SetString(PyExc_RuntimeError, - "the processor time used is not available " - "or its value cannot be represented"); - return NULL; + if (!initialized) { + initialized = 1; + + /* must sure that _PyTime_MulDiv(ticks, SEC_TO_NS, CLOCKS_PER_SEC) + above cannot overflow */ + if ((_PyTime_t)CLOCKS_PER_SEC > _PyTime_MAX / SEC_TO_NS) { + PyErr_SetString(PyExc_OverflowError, + "CLOCKS_PER_SEC is too large"); + return -1; + } } + if (info) { info->implementation = "clock()"; info->resolution = 1.0 / (double)CLOCKS_PER_SEC; info->monotonic = 1; info->adjustable = 0; } - return PyFloat_FromDouble((double)value / CLOCKS_PER_SEC); + + ticks = clock(); + if (ticks == (clock_t)-1) { + PyErr_SetString(PyExc_RuntimeError, + "the processor time used is not available " + "or its value cannot be represented"); + return -1; + } + *tp = _PyTime_MulDiv(ticks, SEC_TO_NS, (_PyTime_t)CLOCKS_PER_SEC); + return 0; } #endif /* HAVE_CLOCK */ @@ -95,8 +128,7 @@ perf_counter(_Py_clock_info_t *info) if (_PyTime_GetPerfCounterWithInfo(&t, info) < 0) { return NULL; } - double d = _PyTime_AsSecondsDouble(t); - return PyFloat_FromDouble(d); + return _PyFloat_FromPyTime(t); } #if defined(MS_WINDOWS) || defined(HAVE_CLOCK) @@ -111,10 +143,15 @@ pyclock(_Py_clock_info_t *info) "instead", 1) < 0) { return NULL; } + #ifdef MS_WINDOWS return perf_counter(info); #else - return floatclock(info); + _PyTime_t t; + if (_PyTime_GetClockWithInfo(&t, info) < 0) { + return NULL; + } + return _PyFloat_FromPyTime(t); #endif } @@ -140,8 +177,9 @@ time_clock_gettime(PyObject *self, PyObject *args) int clk_id; struct timespec tp; - if (!PyArg_ParseTuple(args, "i:clock_gettime", &clk_id)) + if (!PyArg_ParseTuple(args, "i:clock_gettime", &clk_id)) { return NULL; + } ret = clock_gettime((clockid_t)clk_id, &tp); if (ret != 0) { @@ -152,9 +190,37 @@ time_clock_gettime(PyObject *self, PyObject *args) } PyDoc_STRVAR(clock_gettime_doc, -"clock_gettime(clk_id) -> floating point number\n\ +"clock_gettime(clk_id) -> float\n\ \n\ Return the time of the specified clock clk_id."); + +static PyObject * +time_clock_gettime_ns(PyObject *self, PyObject *args) +{ + int ret; + int clk_id; + struct timespec ts; + _PyTime_t t; + + if (!PyArg_ParseTuple(args, "i:clock_gettime", &clk_id)) { + return NULL; + } + + ret = clock_gettime((clockid_t)clk_id, &ts); + if (ret != 0) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + if (_PyTime_FromTimespec(&t, &ts) < 0) { + return NULL; + } + return _PyTime_AsNanosecondsObject(t); +} + +PyDoc_STRVAR(clock_gettime_ns_doc, +"clock_gettime_ns(clk_id) -> int\n\ +\n\ +Return the time of the specified clock clk_id as nanoseconds."); #endif /* HAVE_CLOCK_GETTIME */ #ifdef HAVE_CLOCK_SETTIME @@ -188,6 +254,39 @@ PyDoc_STRVAR(clock_settime_doc, "clock_settime(clk_id, time)\n\ \n\ Set the time of the specified clock clk_id."); + +static PyObject * +time_clock_settime_ns(PyObject *self, PyObject *args) +{ + int clk_id; + PyObject *obj; + _PyTime_t t; + struct timespec ts; + int ret; + + if (!PyArg_ParseTuple(args, "iO:clock_settime", &clk_id, &obj)) { + return NULL; + } + + if (_PyTime_FromNanosecondsObject(&t, obj) < 0) { + return NULL; + } + if (_PyTime_AsTimespec(t, &ts) == -1) { + return NULL; + } + + ret = clock_settime((clockid_t)clk_id, &ts); + if (ret != 0) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + Py_RETURN_NONE; +} + +PyDoc_STRVAR(clock_settime_ns_doc, +"clock_settime_ns(clk_id, time)\n\ +\n\ +Set the time of the specified clock clk_id with nanoseconds."); #endif /* HAVE_CLOCK_SETTIME */ #ifdef HAVE_CLOCK_GETRES @@ -927,26 +1026,28 @@ should not be relied on."); #endif /* HAVE_WORKING_TZSET */ static PyObject * -pymonotonic(_Py_clock_info_t *info) +time_monotonic(PyObject *self, PyObject *unused) { - _PyTime_t t; - if (_PyTime_GetMonotonicClockWithInfo(&t, info) < 0) { - assert(info != NULL); - return NULL; - } + _PyTime_t t = _PyTime_GetMonotonicClock(); return _PyFloat_FromPyTime(t); } +PyDoc_STRVAR(monotonic_doc, +"monotonic() -> float\n\ +\n\ +Monotonic clock, cannot go backward."); + static PyObject * -time_monotonic(PyObject *self, PyObject *unused) +time_monotonic_ns(PyObject *self, PyObject *unused) { - return pymonotonic(NULL); + _PyTime_t t = _PyTime_GetMonotonicClock(); + return _PyTime_AsNanosecondsObject(t); } -PyDoc_STRVAR(monotonic_doc, -"monotonic() -> float\n\ +PyDoc_STRVAR(monotonic_ns_doc, +"monotonic_ns() -> int\n\ \n\ -Monotonic clock, cannot go backward."); +Monotonic clock, cannot go backward, as nanoseconds."); static PyObject * time_perf_counter(PyObject *self, PyObject *unused) @@ -959,47 +1060,61 @@ PyDoc_STRVAR(perf_counter_doc, \n\ Performance counter for benchmarking."); -static PyObject* -py_process_time(_Py_clock_info_t *info) +static PyObject * +time_perf_counter_ns(PyObject *self, PyObject *unused) +{ + _PyTime_t t = _PyTime_GetPerfCounter(); + return _PyTime_AsNanosecondsObject(t); +} + +PyDoc_STRVAR(perf_counter_ns_doc, +"perf_counter_ns() -> int\n\ +\n\ +Performance counter for benchmarking as nanoseconds."); + +static int +_PyTime_GetProcessTimeWithInfo(_PyTime_t *tp, _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; + _PyTime_t ktime, utime, t; BOOL ok; process = GetCurrentProcess(); - ok = GetProcessTimes(process, &creation_time, &exit_time, &kernel_time, &user_time); - if (!ok) - return PyErr_SetFromWindowsErr(0); + ok = GetProcessTimes(process, &creation_time, &exit_time, + &kernel_time, &user_time); + if (!ok) { + PyErr_SetFromWindowsErr(0); + return -1; + } - 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->monotonic = 1; info->adjustable = 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 + large.u.LowPart = kernel_time.dwLowDateTime; + large.u.HighPart = kernel_time.dwHighDateTime; + ktime = large.QuadPart; + + large.u.LowPart = user_time.dwLowDateTime; + large.u.HighPart = user_time.dwHighDateTime; + utime = large.QuadPart; + + /* ktime and utime have a resolution of 100 nanoseconds */ + t = _PyTime_FromNanoseconds((ktime + utime) * 100); + *tp = t; + return 0; +#else + /* clock_gettime */ #if defined(HAVE_CLOCK_GETTIME) \ && (defined(CLOCK_PROCESS_CPUTIME_ID) || defined(CLOCK_PROF)) - struct timespec tp; + struct timespec ts; #ifdef CLOCK_PROF const clockid_t clk_id = CLOCK_PROF; const char *function = "clock_gettime(CLOCK_PROF)"; @@ -1008,75 +1123,117 @@ py_process_time(_Py_clock_info_t *info) const char *function = "clock_gettime(CLOCK_PROCESS_CPUTIME_ID)"; #endif - if (clock_gettime(clk_id, &tp) == 0) { + if (clock_gettime(clk_id, &ts) == 0) { if (info) { struct timespec res; info->implementation = function; info->monotonic = 1; 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; + if (clock_getres(clk_id, &res)) { + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + info->resolution = res.tv_sec + res.tv_nsec * 1e-9; } - return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9); + + if (_PyTime_FromTimespec(tp, &ts) < 0) { + return -1; + } + return 0; } #endif + /* getrusage(RUSAGE_SELF) */ #if defined(HAVE_SYS_RESOURCE_H) + struct rusage ru; + 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; + _PyTime_t utime, stime; + if (info) { info->implementation = "getrusage(RUSAGE_SELF)"; info->monotonic = 1; info->adjustable = 0; info->resolution = 1e-6; } - return PyFloat_FromDouble(total); + + if (_PyTime_FromTimeval(&utime, &ru.ru_utime) < 0) { + return -1; + } + if (_PyTime_FromTimeval(&stime, &ru.ru_stime) < 0) { + return -1; + } + + _PyTime_t total = utime + utime; + *tp = total; + return 0; } #endif + /* times() */ #ifdef HAVE_TIMES + struct tms t; + if (times(&t) != (clock_t)-1) { - double total; + static long ticks_per_second = -1; if (ticks_per_second == -1) { + long freq; #if defined(HAVE_SYSCONF) && defined(_SC_CLK_TCK) - ticks_per_second = sysconf(_SC_CLK_TCK); - if (ticks_per_second < 1) - ticks_per_second = -1; + freq = sysconf(_SC_CLK_TCK); + if (freq < 1) { + freq = -1; + } #elif defined(HZ) - ticks_per_second = HZ; + freq = HZ; #else - ticks_per_second = 60; /* magic fallback value; may be bogus */ + freq = 60; /* magic fallback value; may be bogus */ #endif + + if (freq != -1) { + /* check that _PyTime_MulDiv(t, SEC_TO_NS, ticks_per_second) + cannot overflow below */ + if ((_PyTime_t)freq > _PyTime_MAX / SEC_TO_NS) { + PyErr_SetString(PyExc_OverflowError, + "_SC_CLK_TCK is too large"); + return -1; + } + + ticks_per_second = freq; + } } 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->monotonic = 1; info->adjustable = 0; - info->resolution = 1.0 / ticks_per_second; + info->resolution = 1.0 / (double)ticks_per_second; } - return PyFloat_FromDouble(total); + + _PyTime_t total; + total = _PyTime_MulDiv(t.tms_utime, SEC_TO_NS, ticks_per_second); + total += _PyTime_MulDiv(t.tms_stime, SEC_TO_NS, ticks_per_second); + *tp = total; + return 0; } } #endif + /* clock */ /* Currently, Python 3 requires clock() to build: see issue #22624 */ - return floatclock(info); + return _PyTime_GetClockWithInfo(tp, info); #endif } static PyObject * time_process_time(PyObject *self, PyObject *unused) { - return py_process_time(NULL); + _PyTime_t t; + if (_PyTime_GetProcessTimeWithInfo(&t, NULL) < 0) { + return NULL; + } + return _PyFloat_FromPyTime(t); } PyDoc_STRVAR(process_time_doc, @@ -1084,6 +1241,22 @@ PyDoc_STRVAR(process_time_doc, \n\ Process time for profiling: sum of the kernel and user-space CPU time."); +static PyObject * +time_process_time_ns(PyObject *self, PyObject *unused) +{ + _PyTime_t t; + if (_PyTime_GetProcessTimeWithInfo(&t, NULL) < 0) { + return NULL; + } + return _PyTime_AsNanosecondsObject(t); +} + +PyDoc_STRVAR(process_time_ns_doc, +"process_time() -> int\n\ +\n\ +Process time for profiling as nanoseconds:\n\ +sum of the kernel and user-space CPU time."); + static PyObject * time_get_clock_info(PyObject *self, PyObject *args) @@ -1091,9 +1264,11 @@ time_get_clock_info(PyObject *self, PyObject *args) char *name; _Py_clock_info_t info; PyObject *obj = NULL, *dict, *ns; + _PyTime_t t; - if (!PyArg_ParseTuple(args, "s:get_clock_info", &name)) + if (!PyArg_ParseTuple(args, "s:get_clock_info", &name)) { return NULL; + } #ifdef Py_DEBUG info.implementation = NULL; @@ -1107,61 +1282,84 @@ time_get_clock_info(PyObject *self, PyObject *args) info.resolution = 1.0; #endif - if (strcmp(name, "time") == 0) - obj = floattime(&info); + if (strcmp(name, "time") == 0) { + if (_PyTime_GetSystemClockWithInfo(&t, &info) < 0) { + return NULL; + } + } #ifdef PYCLOCK - else if (strcmp(name, "clock") == 0) + else if (strcmp(name, "clock") == 0) { obj = pyclock(&info); + if (obj == NULL) { + return NULL; + } + Py_DECREF(obj); + } #endif - else if (strcmp(name, "monotonic") == 0) - obj = pymonotonic(&info); - else if (strcmp(name, "perf_counter") == 0) - obj = perf_counter(&info); - else if (strcmp(name, "process_time") == 0) - obj = py_process_time(&info); + else if (strcmp(name, "monotonic") == 0) { + if (_PyTime_GetMonotonicClockWithInfo(&t, &info) < 0) { + return NULL; + } + } + else if (strcmp(name, "perf_counter") == 0) { + if (_PyTime_GetPerfCounterWithInfo(&t, &info) < 0) { + return NULL; + } + } + else if (strcmp(name, "process_time") == 0) { + if (_PyTime_GetProcessTimeWithInfo(&t, &info) < 0) { + return NULL; + } + } else { PyErr_SetString(PyExc_ValueError, "unknown clock"); return NULL; } - if (obj == NULL) - return NULL; - Py_DECREF(obj); dict = PyDict_New(); - if (dict == NULL) + if (dict == NULL) { return NULL; + } assert(info.implementation != NULL); obj = PyUnicode_FromString(info.implementation); - if (obj == NULL) + if (obj == NULL) { goto error; - if (PyDict_SetItemString(dict, "implementation", obj) == -1) + } + if (PyDict_SetItemString(dict, "implementation", obj) == -1) { goto error; + } Py_CLEAR(obj); assert(info.monotonic != -1); obj = PyBool_FromLong(info.monotonic); - if (obj == NULL) + if (obj == NULL) { goto error; - if (PyDict_SetItemString(dict, "monotonic", obj) == -1) + } + if (PyDict_SetItemString(dict, "monotonic", obj) == -1) { goto error; + } Py_CLEAR(obj); assert(info.adjustable != -1); obj = PyBool_FromLong(info.adjustable); - if (obj == NULL) + if (obj == NULL) { goto error; - if (PyDict_SetItemString(dict, "adjustable", obj) == -1) + } + if (PyDict_SetItemString(dict, "adjustable", obj) == -1) { goto error; + } Py_CLEAR(obj); assert(info.resolution > 0.0); assert(info.resolution <= 1.0); obj = PyFloat_FromDouble(info.resolution); - if (obj == NULL) + if (obj == NULL) { goto error; - if (PyDict_SetItemString(dict, "resolution", obj) == -1) + } + if (PyDict_SetItemString(dict, "resolution", obj) == -1) { goto error; + } Py_CLEAR(obj); ns = _PyNamespace_New(dict); @@ -1284,14 +1482,17 @@ PyInit_timezone(PyObject *m) { static PyMethodDef time_methods[] = { {"time", time_time, METH_NOARGS, time_doc}, + {"time_ns", time_time_ns, METH_NOARGS, time_ns_doc}, #ifdef PYCLOCK {"clock", time_clock, METH_NOARGS, clock_doc}, #endif #ifdef HAVE_CLOCK_GETTIME {"clock_gettime", time_clock_gettime, METH_VARARGS, clock_gettime_doc}, + {"clock_gettime_ns",time_clock_gettime_ns, METH_VARARGS, clock_gettime_ns_doc}, #endif #ifdef HAVE_CLOCK_SETTIME {"clock_settime", time_clock_settime, METH_VARARGS, clock_settime_doc}, + {"clock_settime_ns",time_clock_settime_ns, METH_VARARGS, clock_settime_ns_doc}, #endif #ifdef HAVE_CLOCK_GETRES {"clock_getres", time_clock_getres, METH_VARARGS, clock_getres_doc}, @@ -1315,8 +1516,11 @@ static PyMethodDef time_methods[] = { {"tzset", time_tzset, METH_NOARGS, tzset_doc}, #endif {"monotonic", time_monotonic, METH_NOARGS, monotonic_doc}, + {"monotonic_ns", time_monotonic_ns, METH_NOARGS, monotonic_ns_doc}, {"process_time", time_process_time, METH_NOARGS, process_time_doc}, + {"process_time_ns", time_process_time_ns, METH_NOARGS, process_time_ns_doc}, {"perf_counter", time_perf_counter, METH_NOARGS, perf_counter_doc}, + {"perf_counter_ns", time_perf_counter_ns, METH_NOARGS, perf_counter_ns_doc}, {"get_clock_info", time_get_clock_info, METH_VARARGS, get_clock_info_doc}, {NULL, NULL} /* sentinel */ }; @@ -1411,18 +1615,6 @@ PyInit_time(void) return m; } -static PyObject* -floattime(_Py_clock_info_t *info) -{ - _PyTime_t t; - if (_PyTime_GetSystemClockWithInfo(&t, info) < 0) { - assert(info != NULL); - return NULL; - } - return _PyFloat_FromPyTime(t); -} - - /* Implement pysleep() for various platforms. When interrupted (or when another error occurs), return -1 and set an exception; else return 0. */ |