From e14679c78464d1e0e16786c2a0e9bcebe49e842b Mon Sep 17 00:00:00 2001 From: pdox Date: Thu, 5 Oct 2017 00:01:56 -0700 Subject: [PATCH] closes bpo-31596: Add an interface for pthread_getcpuclockid(3) (#3756) --- Doc/library/time.rst | 16 ++++++++++ Lib/test/test_time.py | 20 ++++++++++++ .../2017-09-26-11-38-52.bpo-31596.50Eyel.rst | 1 + Modules/timemodule.c | 32 +++++++++++++++++++ configure | 11 +++++++ configure.ac | 1 + pyconfig.h.in | 3 ++ 7 files changed, 84 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2017-09-26-11-38-52.bpo-31596.50Eyel.rst diff --git a/Doc/library/time.rst b/Doc/library/time.rst index ef0e69a5e8c1f8..179762ed1c898b 100644 --- a/Doc/library/time.rst +++ b/Doc/library/time.rst @@ -162,6 +162,22 @@ The module defines the following functions and data items: :func:`perf_counter` or :func:`process_time` instead, depending on your requirements, to have a well defined behaviour. +.. function:: pthread_getcpuclockid(thread_id) + + Return the *clk_id* of the thread-specific CPU-time clock for the specified *thread_id*. + + Use :func:`threading.get_ident` or the :attr:`~threading.Thread.ident` + attribute of :class:`threading.Thread` objects to get a suitable value + for *thread_id*. + + .. warning:: + Passing an invalid or expired *thread_id* may result in + undefined behavior, such as segmentation fault. + + Availability: Unix (see the man page for :manpage:`pthread_getcpuclockid(3)` for + further information) + + .. versionadded:: 3.7 .. function:: clock_getres(clk_id) diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py index b2aedc3be508b3..1456748ed8ea4e 100644 --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -7,6 +7,7 @@ import sys import sysconfig import time +import threading import unittest try: import _testcapi @@ -80,6 +81,25 @@ def test_clock_monotonic(self): b = time.clock_gettime(time.CLOCK_MONOTONIC) self.assertLessEqual(a, b) + @unittest.skipUnless(hasattr(time, 'pthread_getcpuclockid'), + 'need time.pthread_getcpuclockid()') + @unittest.skipUnless(hasattr(time, 'clock_gettime'), + 'need time.clock_gettime()') + @unittest.skipUnless(hasattr(time, 'CLOCK_THREAD_CPUTIME_ID'), + 'need time.CLOCK_THREAD_CPUTIME_ID') + def test_pthread_getcpuclockid(self): + clk_id = time.pthread_getcpuclockid(threading.get_ident()) + self.assertTrue(type(clk_id) is int) + self.assertNotEqual(clk_id, time.CLOCK_THREAD_CPUTIME_ID) + # This should suffice to show that both calls are measuring the same clock. + t1 = time.clock_gettime(clk_id) + t2 = time.clock_gettime(time.CLOCK_THREAD_CPUTIME_ID) + t3 = time.clock_gettime(clk_id) + t4 = time.clock_gettime(time.CLOCK_THREAD_CPUTIME_ID) + self.assertLessEqual(t1, t2) + self.assertLessEqual(t2, t3) + self.assertLessEqual(t3, t4) + @unittest.skipUnless(hasattr(time, 'clock_getres'), 'need time.clock_getres()') def test_clock_getres(self): diff --git a/Misc/NEWS.d/next/Library/2017-09-26-11-38-52.bpo-31596.50Eyel.rst b/Misc/NEWS.d/next/Library/2017-09-26-11-38-52.bpo-31596.50Eyel.rst new file mode 100644 index 00000000000000..85b1e1a8cadee1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2017-09-26-11-38-52.bpo-31596.50Eyel.rst @@ -0,0 +1 @@ +Added pthread_getcpuclockid() to the time module diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 36a95bbcedd6fe..34e057d9d74fd0 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -20,6 +20,10 @@ #include #endif +#if defined(HAVE_PTHREAD_H) +# include +#endif + #if defined(__WATCOMC__) && !defined(__QNX__) #include #else @@ -221,6 +225,31 @@ PyDoc_STRVAR(clock_getres_doc, Return the resolution (precision) of the specified clock clk_id."); #endif /* HAVE_CLOCK_GETRES */ +#ifdef HAVE_PTHREAD_GETCPUCLOCKID +static PyObject * +time_pthread_getcpuclockid(PyObject *self, PyObject *args) +{ + unsigned long thread_id; + int err; + clockid_t clk_id; + if (!PyArg_ParseTuple(args, "k:pthread_getcpuclockid", &thread_id)) { + return NULL; + } + err = pthread_getcpuclockid((pthread_t)thread_id, &clk_id); + if (err) { + errno = err; + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + return PyLong_FromLong(clk_id); +} + +PyDoc_STRVAR(pthread_getcpuclockid_doc, +"pthread_getcpuclockid(thread_id) -> int\n\ +\n\ +Return the clk_id of a thread's CPU time clock."); +#endif /* HAVE_PTHREAD_GETCPUCLOCKID */ + static PyObject * time_sleep(PyObject *self, PyObject *obj) { @@ -1287,6 +1316,9 @@ static PyMethodDef time_methods[] = { #endif #ifdef HAVE_CLOCK_GETRES {"clock_getres", time_clock_getres, METH_VARARGS, clock_getres_doc}, +#endif +#ifdef HAVE_PTHREAD_GETCPUCLOCKID + {"pthread_getcpuclockid", time_pthread_getcpuclockid, METH_VARARGS, pthread_getcpuclockid_doc}, #endif {"sleep", time_sleep, METH_O, sleep_doc}, {"gmtime", time_gmtime, METH_VARARGS, gmtime_doc}, diff --git a/configure b/configure index 8a763ef6e495ff..2d1bed22476c59 100755 --- a/configure +++ b/configure @@ -10480,6 +10480,17 @@ if test "x$ac_cv_func_pthread_atfork" = xyes; then : #define HAVE_PTHREAD_ATFORK 1 _ACEOF +fi +done + + for ac_func in pthread_getcpuclockid +do : + ac_fn_c_check_func "$LINENO" "pthread_getcpuclockid" "ac_cv_func_pthread_getcpuclockid" +if test "x$ac_cv_func_pthread_getcpuclockid" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_PTHREAD_GETCPUCLOCKID 1 +_ACEOF + fi done diff --git a/configure.ac b/configure.ac index 41bd9effbfd295..b562fe405a4fef 100644 --- a/configure.ac +++ b/configure.ac @@ -3025,6 +3025,7 @@ if test "$posix_threads" = "yes"; then ;; esac]) AC_CHECK_FUNCS(pthread_atfork) + AC_CHECK_FUNCS(pthread_getcpuclockid) fi diff --git a/pyconfig.h.in b/pyconfig.h.in index d4feabeece3088..637341afee3c1e 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -694,6 +694,9 @@ /* Defined for Solaris 2.6 bug in pthread header. */ #undef HAVE_PTHREAD_DESTRUCTOR +/* Define to 1 if you have the `pthread_getcpuclockid' function. */ +#undef HAVE_PTHREAD_GETCPUCLOCKID + /* Define to 1 if you have the header file. */ #undef HAVE_PTHREAD_H