mirror of https://github.com/python/cpython
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).
This commit is contained in:
parent
cf2a807831
commit
754851f456
|
@ -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
|
||||
|
|
|
@ -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
|
||||
=============
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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__":
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
13
Misc/NEWS
13
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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue