From 754851f456d0b97c86f2d700e032632323840e29 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 19 Apr 2011 23:58:51 +0200 Subject: [PATCH] Issue #11223: Add threading._info() function providing informations about the thread implementation. Skip test_lock_acquire_interruption() and test_rlock_acquire_interruption() of test_threadsignals if a thread lock is implemented using a POSIX mutex and a POSIX condition variable. A POSIX condition variable cannot be interrupted by a signal (e.g. on Linux, the futex system call is restarted). --- Doc/library/threading.rst | 24 ++++++++++++++ Doc/whatsnew/3.3.rst | 8 +++++ Include/pythread.h | 4 ++- Lib/test/test_os.py | 15 +++++---- Lib/test/test_threading.py | 13 +++++++- Lib/test/test_threadsignals.py | 7 +++++ Lib/threading.py | 3 +- Misc/NEWS | 13 ++++++-- Modules/_threadmodule.c | 24 +++++++++----- Python/thread.c | 57 ++++++++++++++++++++++++++++++++++ 10 files changed, 150 insertions(+), 18 deletions(-) diff --git a/Doc/library/threading.rst b/Doc/library/threading.rst index df47045ffb5..dd2226d67f5 100644 --- a/Doc/library/threading.rst +++ b/Doc/library/threading.rst @@ -175,6 +175,30 @@ This module defines the following functions and objects: Availability: Windows, systems with POSIX threads. +.. function:: _info() + + Return a dictionary with informations about the thread implementation. + The ``'name'`` key gives the name of the thread implementation (string): + + * ``'nt'``: Windows threads + * ``'os2'``: OS/2 threads + * ``'pthread'``: POSIX threads + * ``'solaris'``: Solaris threads + + POSIX threads have two more keys: + + * ``'lock_implementation'`` (string): name of the lock + implementation + + * ``'semaphore'``: a lock uses a semaphore + * ``'mutex+cond'``: a lock uses a mutex and a condition variable + + * ``'pthread_version'`` (string, optional): name and version of the pthread + library + + .. versionadded:: 3.3 + + This module also defines the following constant: .. data:: TIMEOUT_MAX diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst index 602b2b70e3b..d3c9a97fc1f 100644 --- a/Doc/whatsnew/3.3.rst +++ b/Doc/whatsnew/3.3.rst @@ -112,6 +112,14 @@ connection when done:: (Contributed by Giampaolo RodolĂ  in :issue:`9795`) +threading +--------- + +* The :mod:`threading` module has a new :func:`~threading._info` function which + provides informations about the thread implementation. + + (:issue:`11223`) + Optimizations ============= diff --git a/Include/pythread.h b/Include/pythread.h index 9806c61159b..9a35e5d01c4 100644 --- a/Include/pythread.h +++ b/Include/pythread.h @@ -32,7 +32,7 @@ PyAPI_FUNC(int) PyThread_acquire_lock(PyThread_type_lock, int); on a lock (see PyThread_acquire_lock_timed() below). PY_TIMEOUT_MAX is the highest usable value (in microseconds) of that type, and depends on the system threading API. - + NOTE: this isn't the same value as `_thread.TIMEOUT_MAX`. The _thread module exposes a higher-level API, with timeouts expressed in seconds and floating-point numbers allowed. @@ -74,6 +74,8 @@ PyAPI_FUNC(void) PyThread_release_lock(PyThread_type_lock); PyAPI_FUNC(size_t) PyThread_get_stacksize(void); PyAPI_FUNC(int) PyThread_set_stacksize(size_t); +PyAPI_FUNC(PyObject*) _PyThread_Info(void); + /* Thread Local Storage (TLS) API */ PyAPI_FUNC(int) PyThread_create_key(void); PyAPI_FUNC(void) PyThread_delete_key(int); diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 35aa7fa75ed..543241294c6 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -27,12 +27,15 @@ except ImportError: # and unmaintained) linuxthreads threading library. There's an issue # when combining linuxthreads with a failed execv call: see # http://bugs.python.org/issue4970. -if (hasattr(os, "confstr_names") and - "CS_GNU_LIBPTHREAD_VERSION" in os.confstr_names): - libpthread = os.confstr("CS_GNU_LIBPTHREAD_VERSION") - USING_LINUXTHREADS= libpthread.startswith("linuxthreads") -else: - USING_LINUXTHREADS= False +USING_LINUXTHREADS = False +if threading: + info = threading._info() + try: + pthread_version = info['pthread_version'] + except KeyError: + pass + else: + USING_LINUXTHREADS = pthread_version.startswith("linuxthreads") # Tests creating TESTFN class FileTests(unittest.TestCase): diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index c107652d268..fd63d39367d 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -718,6 +718,17 @@ class BoundedSemaphoreTests(lock_tests.BoundedSemaphoreTests): class BarrierTests(lock_tests.BarrierTests): barriertype = staticmethod(threading.Barrier) + +class MiscTests(unittest.TestCase): + def test_info(self): + info = threading._info() + self.assertIn(info['name'], + 'nt os2 pthread solaris'.split()) + if info['name'] == 'pthread': + self.assertIn(info['lock_implementation'], + ('semaphore', 'mutex+cond')) + + def test_main(): test.support.run_unittest(LockTests, PyRLockTests, CRLockTests, EventTests, ConditionAsRLockTests, ConditionTests, @@ -725,7 +736,7 @@ def test_main(): ThreadTests, ThreadJoinOnShutdown, ThreadingExceptionTests, - BarrierTests + BarrierTests, MiscTests, ) if __name__ == "__main__": diff --git a/Lib/test/test_threadsignals.py b/Lib/test/test_threadsignals.py index 46e405ab818..b0bc6072851 100644 --- a/Lib/test/test_threadsignals.py +++ b/Lib/test/test_threadsignals.py @@ -14,6 +14,9 @@ if sys.platform[:3] in ('win', 'os2') or sys.platform=='riscos': process_pid = os.getpid() signalled_all=thread.allocate_lock() +info = thread.info() +USING_PTHREAD_COND = (info['name'] == 'pthread' + and info['lock_implementation'] == 'mutex+cond') def registerSignals(for_usr1, for_usr2, for_alrm): usr1 = signal.signal(signal.SIGUSR1, for_usr1) @@ -70,6 +73,8 @@ class ThreadSignals(unittest.TestCase): def alarm_interrupt(self, sig, frame): raise KeyboardInterrupt + @unittest.skipIf(USING_PTHREAD_COND, + 'POSIX condition variables cannot be interrupted') def test_lock_acquire_interruption(self): # Mimic receiving a SIGINT (KeyboardInterrupt) with SIGALRM while stuck # in a deadlock. @@ -91,6 +96,8 @@ class ThreadSignals(unittest.TestCase): finally: signal.signal(signal.SIGALRM, oldalrm) + @unittest.skipIf(USING_PTHREAD_COND, + 'POSIX condition variables cannot be interrupted') def test_rlock_acquire_interruption(self): # Mimic receiving a SIGINT (KeyboardInterrupt) with SIGALRM while stuck # in a deadlock. diff --git a/Lib/threading.py b/Lib/threading.py index cb09afaa663..eb3cb626c37 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -19,7 +19,7 @@ from collections import deque __all__ = ['active_count', 'Condition', 'current_thread', 'enumerate', 'Event', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Thread', 'Barrier', - 'Timer', 'setprofile', 'settrace', 'local', 'stack_size'] + 'Timer', 'setprofile', 'settrace', 'local', 'stack_size', '_info'] # Rename some stuff so "from threading import *" is safe _start_new_thread = _thread.start_new_thread @@ -31,6 +31,7 @@ try: except AttributeError: _CRLock = None TIMEOUT_MAX = _thread.TIMEOUT_MAX +_info = _thread.info del _thread diff --git a/Misc/NEWS b/Misc/NEWS index 27be0ccdf4b..7cb812cf79d 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -110,6 +110,9 @@ Core and Builtins Library ------- +- Issue #11223: Add threading._info() function providing informations about + the thread implementation. + - Issue #11731: simplify/enhance email parser/generator API by introducing policy objects. @@ -463,9 +466,9 @@ Build - Issue #11268: Prevent Mac OS X Installer failure if Documentation package had previously been installed. - + - Issue #11495: OSF support is eliminated. It was deprecated in Python 3.2. - + IDLE ---- @@ -487,6 +490,12 @@ Extensions Tests ----- +- Issue #11223: Skip test_lock_acquire_interruption() and + test_rlock_acquire_interruption() of test_threadsignals if a thread lock is + implemented using a POSIX mutex and a POSIX condition variable. A POSIX + condition variable cannot be interrupted by a signal (e.g. on Linux, the + futex system call is restarted). + - Issue #11790: Fix sporadic failures in test_multiprocessing.WithProcessesTestCondition. - Fix possible "file already exists" error when running the tests in parallel. diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index ef17b2845c3..914d671d6a2 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -1221,13 +1221,22 @@ requiring allocation in multiples of the system memory page size\n\ (4kB pages are common; using multiples of 4096 for the stack size is\n\ the suggested approach in the absence of more specific information)."); +static PyObject * +thread_info(PyObject *self) +{ + return _PyThread_Info(); +} + +PyDoc_STRVAR(thread_info_doc, +"info() -> dict\n\ +\n\ +Informations about the thread implementation."); + static PyMethodDef thread_methods[] = { {"start_new_thread", (PyCFunction)thread_PyThread_start_new_thread, - METH_VARARGS, - start_new_doc}, + METH_VARARGS, start_new_doc}, {"start_new", (PyCFunction)thread_PyThread_start_new_thread, - METH_VARARGS, - start_new_doc}, + METH_VARARGS, start_new_doc}, {"allocate_lock", (PyCFunction)thread_PyThread_allocate_lock, METH_NOARGS, allocate_doc}, {"allocate", (PyCFunction)thread_PyThread_allocate_lock, @@ -1243,8 +1252,9 @@ static PyMethodDef thread_methods[] = { {"_count", (PyCFunction)thread__count, METH_NOARGS, _count_doc}, {"stack_size", (PyCFunction)thread_stack_size, - METH_VARARGS, - stack_size_doc}, + METH_VARARGS, stack_size_doc}, + {"info", (PyCFunction)thread_info, + METH_NOARGS, thread_info_doc}, {NULL, NULL} /* sentinel */ }; @@ -1310,7 +1320,7 @@ PyInit__thread(void) d = PyModule_GetDict(m); ThreadError = PyExc_RuntimeError; Py_INCREF(ThreadError); - + PyDict_SetItemString(d, "error", ThreadError); Locktype.tp_doc = lock_doc; Py_INCREF(&Locktype); diff --git a/Python/thread.c b/Python/thread.c index d224046e647..5213a725aed 100644 --- a/Python/thread.c +++ b/Python/thread.c @@ -100,6 +100,7 @@ static size_t _pythread_stacksize = 0; #endif #ifdef SOLARIS_THREADS +#define PYTHREAD_NAME "solaris" #include "thread_solaris.h" #endif @@ -115,6 +116,7 @@ static size_t _pythread_stacksize = 0; #endif #ifdef _POSIX_THREADS +#define PYTHREAD_NAME "pthread" #include "thread_pthread.h" #endif @@ -124,14 +126,17 @@ static size_t _pythread_stacksize = 0; #endif #ifdef NT_THREADS +#define PYTHREAD_NAME "nt" #include "thread_nt.h" #endif #ifdef OS2_THREADS +#define PYTHREAD_NAME "os2" #include "thread_os2.h" #endif #ifdef PLAN9_THREADS +#define PYTHREAD_NAME "plan9" #include "thread_plan9.h" #endif @@ -409,3 +414,55 @@ PyThread_ReInitTLS(void) } #endif /* Py_HAVE_NATIVE_TLS */ + +PyObject* +_PyThread_Info(void) +{ + PyObject *info, *value; + int ret; + char buffer[255]; + int len; + + info = PyDict_New(); + if (info == NULL) + return NULL; + + value = PyUnicode_FromString(PYTHREAD_NAME); + ret = PyDict_SetItemString(info, "name", value); + Py_DECREF(value); + if (ret) + goto error; + +#ifdef _POSIX_THREADS +#ifdef USE_SEMAPHORES + value = PyUnicode_FromString("semaphore"); +#else + value = PyUnicode_FromString("mutex+cond"); +#endif + if (value == NULL) + return NULL; + ret = PyDict_SetItemString(info, "lock_implementation", value); + Py_DECREF(value); + if (ret) + goto error; + +#if defined(HAVE_CONFSTR) && defined(_CS_GNU_LIBPTHREAD_VERSION) + len = confstr(_CS_GNU_LIBPTHREAD_VERSION, buffer, sizeof(buffer)); + if (0 < len && len < sizeof(buffer)) { + value = PyUnicode_DecodeFSDefaultAndSize(buffer, len-1); + if (value == NULL) + goto error; + ret = PyDict_SetItemString(info, "pthread_version", value); + Py_DECREF(value); + if (ret) + goto error; + } +#endif +#endif + + return info; + +error: + Py_DECREF(info); + return NULL; +}