mirror of
https://github.com/python/cpython.git
synced 2025-07-24 03:35:53 +00:00
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
10 changed files with 150 additions and 18 deletions
|
@ -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::
|
||||||
|
|
||||||
(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
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ PyAPI_FUNC(int) PyThread_acquire_lock(PyThread_type_lock, int);
|
||||||
on a lock (see PyThread_acquire_lock_timed() below).
|
on a lock (see PyThread_acquire_lock_timed() below).
|
||||||
PY_TIMEOUT_MAX is the highest usable value (in microseconds) of that
|
PY_TIMEOUT_MAX is the highest usable value (in microseconds) of that
|
||||||
type, and depends on the system threading API.
|
type, and depends on the system threading API.
|
||||||
|
|
||||||
NOTE: this isn't the same value as `_thread.TIMEOUT_MAX`. The _thread
|
NOTE: this isn't the same value as `_thread.TIMEOUT_MAX`. The _thread
|
||||||
module exposes a higher-level API, with timeouts expressed in seconds
|
module exposes a higher-level API, with timeouts expressed in seconds
|
||||||
and floating-point numbers allowed.
|
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(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:
|
||||||
# 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):
|
||||||
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():
|
||||||
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':
|
||||||
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):
|
||||||
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):
|
||||||
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
|
||||||
|
|
||||||
__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:
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
|
|
13
Misc/NEWS
13
Misc/NEWS
|
@ -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.
|
||||||
|
|
||||||
|
@ -463,9 +466,9 @@ Build
|
||||||
|
|
||||||
- Issue #11268: Prevent Mac OS X Installer failure if Documentation
|
- Issue #11268: Prevent Mac OS X Installer failure if Documentation
|
||||||
package had previously been installed.
|
package had previously been installed.
|
||||||
|
|
||||||
- Issue #11495: OSF support is eliminated. It was deprecated in Python 3.2.
|
- Issue #11495: OSF support is eliminated. It was deprecated in Python 3.2.
|
||||||
|
|
||||||
|
|
||||||
IDLE
|
IDLE
|
||||||
----
|
----
|
||||||
|
@ -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\
|
||||||
(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[] = {
|
||||||
{"_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 */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1310,7 +1320,7 @@ PyInit__thread(void)
|
||||||
d = PyModule_GetDict(m);
|
d = PyModule_GetDict(m);
|
||||||
ThreadError = PyExc_RuntimeError;
|
ThreadError = PyExc_RuntimeError;
|
||||||
Py_INCREF(ThreadError);
|
Py_INCREF(ThreadError);
|
||||||
|
|
||||||
PyDict_SetItemString(d, "error", ThreadError);
|
PyDict_SetItemString(d, "error", ThreadError);
|
||||||
Locktype.tp_doc = lock_doc;
|
Locktype.tp_doc = lock_doc;
|
||||||
Py_INCREF(&Locktype);
|
Py_INCREF(&Locktype);
|
||||||
|
|
|
@ -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;
|
||||||
#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;
|
||||||
#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)
|
||||||
}
|
}
|
||||||
|
|
||||||
#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;
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue