diff --git a/Doc/library/threading.rst b/Doc/library/threading.rst --- a/Doc/library/threading.rst +++ b/Doc/library/threading.rst @@ -175,6 +175,27 @@ This module defines the following functi Availability: Windows, systems with POSIX threads. +.. function:: _info() + + Return a dictionary with informations about the thread implementation. Keys: + + * ``'name'``: name of the implementation: + + * ``'cthread'``: Mach C threads + * ``'lwp'``: SunOS lightweight processes + * ``'nt'``: Windows threads + * ``'os2'``: OS/2 threads + * ``'pth'``: GNU pth threads + * ``'pthread'``: POSIX threads + * ``'sgi'``: SGI Irix threads + * ``'solaris'``: Solaris threads + + * ``'use_semaphores'`` (only available for POSIX threads): boolean + + * ``True``: use POSIX semaphores + * ``False``: use mutexes + + This module also defines the following constant: .. data:: TIMEOUT_MAX diff --git a/Include/pythread.h b/Include/pythread.h --- a/Include/pythread.h +++ b/Include/pythread.h @@ -32,7 +32,7 @@ PyAPI_FUNC(int) PyThread_acquire_lock(Py 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(P 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_threadsignals.py b/Lib/test/test_threadsignals.py --- a/Lib/test/test_threadsignals.py +++ b/Lib/test/test_threadsignals.py @@ -13,6 +13,7 @@ if sys.platform[:3] in ('win', 'os2') or process_pid = os.getpid() signalled_all=thread.allocate_lock() +info = thread.info() def registerSignals(for_usr1, for_usr2, for_alrm): @@ -70,6 +71,8 @@ class ThreadSignals(unittest.TestCase): def alarm_interrupt(self, sig, frame): raise KeyboardInterrupt + @unittest.skipIf(info['name'] == 'pthread' and not info['use_semaphores'], + 'POSIX mutexes cannot be interrupted') def test_lock_acquire_interruption(self): # Mimic receiving a SIGINT (KeyboardInterrupt) with SIGALRM while stuck # in a deadlock. @@ -91,6 +94,8 @@ class ThreadSignals(unittest.TestCase): finally: signal.signal(signal.SIGALRM, oldalrm) + @unittest.skipIf(info['name'] == 'pthread' and not info['use_semaphores'], + 'POSIX mutexes 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 --- 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/Modules/_threadmodule.c b/Modules/_threadmodule.c --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -1221,6 +1221,17 @@ requiring allocation in multiples of the (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, @@ -1245,6 +1256,9 @@ static PyMethodDef thread_methods[] = { {"stack_size", (PyCFunction)thread_stack_size, METH_VARARGS, stack_size_doc}, + {"info", (PyCFunction)thread_info, + METH_NOARGS, + thread_info_doc}, {NULL, NULL} /* sentinel */ }; @@ -1310,7 +1324,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 --- a/Python/thread.c +++ b/Python/thread.c @@ -96,42 +96,51 @@ static size_t _pythread_stacksize = 0; #ifdef SGI_THREADS #error SGI Irix threads are now unsupported, and code will be removed in 3.3. +#define PYTHREAD_NAME "sgi" #include "thread_sgi.h" #endif #ifdef SOLARIS_THREADS +#define PYTHREAD_NAME "solaris" #include "thread_solaris.h" #endif #ifdef SUN_LWP #error SunOS lightweight processes are now unsupported, and code will be removed in 3.3. +#define PYTHREAD_NAME "lwp" #include "thread_lwp.h" #endif #ifdef HAVE_PTH #error GNU pth threads are now unsupported, and code will be removed in 3.3. +#define PYTHREAD_NAME "pth" #include "thread_pth.h" #undef _POSIX_THREADS #endif #ifdef _POSIX_THREADS +#define PYTHREAD_NAME "pthread" #include "thread_pthread.h" #endif #ifdef C_THREADS #error Mach C Threads are now unsupported, and code will be removed in 3.3. +#define PYTHREAD_NAME "cthread" #include "thread_cthread.h" #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 +418,36 @@ PyThread_ReInitTLS(void) } #endif /* Py_HAVE_NATIVE_TLS */ + +PyObject* +PyThread_Info(void) +{ + PyObject *info, *value; + int ret; + + 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 + ret = PyDict_SetItemString(info, "use_semaphores", Py_True); +#else + ret = PyDict_SetItemString(info, "use_semaphores", Py_False); +#endif + if (ret) + goto error; +#endif + + return info; + +error: + Py_DECREF(info); + return NULL; +}