Commit 754851f4 authored by Victor Stinner's avatar Victor Stinner

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).
parent cf2a8078
...@@ -175,6 +175,30 @@ This module defines the following functions and objects: ...@@ -175,6 +175,30 @@ This module defines the following functions and objects:
Availability: Windows, systems with POSIX threads. 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: This module also defines the following constant:
.. data:: TIMEOUT_MAX .. data:: TIMEOUT_MAX
......
...@@ -112,6 +112,14 @@ connection when done:: ...@@ -112,6 +112,14 @@ connection when done::
(Contributed by Giampaolo Rodolà in :issue:`9795`) (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 Optimizations
============= =============
......
...@@ -74,6 +74,8 @@ PyAPI_FUNC(void) PyThread_release_lock(PyThread_type_lock); ...@@ -74,6 +74,8 @@ PyAPI_FUNC(void) PyThread_release_lock(PyThread_type_lock);
PyAPI_FUNC(size_t) PyThread_get_stacksize(void); PyAPI_FUNC(size_t) PyThread_get_stacksize(void);
PyAPI_FUNC(int) PyThread_set_stacksize(size_t); PyAPI_FUNC(int) PyThread_set_stacksize(size_t);
PyAPI_FUNC(PyObject*) _PyThread_Info(void);
/* Thread Local Storage (TLS) API */ /* Thread Local Storage (TLS) API */
PyAPI_FUNC(int) PyThread_create_key(void); PyAPI_FUNC(int) PyThread_create_key(void);
PyAPI_FUNC(void) PyThread_delete_key(int); PyAPI_FUNC(void) PyThread_delete_key(int);
......
...@@ -27,12 +27,15 @@ except ImportError: ...@@ -27,12 +27,15 @@ except ImportError:
# and unmaintained) linuxthreads threading library. There's an issue # and unmaintained) linuxthreads threading library. There's an issue
# when combining linuxthreads with a failed execv call: see # when combining linuxthreads with a failed execv call: see
# http://bugs.python.org/issue4970. # http://bugs.python.org/issue4970.
if (hasattr(os, "confstr_names") and USING_LINUXTHREADS = False
"CS_GNU_LIBPTHREAD_VERSION" in os.confstr_names): if threading:
libpthread = os.confstr("CS_GNU_LIBPTHREAD_VERSION") info = threading._info()
USING_LINUXTHREADS= libpthread.startswith("linuxthreads") try:
else: pthread_version = info['pthread_version']
USING_LINUXTHREADS= False except KeyError:
pass
else:
USING_LINUXTHREADS = pthread_version.startswith("linuxthreads")
# Tests creating TESTFN # Tests creating TESTFN
class FileTests(unittest.TestCase): class FileTests(unittest.TestCase):
......
...@@ -718,6 +718,17 @@ class BoundedSemaphoreTests(lock_tests.BoundedSemaphoreTests): ...@@ -718,6 +718,17 @@ class BoundedSemaphoreTests(lock_tests.BoundedSemaphoreTests):
class BarrierTests(lock_tests.BarrierTests): class BarrierTests(lock_tests.BarrierTests):
barriertype = staticmethod(threading.Barrier) 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(): def test_main():
test.support.run_unittest(LockTests, PyRLockTests, CRLockTests, EventTests, test.support.run_unittest(LockTests, PyRLockTests, CRLockTests, EventTests,
ConditionAsRLockTests, ConditionTests, ConditionAsRLockTests, ConditionTests,
...@@ -725,7 +736,7 @@ def test_main(): ...@@ -725,7 +736,7 @@ def test_main():
ThreadTests, ThreadTests,
ThreadJoinOnShutdown, ThreadJoinOnShutdown,
ThreadingExceptionTests, ThreadingExceptionTests,
BarrierTests BarrierTests, MiscTests,
) )
if __name__ == "__main__": if __name__ == "__main__":
......
...@@ -14,6 +14,9 @@ if sys.platform[:3] in ('win', 'os2') or sys.platform=='riscos': ...@@ -14,6 +14,9 @@ if sys.platform[:3] in ('win', 'os2') or sys.platform=='riscos':
process_pid = os.getpid() process_pid = os.getpid()
signalled_all=thread.allocate_lock() 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): def registerSignals(for_usr1, for_usr2, for_alrm):
usr1 = signal.signal(signal.SIGUSR1, for_usr1) usr1 = signal.signal(signal.SIGUSR1, for_usr1)
...@@ -70,6 +73,8 @@ class ThreadSignals(unittest.TestCase): ...@@ -70,6 +73,8 @@ class ThreadSignals(unittest.TestCase):
def alarm_interrupt(self, sig, frame): def alarm_interrupt(self, sig, frame):
raise KeyboardInterrupt raise KeyboardInterrupt
@unittest.skipIf(USING_PTHREAD_COND,
'POSIX condition variables cannot be interrupted')
def test_lock_acquire_interruption(self): def test_lock_acquire_interruption(self):
# Mimic receiving a SIGINT (KeyboardInterrupt) with SIGALRM while stuck # Mimic receiving a SIGINT (KeyboardInterrupt) with SIGALRM while stuck
# in a deadlock. # in a deadlock.
...@@ -91,6 +96,8 @@ class ThreadSignals(unittest.TestCase): ...@@ -91,6 +96,8 @@ class ThreadSignals(unittest.TestCase):
finally: finally:
signal.signal(signal.SIGALRM, oldalrm) signal.signal(signal.SIGALRM, oldalrm)
@unittest.skipIf(USING_PTHREAD_COND,
'POSIX condition variables cannot be interrupted')
def test_rlock_acquire_interruption(self): def test_rlock_acquire_interruption(self):
# Mimic receiving a SIGINT (KeyboardInterrupt) with SIGALRM while stuck # Mimic receiving a SIGINT (KeyboardInterrupt) with SIGALRM while stuck
# in a deadlock. # in a deadlock.
......
...@@ -19,7 +19,7 @@ from collections import deque ...@@ -19,7 +19,7 @@ from collections import deque
__all__ = ['active_count', 'Condition', 'current_thread', 'enumerate', 'Event', __all__ = ['active_count', 'Condition', 'current_thread', 'enumerate', 'Event',
'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Thread', 'Barrier', '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 # Rename some stuff so "from threading import *" is safe
_start_new_thread = _thread.start_new_thread _start_new_thread = _thread.start_new_thread
...@@ -31,6 +31,7 @@ try: ...@@ -31,6 +31,7 @@ try:
except AttributeError: except AttributeError:
_CRLock = None _CRLock = None
TIMEOUT_MAX = _thread.TIMEOUT_MAX TIMEOUT_MAX = _thread.TIMEOUT_MAX
_info = _thread.info
del _thread del _thread
......
...@@ -110,6 +110,9 @@ Core and Builtins ...@@ -110,6 +110,9 @@ Core and Builtins
Library Library
------- -------
- Issue #11223: Add threading._info() function providing informations about
the thread implementation.
- Issue #11731: simplify/enhance email parser/generator API by introducing - Issue #11731: simplify/enhance email parser/generator API by introducing
policy objects. policy objects.
...@@ -487,6 +490,12 @@ Extensions ...@@ -487,6 +490,12 @@ Extensions
Tests 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. - Issue #11790: Fix sporadic failures in test_multiprocessing.WithProcessesTestCondition.
- Fix possible "file already exists" error when running the tests in parallel. - Fix possible "file already exists" error when running the tests in parallel.
......
...@@ -1221,13 +1221,22 @@ requiring allocation in multiples of the system memory page size\n\ ...@@ -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\ (4kB pages are common; using multiples of 4096 for the stack size is\n\
the suggested approach in the absence of more specific information)."); 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[] = { static PyMethodDef thread_methods[] = {
{"start_new_thread", (PyCFunction)thread_PyThread_start_new_thread, {"start_new_thread", (PyCFunction)thread_PyThread_start_new_thread,
METH_VARARGS, METH_VARARGS, start_new_doc},
start_new_doc},
{"start_new", (PyCFunction)thread_PyThread_start_new_thread, {"start_new", (PyCFunction)thread_PyThread_start_new_thread,
METH_VARARGS, METH_VARARGS, start_new_doc},
start_new_doc},
{"allocate_lock", (PyCFunction)thread_PyThread_allocate_lock, {"allocate_lock", (PyCFunction)thread_PyThread_allocate_lock,
METH_NOARGS, allocate_doc}, METH_NOARGS, allocate_doc},
{"allocate", (PyCFunction)thread_PyThread_allocate_lock, {"allocate", (PyCFunction)thread_PyThread_allocate_lock,
...@@ -1243,8 +1252,9 @@ static PyMethodDef thread_methods[] = { ...@@ -1243,8 +1252,9 @@ static PyMethodDef thread_methods[] = {
{"_count", (PyCFunction)thread__count, {"_count", (PyCFunction)thread__count,
METH_NOARGS, _count_doc}, METH_NOARGS, _count_doc},
{"stack_size", (PyCFunction)thread_stack_size, {"stack_size", (PyCFunction)thread_stack_size,
METH_VARARGS, METH_VARARGS, stack_size_doc},
stack_size_doc}, {"info", (PyCFunction)thread_info,
METH_NOARGS, thread_info_doc},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
......
...@@ -100,6 +100,7 @@ static size_t _pythread_stacksize = 0; ...@@ -100,6 +100,7 @@ static size_t _pythread_stacksize = 0;
#endif #endif
#ifdef SOLARIS_THREADS #ifdef SOLARIS_THREADS
#define PYTHREAD_NAME "solaris"
#include "thread_solaris.h" #include "thread_solaris.h"
#endif #endif
...@@ -115,6 +116,7 @@ static size_t _pythread_stacksize = 0; ...@@ -115,6 +116,7 @@ static size_t _pythread_stacksize = 0;
#endif #endif
#ifdef _POSIX_THREADS #ifdef _POSIX_THREADS
#define PYTHREAD_NAME "pthread"
#include "thread_pthread.h" #include "thread_pthread.h"
#endif #endif
...@@ -124,14 +126,17 @@ static size_t _pythread_stacksize = 0; ...@@ -124,14 +126,17 @@ static size_t _pythread_stacksize = 0;
#endif #endif
#ifdef NT_THREADS #ifdef NT_THREADS
#define PYTHREAD_NAME "nt"
#include "thread_nt.h" #include "thread_nt.h"
#endif #endif
#ifdef OS2_THREADS #ifdef OS2_THREADS
#define PYTHREAD_NAME "os2"
#include "thread_os2.h" #include "thread_os2.h"
#endif #endif
#ifdef PLAN9_THREADS #ifdef PLAN9_THREADS
#define PYTHREAD_NAME "plan9"
#include "thread_plan9.h" #include "thread_plan9.h"
#endif #endif
...@@ -409,3 +414,55 @@ PyThread_ReInitTLS(void) ...@@ -409,3 +414,55 @@ PyThread_ReInitTLS(void)
} }
#endif /* Py_HAVE_NATIVE_TLS */ #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;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment