From 2067bfdf253e134a4144d3747300dbfcc7cc6203 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 25 May 2008 13:05:15 +0000 Subject: Rename thread to _thread and dummy_thread to _dummy_thread. Issue #2875. --- Demo/metaclasses/Synch.py | 2 +- Demo/pysvr/pysvr.py | 8 +- Demo/threads/Coroutine.py | 2 +- Demo/threads/Generator.py | 2 +- Demo/threads/find.py | 2 +- Demo/threads/sync.py | 2 +- Demo/threads/telnet.py | 2 +- Doc/c-api/exceptions.rst | 2 +- Doc/c-api/init.rst | 4 +- Doc/c-api/typeobj.rst | 2 +- Doc/library/_dummy_thread.rst | 22 + Doc/library/_thread.rst | 169 ++++++++ Doc/library/dummy_thread.rst | 23 - Doc/library/dummy_threading.rst | 13 +- Doc/library/someos.rst | 4 +- Doc/library/thread.rst | 170 -------- Doc/library/threading.rst | 9 +- Include/ceval.h | 2 +- Lib/_dummy_thread.py | 149 +++++++ Lib/_strptime.py | 4 +- Lib/bsddb/__init__.py | 4 +- Lib/dummy_thread.py | 149 ------- Lib/dummy_threading.py | 20 +- Lib/idlelib/run.py | 2 +- Lib/logging/__init__.py | 2 +- Lib/logging/config.py | 2 +- Lib/mimetools.py | 8 +- Lib/pydoc.py | 2 +- Lib/telnetlib.py | 4 +- Lib/tempfile.py | 4 +- Lib/test/crashers/multithreaded_close.py | 4 +- Lib/test/fork_wait.py | 4 +- Lib/test/test___all__.py | 2 +- Lib/test/test_asynchat.py | 2 +- Lib/test/test_capi.py | 8 +- Lib/test/test_dummy_thread.py | 6 +- Lib/test/test_socket.py | 3 +- Lib/test/test_sys.py | 8 +- Lib/test/test_thread.py | 2 +- Lib/test/test_threaded_import.py | 2 +- Lib/test/test_threadedtempfile.py | 2 +- Lib/test/test_threading.py | 18 +- Lib/test/test_threadsignals.py | 2 +- Lib/threading.py | 31 +- Misc/cheatsheet | 4 +- Modules/Setup.config.in | 2 +- Modules/_threadmodule.c | 720 +++++++++++++++++++++++++++++++ Modules/threadmodule.c | 720 ------------------------------- PC/VC6/pythoncore.dsp | 2 +- PC/VS7.1/pythoncore.vcproj | 2 +- PC/VS8.0/pythoncore.vcproj | 2 +- PC/config.c | 4 +- PC/os2emx/Makefile | 2 +- PC/os2emx/config.c | 4 +- PC/os2emx/python26.def | 4 +- PC/os2vacpp/config.c | 4 +- PC/os2vacpp/makefile | 2 +- PC/os2vacpp/makefile.omk | 2 +- PCbuild/pythoncore.vcproj | 2 +- 59 files changed, 1176 insertions(+), 1184 deletions(-) create mode 100644 Doc/library/_dummy_thread.rst create mode 100644 Doc/library/_thread.rst delete mode 100644 Doc/library/dummy_thread.rst delete mode 100644 Doc/library/thread.rst create mode 100644 Lib/_dummy_thread.py delete mode 100644 Lib/dummy_thread.py create mode 100644 Modules/_threadmodule.c delete mode 100644 Modules/threadmodule.c diff --git a/Demo/metaclasses/Synch.py b/Demo/metaclasses/Synch.py index 445ce83..e02f88f 100644 --- a/Demo/metaclasses/Synch.py +++ b/Demo/metaclasses/Synch.py @@ -4,7 +4,7 @@ This metaclass makes it possible to declare synchronized methods. """ -import thread +import _thread as thread # First we need to define a reentrant lock. # This is generally useful and should probably be in a standard Python diff --git a/Demo/pysvr/pysvr.py b/Demo/pysvr/pysvr.py index b200fad..3e94dbe 100755 --- a/Demo/pysvr/pysvr.py +++ b/Demo/pysvr/pysvr.py @@ -12,7 +12,7 @@ can log in on your machine. Use with caution! """ -import sys, os, string, getopt, thread, socket, traceback +import sys, os, string, getopt, _thread, socket, traceback PORT = 4000 # Default port @@ -52,17 +52,17 @@ def main_thread(port): conn.close() print("Refusing connection from non-local host", addr[0], ".") continue - thread.start_new_thread(service_thread, (conn, addr)) + _thread.start_new_thread(service_thread, (conn, addr)) del conn, addr def service_thread(conn, addr): (caddr, cport) = addr - print("Thread %s has connection from %s.\n" % (str(thread.get_ident()), + print("Thread %s has connection from %s.\n" % (str(_thread.get_ident()), caddr), end=' ') stdin = conn.makefile("r") stdout = conn.makefile("w", 0) run_interpreter(stdin, stdout) - print("Thread %s is done.\n" % str(thread.get_ident()), end=' ') + print("Thread %s is done.\n" % str(_thread.get_ident()), end=' ') def run_interpreter(stdin, stdout): globals = {} diff --git a/Demo/threads/Coroutine.py b/Demo/threads/Coroutine.py index 4a155f8..e7d882d 100644 --- a/Demo/threads/Coroutine.py +++ b/Demo/threads/Coroutine.py @@ -66,7 +66,7 @@ # current implementation consumes a thread for each coroutine that # may be resumed. -import thread +import _thread as thread import sync class _CoEvent: diff --git a/Demo/threads/Generator.py b/Demo/threads/Generator.py index 0cc1bda..38c0c8a 100644 --- a/Demo/threads/Generator.py +++ b/Demo/threads/Generator.py @@ -1,6 +1,6 @@ # Generator implementation using threads -import thread +import _thread as thread Killed = 'Generator.Killed' diff --git a/Demo/threads/find.py b/Demo/threads/find.py index 57fe81e..2b4ef7d 100644 --- a/Demo/threads/find.py +++ b/Demo/threads/find.py @@ -20,7 +20,7 @@ import getopt import time import os from stat import * -import thread +import _thread as thread # Work queue class. Usage: diff --git a/Demo/threads/sync.py b/Demo/threads/sync.py index 61e1628..90fff2e 100644 --- a/Demo/threads/sync.py +++ b/Demo/threads/sync.py @@ -268,7 +268,7 @@ # if there are are no threads waiting to write. (This is a # weakness of the interface!) -import thread +import _thread as thread class condition: def __init__(self, lock=None): diff --git a/Demo/threads/telnet.py b/Demo/threads/telnet.py index 7366341..dfe4905 100644 --- a/Demo/threads/telnet.py +++ b/Demo/threads/telnet.py @@ -15,7 +15,7 @@ import sys, os, time from socket import * -import thread +import _thread as thread BUFSIZE = 8*1024 diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index 9c9ee7f..7440dda 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -351,7 +351,7 @@ in various ways. There is a separate error indicator for each thread. be raised. It may be called without holding the interpreter lock. .. % XXX This was described as obsolete, but is used in - .. % thread.interrupt_main() (used from IDLE), so it's still needed. + .. % _thread.interrupt_main() (used from IDLE), so it's still needed. .. cfunction:: int PySignal_SetWakeupFd(int fd) diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index 4bf6445..864991e 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -539,7 +539,7 @@ supports the creation of additional interpreters (using This is a no-op when called for a second time. It is safe to call this function before calling :cfunc:`Py_Initialize`. - .. index:: module: thread + .. index:: module: _thread When only the main thread exists, no lock operations are needed. This is a common situation (most Python programs do not use threads), and the lock @@ -547,7 +547,7 @@ supports the creation of additional interpreters (using initially. This situation is equivalent to having acquired the lock: when there is only a single thread, all object accesses are safe. Therefore, when this function initializes the lock, it also acquires it. Before the Python - :mod:`thread` module creates a new thread, knowing that either it has the lock + :mod:`_thread` module creates a new thread, knowing that either it has the lock or the lock hasn't been created yet, it calls :cfunc:`PyEval_InitThreads`. When this call returns, it is guaranteed that the lock has been created and that the calling thread has acquired it. diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index 9f47516..8ea61b0 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -488,7 +488,7 @@ type objects) *must* have the :attr:`ob_size` field. reference cycles. A typical implementation of a :attr:`tp_traverse` function simply calls :cfunc:`Py_VISIT` on each of the instance's members that are Python objects. For example, this is function :cfunc:`local_traverse` from the - :mod:`thread` extension module:: + :mod:`_thread` extension module:: static int local_traverse(localobject *self, visitproc visit, void *arg) diff --git a/Doc/library/_dummy_thread.rst b/Doc/library/_dummy_thread.rst new file mode 100644 index 0000000..62e5708 --- /dev/null +++ b/Doc/library/_dummy_thread.rst @@ -0,0 +1,22 @@ +:mod:`_dummy_thread` --- Drop-in replacement for the :mod:`_thread` module +========================================================================== + +.. module:: _dummy_thread + :synopsis: Drop-in replacement for the _thread module. + + +This module provides a duplicate interface to the :mod:`_thread` module. It is +meant to be imported when the :mod:`_thread` module is not provided on a +platform. + +Suggested usage is:: + + try: + import _thread + except ImportError: + import dummy_thread as _thread + +Be careful to not use this module where deadlock might occur from a thread being +created that blocks waiting for another thread to be created. This often occurs +with blocking I/O. + diff --git a/Doc/library/_thread.rst b/Doc/library/_thread.rst new file mode 100644 index 0000000..95214d6 --- /dev/null +++ b/Doc/library/_thread.rst @@ -0,0 +1,169 @@ +:mod:`_thread` --- Low-level threading API +========================================== + +.. module:: _thread + :synopsis: Low-level threading API. + + +.. index:: + single: light-weight processes + single: processes, light-weight + single: binary semaphores + single: semaphores, binary + +This module provides low-level primitives for working with multiple threads +(also called :dfn:`light-weight processes` or :dfn:`tasks`) --- multiple threads of +control sharing their global data space. For synchronization, simple locks +(also called :dfn:`mutexes` or :dfn:`binary semaphores`) are provided. +The :mod:`threading` module provides an easier to use and higher-level +threading API built on top of this module. + +.. index:: + single: pthreads + pair: threads; POSIX + +The module is optional. It is supported on Windows, Linux, SGI IRIX, Solaris +2.x, as well as on systems that have a POSIX thread (a.k.a. "pthread") +implementation. For systems lacking the :mod:`_thread` module, the +:mod:`_dummy_thread` module is available. It duplicates this module's interface +and can be used as a drop-in replacement. + +It defines the following constant and functions: + + +.. exception:: error + + Raised on thread-specific errors. + + +.. data:: LockType + + This is the type of lock objects. + + +.. function:: start_new_thread(function, args[, kwargs]) + + Start a new thread and return its identifier. The thread executes the function + *function* with the argument list *args* (which must be a tuple). The optional + *kwargs* argument specifies a dictionary of keyword arguments. When the function + returns, the thread silently exits. When the function terminates with an + unhandled exception, a stack trace is printed and then the thread exits (but + other threads continue to run). + + +.. function:: interrupt_main() + + Raise a :exc:`KeyboardInterrupt` exception in the main thread. A subthread can + use this function to interrupt the main thread. + + +.. function:: exit() + + Raise the :exc:`SystemExit` exception. When not caught, this will cause the + thread to exit silently. + +.. + function:: exit_prog(status) + + Exit all threads and report the value of the integer argument + *status* as the exit status of the entire program. + **Caveat:** code in pending :keyword:`finally` clauses, in this thread + or in other threads, is not executed. + + +.. function:: allocate_lock() + + Return a new lock object. Methods of locks are described below. The lock is + initially unlocked. + + +.. function:: get_ident() + + Return the 'thread identifier' of the current thread. This is a nonzero + integer. Its value has no direct meaning; it is intended as a magic cookie to + be used e.g. to index a dictionary of thread-specific data. Thread identifiers + may be recycled when a thread exits and another thread is created. + + +.. function:: stack_size([size]) + + Return the thread stack size used when creating new threads. The optional + *size* argument specifies the stack size to be used for subsequently created + threads, and must be 0 (use platform or configured default) or a positive + integer value of at least 32,768 (32kB). If changing the thread stack size is + unsupported, a :exc:`ThreadError` is raised. If the specified stack size is + invalid, a :exc:`ValueError` is raised and the stack size is unmodified. 32kB + is currently the minimum supported stack size value to guarantee sufficient + stack space for the interpreter itself. Note that some platforms may have + particular restrictions on values for the stack size, such as requiring a + minimum stack size > 32kB or requiring allocation in multiples of the system + memory page size - platform documentation should be referred to for more + information (4kB pages are common; using multiples of 4096 for the stack size is + the suggested approach in the absence of more specific information). + Availability: Windows, systems with POSIX threads. + + +Lock objects have the following methods: + + +.. method:: lock.acquire([waitflag]) + + Without the optional argument, this method acquires the lock unconditionally, if + necessary waiting until it is released by another thread (only one thread at a + time can acquire a lock --- that's their reason for existence). If the integer + *waitflag* argument is present, the action depends on its value: if it is zero, + the lock is only acquired if it can be acquired immediately without waiting, + while if it is nonzero, the lock is acquired unconditionally as before. The + return value is ``True`` if the lock is acquired successfully, ``False`` if not. + + +.. method:: lock.release() + + Releases the lock. The lock must have been acquired earlier, but not + necessarily by the same thread. + + +.. method:: lock.locked() + + Return the status of the lock: ``True`` if it has been acquired by some thread, + ``False`` if not. + +In addition to these methods, lock objects can also be used via the +:keyword:`with` statement, e.g.:: + + import _thread + + a_lock = _thread.allocate_lock() + + with a_lock: + print("a_lock is locked while this executes") + +**Caveats:** + + .. index:: module: signal + +* Threads interact strangely with interrupts: the :exc:`KeyboardInterrupt` + exception will be received by an arbitrary thread. (When the :mod:`signal` + module is available, interrupts always go to the main thread.) + +* Calling :func:`sys.exit` or raising the :exc:`SystemExit` exception is + equivalent to calling :func:`exit`. + +* Not all built-in functions that may block waiting for I/O allow other threads + to run. (The most popular ones (:func:`time.sleep`, :meth:`file.read`, + :func:`select.select`) work as expected.) + +* It is not possible to interrupt the :meth:`acquire` method on a lock --- the + :exc:`KeyboardInterrupt` exception will happen after the lock has been acquired. + + .. index:: pair: threads; IRIX + +* When the main thread exits, it is system defined whether the other threads + survive. On SGI IRIX using the native thread implementation, they survive. On + most other systems, they are killed without executing :keyword:`try` ... + :keyword:`finally` clauses or executing object destructors. + +* When the main thread exits, it does not do any of its usual cleanup (except + that :keyword:`try` ... :keyword:`finally` clauses are honored), and the + standard I/O files are not flushed. + diff --git a/Doc/library/dummy_thread.rst b/Doc/library/dummy_thread.rst deleted file mode 100644 index 0b2cb17..0000000 --- a/Doc/library/dummy_thread.rst +++ /dev/null @@ -1,23 +0,0 @@ - -:mod:`dummy_thread` --- Drop-in replacement for the :mod:`thread` module -======================================================================== - -.. module:: dummy_thread - :synopsis: Drop-in replacement for the thread module. - - -This module provides a duplicate interface to the :mod:`thread` module. It is -meant to be imported when the :mod:`thread` module is not provided on a -platform. - -Suggested usage is:: - - try: - import thread as _thread - except ImportError: - import dummy_thread as _thread - -Be careful to not use this module where deadlock might occur from a thread -being created that blocks waiting for another thread to be created. This often -occurs with blocking I/O. - diff --git a/Doc/library/dummy_threading.rst b/Doc/library/dummy_threading.rst index 0ffb687..0658df2 100644 --- a/Doc/library/dummy_threading.rst +++ b/Doc/library/dummy_threading.rst @@ -1,4 +1,3 @@ - :mod:`dummy_threading` --- Drop-in replacement for the :mod:`threading` module ============================================================================== @@ -7,17 +6,17 @@ This module provides a duplicate interface to the :mod:`threading` module. It -is meant to be imported when the :mod:`thread` module is not provided on a +is meant to be imported when the :mod:`_thread` module is not provided on a platform. Suggested usage is:: try: - import threading as _threading + import threading except ImportError: - import dummy_threading as _threading + import dummy_threading -Be careful to not use this module where deadlock might occur from a thread -being created that blocks waiting for another thread to be created. This often -occurs with blocking I/O. +Be careful to not use this module where deadlock might occur from a thread being +created that blocks waiting for another thread to be created. This often occurs +with blocking I/O. diff --git a/Doc/library/someos.rst b/Doc/library/someos.rst index 5ee96bc..160ce48 100644 --- a/Doc/library/someos.rst +++ b/Doc/library/someos.rst @@ -14,10 +14,10 @@ some other systems as well (e.g. Windows or NT). Here's an overview: .. toctree:: select.rst - thread.rst threading.rst - dummy_thread.rst dummy_threading.rst + _thread.rst + _dummy_thread.rst mmap.rst readline.rst rlcompleter.rst diff --git a/Doc/library/thread.rst b/Doc/library/thread.rst deleted file mode 100644 index 31d58e7..0000000 --- a/Doc/library/thread.rst +++ /dev/null @@ -1,170 +0,0 @@ - -:mod:`thread` --- Multiple threads of control -============================================= - -.. module:: thread - :synopsis: Create multiple threads of control within one interpreter. - - -.. index:: - single: light-weight processes - single: processes, light-weight - single: binary semaphores - single: semaphores, binary - -This module provides low-level primitives for working with multiple threads -(also called :dfn:`light-weight processes` or :dfn:`tasks`) --- multiple threads of -control sharing their global data space. For synchronization, simple locks -(also called :dfn:`mutexes` or :dfn:`binary semaphores`) are provided. -The :mod:`threading` module provides an easier to use and higher-level -threading API built on top of this module. - -.. index:: - single: pthreads - pair: threads; POSIX - -The module is optional. It is supported on Windows, Linux, SGI IRIX, Solaris -2.x, as well as on systems that have a POSIX thread (a.k.a. "pthread") -implementation. For systems lacking the :mod:`thread` module, the -:mod:`dummy_thread` module is available. It duplicates this module's interface -and can be used as a drop-in replacement. - -It defines the following constant and functions: - - -.. exception:: error - - Raised on thread-specific errors. - - -.. data:: LockType - - This is the type of lock objects. - - -.. function:: start_new_thread(function, args[, kwargs]) - - Start a new thread and return its identifier. The thread executes the function - *function* with the argument list *args* (which must be a tuple). The optional - *kwargs* argument specifies a dictionary of keyword arguments. When the function - returns, the thread silently exits. When the function terminates with an - unhandled exception, a stack trace is printed and then the thread exits (but - other threads continue to run). - - -.. function:: interrupt_main() - - Raise a :exc:`KeyboardInterrupt` exception in the main thread. A subthread can - use this function to interrupt the main thread. - - -.. function:: exit() - - Raise the :exc:`SystemExit` exception. When not caught, this will cause the - thread to exit silently. - -.. - function:: exit_prog(status) - - Exit all threads and report the value of the integer argument - *status* as the exit status of the entire program. - **Caveat:** code in pending :keyword:`finally` clauses, in this thread - or in other threads, is not executed. - - -.. function:: allocate_lock() - - Return a new lock object. Methods of locks are described below. The lock is - initially unlocked. - - -.. function:: get_ident() - - Return the 'thread identifier' of the current thread. This is a nonzero - integer. Its value has no direct meaning; it is intended as a magic cookie to - be used e.g. to index a dictionary of thread-specific data. Thread identifiers - may be recycled when a thread exits and another thread is created. - - -.. function:: stack_size([size]) - - Return the thread stack size used when creating new threads. The optional - *size* argument specifies the stack size to be used for subsequently created - threads, and must be 0 (use platform or configured default) or a positive - integer value of at least 32,768 (32kB). If changing the thread stack size is - unsupported, a :exc:`ThreadError` is raised. If the specified stack size is - invalid, a :exc:`ValueError` is raised and the stack size is unmodified. 32kB - is currently the minimum supported stack size value to guarantee sufficient - stack space for the interpreter itself. Note that some platforms may have - particular restrictions on values for the stack size, such as requiring a - minimum stack size > 32kB or requiring allocation in multiples of the system - memory page size - platform documentation should be referred to for more - information (4kB pages are common; using multiples of 4096 for the stack size is - the suggested approach in the absence of more specific information). - Availability: Windows, systems with POSIX threads. - - -Lock objects have the following methods: - - -.. method:: lock.acquire([waitflag]) - - Without the optional argument, this method acquires the lock unconditionally, if - necessary waiting until it is released by another thread (only one thread at a - time can acquire a lock --- that's their reason for existence). If the integer - *waitflag* argument is present, the action depends on its value: if it is zero, - the lock is only acquired if it can be acquired immediately without waiting, - while if it is nonzero, the lock is acquired unconditionally as before. The - return value is ``True`` if the lock is acquired successfully, ``False`` if not. - - -.. method:: lock.release() - - Releases the lock. The lock must have been acquired earlier, but not - necessarily by the same thread. - - -.. method:: lock.locked() - - Return the status of the lock: ``True`` if it has been acquired by some thread, - ``False`` if not. - -In addition to these methods, lock objects can also be used via the -:keyword:`with` statement, e.g.:: - - import thread - - a_lock = thread.allocate_lock() - - with a_lock: - print("a_lock is locked while this executes") - -**Caveats:** - - .. index:: module: signal - -* Threads interact strangely with interrupts: the :exc:`KeyboardInterrupt` - exception will be received by an arbitrary thread. (When the :mod:`signal` - module is available, interrupts always go to the main thread.) - -* Calling :func:`sys.exit` or raising the :exc:`SystemExit` exception is - equivalent to calling :func:`exit`. - -* Not all built-in functions that may block waiting for I/O allow other threads - to run. (The most popular ones (:func:`time.sleep`, :meth:`file.read`, - :func:`select.select`) work as expected.) - -* It is not possible to interrupt the :meth:`acquire` method on a lock --- the - :exc:`KeyboardInterrupt` exception will happen after the lock has been acquired. - - .. index:: pair: threads; IRIX - -* When the main thread exits, it is system defined whether the other threads - survive. On SGI IRIX using the native thread implementation, they survive. On - most other systems, they are killed without executing :keyword:`try` ... - :keyword:`finally` clauses or executing object destructors. - -* When the main thread exits, it does not do any of its usual cleanup (except - that :keyword:`try` ... :keyword:`finally` clauses are honored), and the - standard I/O files are not flushed. - diff --git a/Doc/library/threading.rst b/Doc/library/threading.rst index 6be2f62..2381f24 100644 --- a/Doc/library/threading.rst +++ b/Doc/library/threading.rst @@ -6,12 +6,11 @@ :synopsis: Higher-level threading interface. -This module constructs higher-level threading interfaces on top of the lower -level :mod:`thread` module. -See also the :mod:`queue` module. +This module constructs higher-level threading interfaces on top of the lower +level :mod:`_thread` module. See also the :mod:`queue` module. The :mod:`dummy_threading` module is provided for situations where -:mod:`threading` cannot be used because :mod:`thread` is missing. +:mod:`threading` cannot be used because :mod:`_thread` is missing. This module defines the following functions and objects: @@ -170,7 +169,7 @@ Lock Objects A primitive lock is a synchronization primitive that is not owned by a particular thread when locked. In Python, it is currently the lowest level -synchronization primitive available, implemented directly by the :mod:`thread` +synchronization primitive available, implemented directly by the :mod:`_thread` extension module. A primitive lock is in one of two states, "locked" or "unlocked". It is created diff --git a/Include/ceval.h b/Include/ceval.h index 03eb8cb..f0385cf 100644 --- a/Include/ceval.h +++ b/Include/ceval.h @@ -120,7 +120,7 @@ PyAPI_DATA(int) _Py_CheckInterval; Py_END_ALLOW_THREADS!!! The function PyEval_InitThreads() should be called only from - initthread() in "threadmodule.c". + init_thread() in "_threadmodule.c". Note that not yet all candidates have been converted to use this mechanism! diff --git a/Lib/_dummy_thread.py b/Lib/_dummy_thread.py new file mode 100644 index 0000000..352215a --- /dev/null +++ b/Lib/_dummy_thread.py @@ -0,0 +1,149 @@ +"""Drop-in replacement for the thread module. + +Meant to be used as a brain-dead substitute so that threaded code does +not need to be rewritten for when the thread module is not present. + +Suggested usage is:: + + try: + import _thread + except ImportError: + import _dummy_thread as _thread + +""" +# Exports only things specified by thread documentation; +# skipping obsolete synonyms allocate(), start_new(), exit_thread(). +__all__ = ['error', 'start_new_thread', 'exit', 'get_ident', 'allocate_lock', + 'interrupt_main', 'LockType'] + +import traceback as _traceback +import warnings + +class error(Exception): + """Dummy implementation of _thread.error.""" + + def __init__(self, *args): + self.args = args + +def start_new_thread(function, args, kwargs={}): + """Dummy implementation of _thread.start_new_thread(). + + Compatibility is maintained by making sure that ``args`` is a + tuple and ``kwargs`` is a dictionary. If an exception is raised + and it is SystemExit (which can be done by _thread.exit()) it is + caught and nothing is done; all other exceptions are printed out + by using traceback.print_exc(). + + If the executed function calls interrupt_main the KeyboardInterrupt will be + raised when the function returns. + + """ + if type(args) != type(tuple()): + raise TypeError("2nd arg must be a tuple") + if type(kwargs) != type(dict()): + raise TypeError("3rd arg must be a dict") + global _main + _main = False + try: + function(*args, **kwargs) + except SystemExit: + pass + except: + _traceback.print_exc() + _main = True + global _interrupt + if _interrupt: + _interrupt = False + raise KeyboardInterrupt + +def exit(): + """Dummy implementation of _thread.exit().""" + raise SystemExit + +def get_ident(): + """Dummy implementation of _thread.get_ident(). + + Since this module should only be used when _threadmodule is not + available, it is safe to assume that the current process is the + only thread. Thus a constant can be safely returned. + """ + return -1 + +def allocate_lock(): + """Dummy implementation of _thread.allocate_lock().""" + return LockType() + +def stack_size(size=None): + """Dummy implementation of _thread.stack_size().""" + if size is not None: + raise error("setting thread stack size not supported") + return 0 + +class LockType(object): + """Class implementing dummy implementation of _thread.LockType. + + Compatibility is maintained by maintaining self.locked_status + which is a boolean that stores the state of the lock. Pickling of + the lock, though, should not be done since if the _thread module is + then used with an unpickled ``lock()`` from here problems could + occur from this class not having atomic methods. + + """ + + def __init__(self): + self.locked_status = False + + def acquire(self, waitflag=None): + """Dummy implementation of acquire(). + + For blocking calls, self.locked_status is automatically set to + True and returned appropriately based on value of + ``waitflag``. If it is non-blocking, then the value is + actually checked and not set if it is already acquired. This + is all done so that threading.Condition's assert statements + aren't triggered and throw a little fit. + + """ + if waitflag is None: + self.locked_status = True + return None + elif not waitflag: + if not self.locked_status: + self.locked_status = True + return True + else: + return False + else: + self.locked_status = True + return True + + __enter__ = acquire + + def __exit__(self, typ, val, tb): + self.release() + + def release(self): + """Release the dummy lock.""" + # XXX Perhaps shouldn't actually bother to test? Could lead + # to problems for complex, threaded code. + if not self.locked_status: + raise error + self.locked_status = False + return True + + def locked(self): + return self.locked_status + +# Used to signal that interrupt_main was called in a "thread" +_interrupt = False +# True when not executing in a "thread" +_main = True + +def interrupt_main(): + """Set _interrupt flag to True to have start_new_thread raise + KeyboardInterrupt upon exiting.""" + if _main: + raise KeyboardInterrupt + else: + global _interrupt + _interrupt = True diff --git a/Lib/_strptime.py b/Lib/_strptime.py index b323e8f..c3568b0 100644 --- a/Lib/_strptime.py +++ b/Lib/_strptime.py @@ -18,9 +18,9 @@ from re import IGNORECASE from re import escape as re_escape from datetime import date as datetime_date try: - from thread import allocate_lock as _thread_allocate_lock + from _thread import allocate_lock as _thread_allocate_lock except: - from dummy_thread import allocate_lock as _thread_allocate_lock + from _dummy_thread import allocate_lock as _thread_allocate_lock __all__ = [] diff --git a/Lib/bsddb/__init__.py b/Lib/bsddb/__init__.py index b2786d3..0e68219 100644 --- a/Lib/bsddb/__init__.py +++ b/Lib/bsddb/__init__.py @@ -526,8 +526,8 @@ def _checkflag(flag, file): # BerkeleyDB was too. try: - import thread - del thread + import _thread + del _thread if db.version() < (3, 3, 0): db.DB_THREAD = 0 except ImportError: diff --git a/Lib/dummy_thread.py b/Lib/dummy_thread.py deleted file mode 100644 index c131384..0000000 --- a/Lib/dummy_thread.py +++ /dev/null @@ -1,149 +0,0 @@ -"""Drop-in replacement for the thread module. - -Meant to be used as a brain-dead substitute so that threaded code does -not need to be rewritten for when the thread module is not present. - -Suggested usage is:: - - try: - import thread - except ImportError: - import dummy_thread as thread - -""" -# Exports only things specified by thread documentation; -# skipping obsolete synonyms allocate(), start_new(), exit_thread(). -__all__ = ['error', 'start_new_thread', 'exit', 'get_ident', 'allocate_lock', - 'interrupt_main', 'LockType'] - -import traceback as _traceback -import warnings - -class error(Exception): - """Dummy implementation of thread.error.""" - - def __init__(self, *args): - self.args = args - -def start_new_thread(function, args, kwargs={}): - """Dummy implementation of thread.start_new_thread(). - - Compatibility is maintained by making sure that ``args`` is a - tuple and ``kwargs`` is a dictionary. If an exception is raised - and it is SystemExit (which can be done by thread.exit()) it is - caught and nothing is done; all other exceptions are printed out - by using traceback.print_exc(). - - If the executed function calls interrupt_main the KeyboardInterrupt will be - raised when the function returns. - - """ - if type(args) != type(tuple()): - raise TypeError("2nd arg must be a tuple") - if type(kwargs) != type(dict()): - raise TypeError("3rd arg must be a dict") - global _main - _main = False - try: - function(*args, **kwargs) - except SystemExit: - pass - except: - _traceback.print_exc() - _main = True - global _interrupt - if _interrupt: - _interrupt = False - raise KeyboardInterrupt - -def exit(): - """Dummy implementation of thread.exit().""" - raise SystemExit - -def get_ident(): - """Dummy implementation of thread.get_ident(). - - Since this module should only be used when threadmodule is not - available, it is safe to assume that the current process is the - only thread. Thus a constant can be safely returned. - """ - return -1 - -def allocate_lock(): - """Dummy implementation of thread.allocate_lock().""" - return LockType() - -def stack_size(size=None): - """Dummy implementation of thread.stack_size().""" - if size is not None: - raise error("setting thread stack size not supported") - return 0 - -class LockType(object): - """Class implementing dummy implementation of thread.LockType. - - Compatibility is maintained by maintaining self.locked_status - which is a boolean that stores the state of the lock. Pickling of - the lock, though, should not be done since if the thread module is - then used with an unpickled ``lock()`` from here problems could - occur from this class not having atomic methods. - - """ - - def __init__(self): - self.locked_status = False - - def acquire(self, waitflag=None): - """Dummy implementation of acquire(). - - For blocking calls, self.locked_status is automatically set to - True and returned appropriately based on value of - ``waitflag``. If it is non-blocking, then the value is - actually checked and not set if it is already acquired. This - is all done so that threading.Condition's assert statements - aren't triggered and throw a little fit. - - """ - if waitflag is None: - self.locked_status = True - return None - elif not waitflag: - if not self.locked_status: - self.locked_status = True - return True - else: - return False - else: - self.locked_status = True - return True - - __enter__ = acquire - - def __exit__(self, typ, val, tb): - self.release() - - def release(self): - """Release the dummy lock.""" - # XXX Perhaps shouldn't actually bother to test? Could lead - # to problems for complex, threaded code. - if not self.locked_status: - raise error - self.locked_status = False - return True - - def locked(self): - return self.locked_status - -# Used to signal that interrupt_main was called in a "thread" -_interrupt = False -# True when not executing in a "thread" -_main = True - -def interrupt_main(): - """Set _interrupt flag to True to have start_new_thread raise - KeyboardInterrupt upon exiting.""" - if _main: - raise KeyboardInterrupt - else: - global _interrupt - _interrupt = True diff --git a/Lib/dummy_threading.py b/Lib/dummy_threading.py index 81028a3..1bb7eee 100644 --- a/Lib/dummy_threading.py +++ b/Lib/dummy_threading.py @@ -3,12 +3,12 @@ The module ``_dummy_threading`` is added to ``sys.modules`` in order to not have ``threading`` considered imported. Had ``threading`` been directly imported it would have made all subsequent imports succeed -regardless of whether ``thread`` was available which is not desired. +regardless of whether ``_thread`` was available which is not desired. """ from sys import modules as sys_modules -import dummy_thread +import _dummy_thread # Declaring now so as to not have to nest ``try``s to get proper clean-up. holding_thread = False @@ -16,15 +16,15 @@ holding_threading = False holding__threading_local = False try: - # Could have checked if ``thread`` was not in sys.modules and gone + # Could have checked if ``_thread`` was not in sys.modules and gone # a different route, but decided to mirror technique used with # ``threading`` below. - if 'thread' in sys_modules: - held_thread = sys_modules['thread'] + if '_thread' in sys_modules: + held_thread = sys_modules['_thread'] holding_thread = True - # Must have some module named ``thread`` that implements its API + # Must have some module named ``_thread`` that implements its API # in order to initially import ``threading``. - sys_modules['thread'] = sys_modules['dummy_thread'] + sys_modules['_thread'] = sys_modules['_dummy_thread'] if 'threading' in sys_modules: # If ``threading`` is already imported, might as well prevent @@ -68,11 +68,11 @@ finally: # Put back ``thread`` if we overwrote, else del the entry we made if holding_thread: - sys_modules['thread'] = held_thread + sys_modules['_thread'] = held_thread del held_thread else: - del sys_modules['thread'] + del sys_modules['_thread'] del holding_thread - del dummy_thread + del _dummy_thread del sys_modules diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py index c9f5010..7a662c9 100644 --- a/Lib/idlelib/run.py +++ b/Lib/idlelib/run.py @@ -3,7 +3,7 @@ import linecache import time import socket import traceback -import thread +import _thread as thread import threading import queue diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index 92ab502..99e813b 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -35,7 +35,7 @@ except ImportError: codecs = None try: - import thread + import _thread as thread import threading except ImportError: thread = None diff --git a/Lib/logging/config.py b/Lib/logging/config.py index 006d669..73f3ecc 100644 --- a/Lib/logging/config.py +++ b/Lib/logging/config.py @@ -27,7 +27,7 @@ To use, simply 'import logging' and log away! import sys, logging, logging.handlers, socket, struct, os, traceback try: - import thread + import _thread as thread import threading except ImportError: thread = None diff --git a/Lib/mimetools.py b/Lib/mimetools.py index 0112f48..3ea5e1b 100644 --- a/Lib/mimetools.py +++ b/Lib/mimetools.py @@ -96,11 +96,11 @@ class Message(rfc822.Message): # ----------------- try: - import thread + import _thread except ImportError: - import dummy_thread as thread -_counter_lock = thread.allocate_lock() -del thread + import _dummy_thread as _thread +_counter_lock = _thread.allocate_lock() +del _thread _counter = 0 def _get_next_counter(): diff --git a/Lib/pydoc.py b/Lib/pydoc.py index 57bd5e2..140e1d8 100755 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -347,7 +347,7 @@ class Doc: if (isinstance(object, type(os)) and (object.__name__ in ('errno', 'exceptions', 'gc', 'imp', 'marshal', 'posix', 'signal', 'sys', - 'thread', 'zipimport') or + '_thread', 'zipimport') or (file.startswith(basedir) and not file.startswith(os.path.join(basedir, 'site-packages'))))): if docloc.startswith("http://"): diff --git a/Lib/telnetlib.py b/Lib/telnetlib.py index db37200..33029e1 100644 --- a/Lib/telnetlib.py +++ b/Lib/telnetlib.py @@ -548,8 +548,8 @@ class Telnet: def mt_interact(self): """Multithreaded version of interact().""" - import thread - thread.start_new_thread(self.listener, ()) + import _thread + _thread.start_new_thread(self.listener, ()) while 1: line = sys.stdin.readline() if not line: diff --git a/Lib/tempfile.py b/Lib/tempfile.py index 1041c14..2caa56a 100644 --- a/Lib/tempfile.py +++ b/Lib/tempfile.py @@ -52,9 +52,9 @@ else: try: - import thread as _thread + import _thread except ImportError: - import dummy_thread as _thread + import _dummy_thread as _thread _allocate_lock = _thread.allocate_lock _text_openflags = _os.O_RDWR | _os.O_CREAT | _os.O_EXCL diff --git a/Lib/test/crashers/multithreaded_close.py b/Lib/test/crashers/multithreaded_close.py index 5224341..f862d28 100644 --- a/Lib/test/crashers/multithreaded_close.py +++ b/Lib/test/crashers/multithreaded_close.py @@ -6,9 +6,9 @@ # http://bugs.python.org/issue595601 # http://bugs.python.org/issue815646 -import thread +import _thread while 1: f = open("multithreaded_close.tmp", "w") - thread.start_new_thread(f.close, ()) + _thread.start_new_thread(f.close, ()) f.close() diff --git a/Lib/test/fork_wait.py b/Lib/test/fork_wait.py index 678e3c4..03a4d6f 100644 --- a/Lib/test/fork_wait.py +++ b/Lib/test/fork_wait.py @@ -9,7 +9,7 @@ On some systems (e.g. Solaris without posix threads) we find that all active threads survive in the child after a fork(); this is an error. """ -import os, sys, time, thread, unittest +import os, sys, time, _thread, unittest LONGSLEEP = 2 SHORTSLEEP = 0.5 @@ -43,7 +43,7 @@ class ForkWait(unittest.TestCase): def test_wait(self): for i in range(NUM_THREADS): - thread.start_new(self.f, (i,)) + _thread.start_new(self.f, (i,)) time.sleep(LONGSLEEP) diff --git a/Lib/test/test___all__.py b/Lib/test/test___all__.py index c58aead..615f6c5 100644 --- a/Lib/test/test___all__.py +++ b/Lib/test/test___all__.py @@ -63,7 +63,7 @@ class AllTest(unittest.TestCase): self.check_all("dircache") self.check_all("dis") self.check_all("doctest") - self.check_all("dummy_thread") + self.check_all("_dummy_thread") self.check_all("dummy_threading") self.check_all("filecmp") self.check_all("fileinput") diff --git a/Lib/test/test_asynchat.py b/Lib/test/test_asynchat.py index 4d49f58..082fde9 100644 --- a/Lib/test/test_asynchat.py +++ b/Lib/test/test_asynchat.py @@ -1,6 +1,6 @@ # test asynchat -- requires threading -import thread # If this fails, we can't test this module +import _thread as thread # If this fails, we can't test this module import asyncore, asynchat, socket, threading, time import unittest import sys diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index df5ce0b..6c87645 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -22,13 +22,13 @@ def test_main(): idents = [] def callback(): - idents.append(thread.get_ident()) + idents.append(_thread.get_ident()) _testcapi._test_thread_state(callback) a = b = callback time.sleep(1) # Check our main thread is in the list exactly 3 times. - if idents.count(thread.get_ident()) != 3: + if idents.count(_thread.get_ident()) != 3: raise support.TestFailed( "Couldn't find main thread correctly in the list") @@ -39,11 +39,11 @@ def test_main(): have_thread_state = False if have_thread_state: - import thread + import _thread import time TestThreadState() import threading - t=threading.Thread(target=TestThreadState) + t = threading.Thread(target=TestThreadState) t.start() t.join() diff --git a/Lib/test/test_dummy_thread.py b/Lib/test/test_dummy_thread.py index 585711f..5b7db18 100644 --- a/Lib/test/test_dummy_thread.py +++ b/Lib/test/test_dummy_thread.py @@ -5,15 +5,15 @@ to be used, test_main() can be called with the module to use as the thread implementation as its sole argument. """ -import dummy_thread as _thread +import _dummy_thread as _thread import time import queue import random import unittest from test import support -DELAY = 0 # Set > 0 when testing a module other than dummy_thread, such as - # the 'thread' module. +DELAY = 0 # Set > 0 when testing a module other than _dummy_thread, such as + # the '_thread' module. class LockTests(unittest.TestCase): """Test lock objects.""" diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 349548f..d12968c 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -6,7 +6,8 @@ from test import support import errno import socket import select -import thread, threading +import _thread as thread +import threading import time import traceback import queue diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 7c66e0d..231b460 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -191,7 +191,7 @@ class SysModuleTest(unittest.TestCase): def test_current_frames(self): have_threads = True try: - import thread + import _thread except ImportError: have_threads = False @@ -202,7 +202,7 @@ class SysModuleTest(unittest.TestCase): # Test sys._current_frames() in a WITH_THREADS build. def current_frames_with_threads(self): - import threading, thread + import threading, _thread import traceback # Spawn a thread that blocks at a known place. Then the main @@ -216,7 +216,7 @@ class SysModuleTest(unittest.TestCase): g456() def g456(): - thread_info.append(thread.get_ident()) + thread_info.append(_thread.get_ident()) entered_g.set() leave_g.wait() @@ -232,7 +232,7 @@ class SysModuleTest(unittest.TestCase): d = sys._current_frames() - main_id = thread.get_ident() + main_id = _thread.get_ident() self.assert_(main_id in d) self.assert_(thread_id in d) diff --git a/Lib/test/test_thread.py b/Lib/test/test_thread.py index cff9f81..84d5a9d 100644 --- a/Lib/test/test_thread.py +++ b/Lib/test/test_thread.py @@ -2,7 +2,7 @@ import os import unittest import random from test import support -import thread +import _thread as thread import time diff --git a/Lib/test/test_threaded_import.py b/Lib/test/test_threaded_import.py index fbe479c..67d1505 100644 --- a/Lib/test/test_threaded_import.py +++ b/Lib/test/test_threaded_import.py @@ -5,7 +5,7 @@ # complains several times about module random having no attribute # randrange, and then Python hangs. -import thread +import _thread as thread from test.support import verbose, TestSkipped, TestFailed critical_section = thread.allocate_lock() diff --git a/Lib/test/test_threadedtempfile.py b/Lib/test/test_threadedtempfile.py index fb122d0..d93b382 100644 --- a/Lib/test/test_threadedtempfile.py +++ b/Lib/test/test_threadedtempfile.py @@ -16,7 +16,7 @@ provoking a 2.0 failure under Linux. NUM_THREADS = 20 FILES_PER_THREAD = 50 -import thread # If this fails, we can't test this module +import _thread as thread # If this fails, we can't test this module import threading import tempfile diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 9582dde..3c09b0b 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -5,7 +5,7 @@ from test.support import verbose import random import sys import threading -import thread +import _thread import time import unittest import weakref @@ -88,7 +88,7 @@ class ThreadTests(unittest.TestCase): print('with 256kB thread stack size...') try: threading.stack_size(262144) - except thread.error: + except _thread.error: if verbose: print('platform does not support changing thread stack size') return @@ -101,7 +101,7 @@ class ThreadTests(unittest.TestCase): print('with 1MB thread stack size...') try: threading.stack_size(0x100000) - except thread.error: + except _thread.error: if verbose: print('platform does not support changing thread stack size') return @@ -120,7 +120,7 @@ class ThreadTests(unittest.TestCase): mutex = threading.Lock() mutex.acquire() - tid = thread.start_new_thread(f, (mutex,)) + tid = _thread.start_new_thread(f, (mutex,)) # Wait for the thread to finish. mutex.acquire() self.assert_(tid in threading._active) @@ -154,7 +154,7 @@ class ThreadTests(unittest.TestCase): class Worker(threading.Thread): def run(self): - self.id = thread.get_ident() + self.id = _thread.get_ident() self.finished = False try: @@ -211,10 +211,10 @@ class ThreadTests(unittest.TestCase): import subprocess rc = subprocess.call([sys.executable, "-c", """if 1: - import ctypes, sys, time, thread + import ctypes, sys, time, _thread # This lock is used as a simple event variable. - ready = thread.allocate_lock() + ready = _thread.allocate_lock() ready.acquire() # Module globals are cleared before __del__ is run @@ -231,7 +231,7 @@ class ThreadTests(unittest.TestCase): ready.release() time.sleep(100) - thread.start_new_thread(waitingThread, ()) + _thread.start_new_thread(waitingThread, ()) ready.acquire() # Be sure the other thread is waiting. sys.exit(42) """]) @@ -357,7 +357,7 @@ class ThreadingExceptionTests(unittest.TestCase): def test_main(): test.support.run_unittest(ThreadTests, - ThreadingExceptionTests) + ThreadingExceptionTests) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_threadsignals.py b/Lib/test/test_threadsignals.py index cbed42c..14bd3b7 100644 --- a/Lib/test/test_threadsignals.py +++ b/Lib/test/test_threadsignals.py @@ -1,7 +1,7 @@ """PyUnit testing that threads honor our signal semantics""" import unittest -import thread +import _thread as thread import signal import os import sys diff --git a/Lib/threading.py b/Lib/threading.py index be97807..673d88e 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -1,12 +1,7 @@ """Thread module emulating a subset of Java's threading model.""" import sys as _sys - -try: - import thread -except ImportError: - del _sys.modules[__name__] - raise +import _thread from time import time as _time, sleep as _sleep from traceback import format_exc as _format_exc @@ -17,11 +12,11 @@ __all__ = ['activeCount', 'Condition', 'currentThread', 'enumerate', 'Event', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Thread', 'Timer', 'setprofile', 'settrace', 'local', 'stack_size'] -_start_new_thread = thread.start_new_thread -_allocate_lock = thread.allocate_lock -_get_ident = thread.get_ident -ThreadError = thread.error -del thread +_start_new_thread = _thread.start_new_thread +_allocate_lock = _thread.allocate_lock +_get_ident = _thread.get_ident +ThreadError = _thread.error +del _thread # Debug support (adapted from ihooks.py). @@ -556,18 +551,18 @@ class Thread(_Verbose): def _delete(self): "Remove current thread from the dict of currently running threads." - # Notes about running with dummy_thread: + # Notes about running with _dummy_thread: # - # Must take care to not raise an exception if dummy_thread is being + # Must take care to not raise an exception if _dummy_thread is being # used (and thus this module is being used as an instance of - # dummy_threading). dummy_thread.get_ident() always returns -1 since - # there is only one thread if dummy_thread is being used. Thus + # dummy_threading). _dummy_thread.get_ident() always returns -1 since + # there is only one thread if _dummy_thread is being used. Thus # len(_active) is always <= 1 here, and any Thread instance created # overwrites the (if any) thread currently registered in _active. # # An instance of _MainThread is always created by 'threading'. This # gets overwritten the instant an instance of Thread is created; both - # threads return -1 from dummy_thread.get_ident() and thus have the + # threads return -1 from _dummy_thread.get_ident() and thus have the # same key in the dict. So when the _MainThread instance created by # 'threading' tries to clean itself up when atexit calls this method # it gets a KeyError if another Thread instance was created. @@ -763,7 +758,7 @@ def enumerate(): _active_limbo_lock.release() return active -from thread import stack_size +from _thread import stack_size # Create the main thread object, # and make it available for the interpreter @@ -775,7 +770,7 @@ _shutdown = _MainThread()._exitfunc # module, or from the python fallback try: - from thread import _local as local + from _thread import _local as local except ImportError: from _threading_local import local diff --git a/Misc/cheatsheet b/Misc/cheatsheet index d81897f..c383e4a 100644 --- a/Misc/cheatsheet +++ b/Misc/cheatsheet @@ -1988,8 +1988,8 @@ zipfile Read & write PK zipped files. re Functions useful for working with regular expressions string Useful string and characters functions and exceptions random Mersenne Twister pseudo-random number generator - thread Low-level primitives for working with process threads - threading idem, new recommanded interface. + _thread Low-level primitives for working with process threads + threading idem, new recommended interface. * Unix/Posix * diff --git a/Modules/Setup.config.in b/Modules/Setup.config.in index fed62e9..5ac2404 100644 --- a/Modules/Setup.config.in +++ b/Modules/Setup.config.in @@ -4,7 +4,7 @@ # based on configure-time options. # Threading -@USE_THREAD_MODULE@thread threadmodule.c +@USE_THREAD_MODULE@_thread _threadmodule.c # The signal module @USE_SIGNAL_MODULE@signal signalmodule.c diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c new file mode 100644 index 0000000..971ff56 --- /dev/null +++ b/Modules/_threadmodule.c @@ -0,0 +1,720 @@ + +/* Thread module */ +/* Interface to Sjoerd's portable C thread library */ + +#include "Python.h" + +#ifndef WITH_THREAD +#error "Error! The rest of Python is not compiled with thread support." +#error "Rerun configure, adding a --with-threads option." +#error "Then run `make clean' followed by `make'." +#endif + +#include "pythread.h" + +static PyObject *ThreadError; + + +/* Lock objects */ + +typedef struct { + PyObject_HEAD + PyThread_type_lock lock_lock; +} lockobject; + +static void +lock_dealloc(lockobject *self) +{ + assert(self->lock_lock); + /* Unlock the lock so it's safe to free it */ + PyThread_acquire_lock(self->lock_lock, 0); + PyThread_release_lock(self->lock_lock); + + PyThread_free_lock(self->lock_lock); + PyObject_Del(self); +} + +static PyObject * +lock_PyThread_acquire_lock(lockobject *self, PyObject *args) +{ + int i = 1; + + if (!PyArg_ParseTuple(args, "|i:acquire", &i)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + i = PyThread_acquire_lock(self->lock_lock, i); + Py_END_ALLOW_THREADS + + return PyBool_FromLong((long)i); +} + +PyDoc_STRVAR(acquire_doc, +"acquire([wait]) -> None or bool\n\ +(acquire_lock() is an obsolete synonym)\n\ +\n\ +Lock the lock. Without argument, this blocks if the lock is already\n\ +locked (even by the same thread), waiting for another thread to release\n\ +the lock, and return None once the lock is acquired.\n\ +With an argument, this will only block if the argument is true,\n\ +and the return value reflects whether the lock is acquired.\n\ +The blocking operation is not interruptible."); + +static PyObject * +lock_PyThread_release_lock(lockobject *self) +{ + /* Sanity check: the lock must be locked */ + if (PyThread_acquire_lock(self->lock_lock, 0)) { + PyThread_release_lock(self->lock_lock); + PyErr_SetString(ThreadError, "release unlocked lock"); + return NULL; + } + + PyThread_release_lock(self->lock_lock); + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(release_doc, +"release()\n\ +(release_lock() is an obsolete synonym)\n\ +\n\ +Release the lock, allowing another thread that is blocked waiting for\n\ +the lock to acquire the lock. The lock must be in the locked state,\n\ +but it needn't be locked by the same thread that unlocks it."); + +static PyObject * +lock_locked_lock(lockobject *self) +{ + if (PyThread_acquire_lock(self->lock_lock, 0)) { + PyThread_release_lock(self->lock_lock); + return PyBool_FromLong(0L); + } + return PyBool_FromLong(1L); +} + +PyDoc_STRVAR(locked_doc, +"locked() -> bool\n\ +(locked_lock() is an obsolete synonym)\n\ +\n\ +Return whether the lock is in the locked state."); + +static PyMethodDef lock_methods[] = { + {"acquire_lock", (PyCFunction)lock_PyThread_acquire_lock, + METH_VARARGS, acquire_doc}, + {"acquire", (PyCFunction)lock_PyThread_acquire_lock, + METH_VARARGS, acquire_doc}, + {"release_lock", (PyCFunction)lock_PyThread_release_lock, + METH_NOARGS, release_doc}, + {"release", (PyCFunction)lock_PyThread_release_lock, + METH_NOARGS, release_doc}, + {"locked_lock", (PyCFunction)lock_locked_lock, + METH_NOARGS, locked_doc}, + {"locked", (PyCFunction)lock_locked_lock, + METH_NOARGS, locked_doc}, + {"__enter__", (PyCFunction)lock_PyThread_acquire_lock, + METH_VARARGS, acquire_doc}, + {"__exit__", (PyCFunction)lock_PyThread_release_lock, + METH_VARARGS, release_doc}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +lock_getattr(lockobject *self, char *name) +{ + return Py_FindMethod(lock_methods, (PyObject *)self, name); +} + +static PyTypeObject Locktype = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "thread.lock", /*tp_name*/ + sizeof(lockobject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)lock_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)lock_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ +}; + +static lockobject * +newlockobject(void) +{ + lockobject *self; + self = PyObject_New(lockobject, &Locktype); + if (self == NULL) + return NULL; + self->lock_lock = PyThread_allocate_lock(); + if (self->lock_lock == NULL) { + PyObject_Del(self); + self = NULL; + PyErr_SetString(ThreadError, "can't allocate lock"); + } + return self; +} + +/* Thread-local objects */ + +#include "structmember.h" + +typedef struct { + PyObject_HEAD + PyObject *key; + PyObject *args; + PyObject *kw; + PyObject *dict; +} localobject; + +static PyObject * +local_new(PyTypeObject *type, PyObject *args, PyObject *kw) +{ + localobject *self; + PyObject *tdict; + + if (type->tp_init == PyBaseObject_Type.tp_init + && ((args && PyObject_IsTrue(args)) + || (kw && PyObject_IsTrue(kw)))) { + PyErr_SetString(PyExc_TypeError, + "Initialization arguments are not supported"); + return NULL; + } + + self = (localobject *)type->tp_alloc(type, 0); + if (self == NULL) + return NULL; + + Py_XINCREF(args); + self->args = args; + Py_XINCREF(kw); + self->kw = kw; + self->dict = NULL; /* making sure */ + self->key = PyUnicode_FromFormat("thread.local.%p", self); + if (self->key == NULL) + goto err; + + self->dict = PyDict_New(); + if (self->dict == NULL) + goto err; + + tdict = PyThreadState_GetDict(); + if (tdict == NULL) { + PyErr_SetString(PyExc_SystemError, + "Couldn't get thread-state dictionary"); + goto err; + } + + if (PyDict_SetItem(tdict, self->key, self->dict) < 0) + goto err; + + return (PyObject *)self; + + err: + Py_DECREF(self); + return NULL; +} + +static int +local_traverse(localobject *self, visitproc visit, void *arg) +{ + Py_VISIT(self->args); + Py_VISIT(self->kw); + Py_VISIT(self->dict); + return 0; +} + +static int +local_clear(localobject *self) +{ + Py_CLEAR(self->key); + Py_CLEAR(self->args); + Py_CLEAR(self->kw); + Py_CLEAR(self->dict); + return 0; +} + +static void +local_dealloc(localobject *self) +{ + PyThreadState *tstate; + if (self->key + && (tstate = PyThreadState_Get()) + && tstate->interp) { + for(tstate = PyInterpreterState_ThreadHead(tstate->interp); + tstate; + tstate = PyThreadState_Next(tstate)) + if (tstate->dict && + PyDict_GetItem(tstate->dict, self->key)) + PyDict_DelItem(tstate->dict, self->key); + } + + local_clear(self); + Py_TYPE(self)->tp_free((PyObject*)self); +} + +static PyObject * +_ldict(localobject *self) +{ + PyObject *tdict, *ldict; + + tdict = PyThreadState_GetDict(); + if (tdict == NULL) { + PyErr_SetString(PyExc_SystemError, + "Couldn't get thread-state dictionary"); + return NULL; + } + + ldict = PyDict_GetItem(tdict, self->key); + if (ldict == NULL) { + ldict = PyDict_New(); /* we own ldict */ + + if (ldict == NULL) + return NULL; + else { + int i = PyDict_SetItem(tdict, self->key, ldict); + Py_DECREF(ldict); /* now ldict is borrowed */ + if (i < 0) + return NULL; + } + + Py_CLEAR(self->dict); + Py_INCREF(ldict); + self->dict = ldict; /* still borrowed */ + + if (Py_TYPE(self)->tp_init != PyBaseObject_Type.tp_init && + Py_TYPE(self)->tp_init((PyObject*)self, + self->args, self->kw) < 0) { + /* we need to get rid of ldict from thread so + we create a new one the next time we do an attr + acces */ + PyDict_DelItem(tdict, self->key); + return NULL; + } + + } + else if (self->dict != ldict) { + Py_CLEAR(self->dict); + Py_INCREF(ldict); + self->dict = ldict; + } + + return ldict; +} + +static int +local_setattro(localobject *self, PyObject *name, PyObject *v) +{ + PyObject *ldict; + + ldict = _ldict(self); + if (ldict == NULL) + return -1; + + return PyObject_GenericSetAttr((PyObject *)self, name, v); +} + +static PyObject * +local_getdict(localobject *self, void *closure) +{ + if (self->dict == NULL) { + PyErr_SetString(PyExc_AttributeError, "__dict__"); + return NULL; + } + + Py_INCREF(self->dict); + return self->dict; +} + +static PyGetSetDef local_getset[] = { + {"__dict__", (getter)local_getdict, (setter)NULL, + "Local-data dictionary", NULL}, + {NULL} /* Sentinel */ +}; + +static PyObject *local_getattro(localobject *, PyObject *); + +static PyTypeObject localtype = { + PyVarObject_HEAD_INIT(NULL, 0) + /* tp_name */ "thread._local", + /* tp_basicsize */ sizeof(localobject), + /* tp_itemsize */ 0, + /* tp_dealloc */ (destructor)local_dealloc, + /* tp_print */ 0, + /* tp_getattr */ 0, + /* tp_setattr */ 0, + /* tp_compare */ 0, + /* tp_repr */ 0, + /* tp_as_number */ 0, + /* tp_as_sequence */ 0, + /* tp_as_mapping */ 0, + /* tp_hash */ 0, + /* tp_call */ 0, + /* tp_str */ 0, + /* tp_getattro */ (getattrofunc)local_getattro, + /* tp_setattro */ (setattrofunc)local_setattro, + /* tp_as_buffer */ 0, + /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + /* tp_doc */ "Thread-local data", + /* tp_traverse */ (traverseproc)local_traverse, + /* tp_clear */ (inquiry)local_clear, + /* tp_richcompare */ 0, + /* tp_weaklistoffset */ 0, + /* tp_iter */ 0, + /* tp_iternext */ 0, + /* tp_methods */ 0, + /* tp_members */ 0, + /* tp_getset */ local_getset, + /* tp_base */ 0, + /* tp_dict */ 0, /* internal use */ + /* tp_descr_get */ 0, + /* tp_descr_set */ 0, + /* tp_dictoffset */ offsetof(localobject, dict), + /* tp_init */ 0, + /* tp_alloc */ 0, + /* tp_new */ local_new, + /* tp_free */ 0, /* Low-level free-mem routine */ + /* tp_is_gc */ 0, /* For PyObject_IS_GC */ +}; + +static PyObject * +local_getattro(localobject *self, PyObject *name) +{ + PyObject *ldict, *value; + + ldict = _ldict(self); + if (ldict == NULL) + return NULL; + + if (Py_TYPE(self) != &localtype) + /* use generic lookup for subtypes */ + return PyObject_GenericGetAttr((PyObject *)self, name); + + /* Optimization: just look in dict ourselves */ + value = PyDict_GetItem(ldict, name); + if (value == NULL) + /* Fall back on generic to get __class__ and __dict__ */ + return PyObject_GenericGetAttr((PyObject *)self, name); + + Py_INCREF(value); + return value; +} + +/* Module functions */ + +struct bootstate { + PyInterpreterState *interp; + PyObject *func; + PyObject *args; + PyObject *keyw; +}; + +static void +t_bootstrap(void *boot_raw) +{ + struct bootstate *boot = (struct bootstate *) boot_raw; + PyThreadState *tstate; + PyObject *res; + + tstate = PyThreadState_New(boot->interp); + + PyEval_AcquireThread(tstate); + res = PyEval_CallObjectWithKeywords( + boot->func, boot->args, boot->keyw); + if (res == NULL) { + if (PyErr_ExceptionMatches(PyExc_SystemExit)) + PyErr_Clear(); + else { + PyObject *file; + PySys_WriteStderr( + "Unhandled exception in thread started by "); + file = PySys_GetObject("stderr"); + if (file != NULL && file != Py_None) + PyFile_WriteObject(boot->func, file, 0); + else + PyObject_Print(boot->func, stderr, 0); + PySys_WriteStderr("\n"); + PyErr_PrintEx(0); + } + } + else + Py_DECREF(res); + Py_DECREF(boot->func); + Py_DECREF(boot->args); + Py_XDECREF(boot->keyw); + PyMem_DEL(boot_raw); + PyThreadState_Clear(tstate); + PyThreadState_DeleteCurrent(); + PyThread_exit_thread(); +} + +static PyObject * +thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs) +{ + PyObject *func, *args, *keyw = NULL; + struct bootstate *boot; + long ident; + + if (!PyArg_UnpackTuple(fargs, "start_new_thread", 2, 3, + &func, &args, &keyw)) + return NULL; + if (!PyCallable_Check(func)) { + PyErr_SetString(PyExc_TypeError, + "first arg must be callable"); + return NULL; + } + if (!PyTuple_Check(args)) { + PyErr_SetString(PyExc_TypeError, + "2nd arg must be a tuple"); + return NULL; + } + if (keyw != NULL && !PyDict_Check(keyw)) { + PyErr_SetString(PyExc_TypeError, + "optional 3rd arg must be a dictionary"); + return NULL; + } + boot = PyMem_NEW(struct bootstate, 1); + if (boot == NULL) + return PyErr_NoMemory(); + boot->interp = PyThreadState_GET()->interp; + boot->func = func; + boot->args = args; + boot->keyw = keyw; + Py_INCREF(func); + Py_INCREF(args); + Py_XINCREF(keyw); + PyEval_InitThreads(); /* Start the interpreter's thread-awareness */ + ident = PyThread_start_new_thread(t_bootstrap, (void*) boot); + if (ident == -1) { + PyErr_SetString(ThreadError, "can't start new thread"); + Py_DECREF(func); + Py_DECREF(args); + Py_XDECREF(keyw); + PyMem_DEL(boot); + return NULL; + } + return PyLong_FromLong(ident); +} + +PyDoc_STRVAR(start_new_doc, +"start_new_thread(function, args[, kwargs])\n\ +(start_new() is an obsolete synonym)\n\ +\n\ +Start a new thread and return its identifier. The thread will call the\n\ +function with positional arguments from the tuple args and keyword arguments\n\ +taken from the optional dictionary kwargs. The thread exits when the\n\ +function returns; the return value is ignored. The thread will also exit\n\ +when the function raises an unhandled exception; a stack trace will be\n\ +printed unless the exception is SystemExit.\n"); + +static PyObject * +thread_PyThread_exit_thread(PyObject *self) +{ + PyErr_SetNone(PyExc_SystemExit); + return NULL; +} + +PyDoc_STRVAR(exit_doc, +"exit()\n\ +(PyThread_exit_thread() is an obsolete synonym)\n\ +\n\ +This is synonymous to ``raise SystemExit''. It will cause the current\n\ +thread to exit silently unless the exception is caught."); + +static PyObject * +thread_PyThread_interrupt_main(PyObject * self) +{ + PyErr_SetInterrupt(); + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(interrupt_doc, +"interrupt_main()\n\ +\n\ +Raise a KeyboardInterrupt in the main thread.\n\ +A subthread can use this function to interrupt the main thread." +); + +#ifndef NO_EXIT_PROG +static PyObject * +thread_PyThread_exit_prog(PyObject *self, PyObject *args) +{ + int sts; + if (!PyArg_ParseTuple(args, "i:exit_prog", &sts)) + return NULL; + Py_Exit(sts); /* Calls PyThread_exit_prog(sts) or _PyThread_exit_prog(sts) */ + for (;;) { } /* Should not be reached */ +} +#endif + +static lockobject *newlockobject(void); + +static PyObject * +thread_PyThread_allocate_lock(PyObject *self) +{ + return (PyObject *) newlockobject(); +} + +PyDoc_STRVAR(allocate_doc, +"allocate_lock() -> lock object\n\ +(allocate() is an obsolete synonym)\n\ +\n\ +Create a new lock object. See LockType.__doc__ for information about locks."); + +static PyObject * +thread_get_ident(PyObject *self) +{ + long ident; + ident = PyThread_get_thread_ident(); + if (ident == -1) { + PyErr_SetString(ThreadError, "no current thread ident"); + return NULL; + } + return PyLong_FromLong(ident); +} + +PyDoc_STRVAR(get_ident_doc, +"get_ident() -> integer\n\ +\n\ +Return a non-zero integer that uniquely identifies the current thread\n\ +amongst other threads that exist simultaneously.\n\ +This may be used to identify per-thread resources.\n\ +Even though on some platforms threads identities may appear to be\n\ +allocated consecutive numbers starting at 1, this behavior should not\n\ +be relied upon, and the number should be seen purely as a magic cookie.\n\ +A thread's identity may be reused for another thread after it exits."); + +static PyObject * +thread_stack_size(PyObject *self, PyObject *args) +{ + size_t old_size; + Py_ssize_t new_size = 0; + int rc; + + if (!PyArg_ParseTuple(args, "|n:stack_size", &new_size)) + return NULL; + + if (new_size < 0) { + PyErr_SetString(PyExc_ValueError, + "size must be 0 or a positive value"); + return NULL; + } + + old_size = PyThread_get_stacksize(); + + rc = PyThread_set_stacksize((size_t) new_size); + if (rc == -1) { + PyErr_Format(PyExc_ValueError, + "size not valid: %zd bytes", + new_size); + return NULL; + } + if (rc == -2) { + PyErr_SetString(ThreadError, + "setting stack size not supported"); + return NULL; + } + + return PyLong_FromSsize_t((Py_ssize_t) old_size); +} + +PyDoc_STRVAR(stack_size_doc, +"stack_size([size]) -> size\n\ +\n\ +Return the thread stack size used when creating new threads. The\n\ +optional size argument specifies the stack size (in bytes) to be used\n\ +for subsequently created threads, and must be 0 (use platform or\n\ +configured default) or a positive integer value of at least 32,768 (32k).\n\ +If changing the thread stack size is unsupported, a ThreadError\n\ +exception is raised. If the specified size is invalid, a ValueError\n\ +exception is raised, and the stack size is unmodified. 32k bytes\n\ + currently the minimum supported stack size value to guarantee\n\ +sufficient stack space for the interpreter itself.\n\ +\n\ +Note that some platforms may have particular restrictions on values for\n\ +the stack size, such as requiring a minimum stack size larger than 32kB or\n\ +requiring allocation in multiples of the system memory page size\n\ +- platform documentation should be referred to for more information\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 PyMethodDef thread_methods[] = { + {"start_new_thread", (PyCFunction)thread_PyThread_start_new_thread, + METH_VARARGS, + start_new_doc}, + {"start_new", (PyCFunction)thread_PyThread_start_new_thread, + METH_VARARGS, + start_new_doc}, + {"allocate_lock", (PyCFunction)thread_PyThread_allocate_lock, + METH_NOARGS, allocate_doc}, + {"allocate", (PyCFunction)thread_PyThread_allocate_lock, + METH_NOARGS, allocate_doc}, + {"exit_thread", (PyCFunction)thread_PyThread_exit_thread, + METH_NOARGS, exit_doc}, + {"exit", (PyCFunction)thread_PyThread_exit_thread, + METH_NOARGS, exit_doc}, + {"interrupt_main", (PyCFunction)thread_PyThread_interrupt_main, + METH_NOARGS, interrupt_doc}, + {"get_ident", (PyCFunction)thread_get_ident, + METH_NOARGS, get_ident_doc}, + {"stack_size", (PyCFunction)thread_stack_size, + METH_VARARGS, + stack_size_doc}, +#ifndef NO_EXIT_PROG + {"exit_prog", (PyCFunction)thread_PyThread_exit_prog, + METH_VARARGS}, +#endif + {NULL, NULL} /* sentinel */ +}; + + +/* Initialization function */ + +PyDoc_STRVAR(thread_doc, +"This module provides primitive operations to write multi-threaded programs.\n\ +The 'threading' module provides a more convenient interface."); + +PyDoc_STRVAR(lock_doc, +"A lock object is a synchronization primitive. To create a lock,\n\ +call the PyThread_allocate_lock() function. Methods are:\n\ +\n\ +acquire() -- lock the lock, possibly blocking until it can be obtained\n\ +release() -- unlock of the lock\n\ +locked() -- test whether the lock is currently locked\n\ +\n\ +A lock is not owned by the thread that locked it; another thread may\n\ +unlock it. A thread attempting to lock a lock that it has already locked\n\ +will block until another thread unlocks it. Deadlocks may ensue."); + +PyMODINIT_FUNC +init_thread(void) +{ + PyObject *m, *d; + + /* Initialize types: */ + if (PyType_Ready(&localtype) < 0) + return; + if (PyType_Ready(&Locktype) < 0) + return; + + /* Create the module and add the functions */ + m = Py_InitModule3("_thread", thread_methods, thread_doc); + if (m == NULL) + return; + + /* Add a symbolic constant */ + d = PyModule_GetDict(m); + ThreadError = PyErr_NewException("_thread.error", NULL, NULL); + PyDict_SetItemString(d, "error", ThreadError); + Locktype.tp_doc = lock_doc; + Py_INCREF(&Locktype); + PyDict_SetItemString(d, "LockType", (PyObject *)&Locktype); + + Py_INCREF(&localtype); + if (PyModule_AddObject(m, "_local", (PyObject *)&localtype) < 0) + return; + + /* Initialize the C thread library */ + PyThread_init_thread(); +} diff --git a/Modules/threadmodule.c b/Modules/threadmodule.c deleted file mode 100644 index b8b7fb5..0000000 --- a/Modules/threadmodule.c +++ /dev/null @@ -1,720 +0,0 @@ - -/* Thread module */ -/* Interface to Sjoerd's portable C thread library */ - -#include "Python.h" - -#ifndef WITH_THREAD -#error "Error! The rest of Python is not compiled with thread support." -#error "Rerun configure, adding a --with-threads option." -#error "Then run `make clean' followed by `make'." -#endif - -#include "pythread.h" - -static PyObject *ThreadError; - - -/* Lock objects */ - -typedef struct { - PyObject_HEAD - PyThread_type_lock lock_lock; -} lockobject; - -static void -lock_dealloc(lockobject *self) -{ - assert(self->lock_lock); - /* Unlock the lock so it's safe to free it */ - PyThread_acquire_lock(self->lock_lock, 0); - PyThread_release_lock(self->lock_lock); - - PyThread_free_lock(self->lock_lock); - PyObject_Del(self); -} - -static PyObject * -lock_PyThread_acquire_lock(lockobject *self, PyObject *args) -{ - int i = 1; - - if (!PyArg_ParseTuple(args, "|i:acquire", &i)) - return NULL; - - Py_BEGIN_ALLOW_THREADS - i = PyThread_acquire_lock(self->lock_lock, i); - Py_END_ALLOW_THREADS - - return PyBool_FromLong((long)i); -} - -PyDoc_STRVAR(acquire_doc, -"acquire([wait]) -> None or bool\n\ -(acquire_lock() is an obsolete synonym)\n\ -\n\ -Lock the lock. Without argument, this blocks if the lock is already\n\ -locked (even by the same thread), waiting for another thread to release\n\ -the lock, and return None once the lock is acquired.\n\ -With an argument, this will only block if the argument is true,\n\ -and the return value reflects whether the lock is acquired.\n\ -The blocking operation is not interruptible."); - -static PyObject * -lock_PyThread_release_lock(lockobject *self) -{ - /* Sanity check: the lock must be locked */ - if (PyThread_acquire_lock(self->lock_lock, 0)) { - PyThread_release_lock(self->lock_lock); - PyErr_SetString(ThreadError, "release unlocked lock"); - return NULL; - } - - PyThread_release_lock(self->lock_lock); - Py_INCREF(Py_None); - return Py_None; -} - -PyDoc_STRVAR(release_doc, -"release()\n\ -(release_lock() is an obsolete synonym)\n\ -\n\ -Release the lock, allowing another thread that is blocked waiting for\n\ -the lock to acquire the lock. The lock must be in the locked state,\n\ -but it needn't be locked by the same thread that unlocks it."); - -static PyObject * -lock_locked_lock(lockobject *self) -{ - if (PyThread_acquire_lock(self->lock_lock, 0)) { - PyThread_release_lock(self->lock_lock); - return PyBool_FromLong(0L); - } - return PyBool_FromLong(1L); -} - -PyDoc_STRVAR(locked_doc, -"locked() -> bool\n\ -(locked_lock() is an obsolete synonym)\n\ -\n\ -Return whether the lock is in the locked state."); - -static PyMethodDef lock_methods[] = { - {"acquire_lock", (PyCFunction)lock_PyThread_acquire_lock, - METH_VARARGS, acquire_doc}, - {"acquire", (PyCFunction)lock_PyThread_acquire_lock, - METH_VARARGS, acquire_doc}, - {"release_lock", (PyCFunction)lock_PyThread_release_lock, - METH_NOARGS, release_doc}, - {"release", (PyCFunction)lock_PyThread_release_lock, - METH_NOARGS, release_doc}, - {"locked_lock", (PyCFunction)lock_locked_lock, - METH_NOARGS, locked_doc}, - {"locked", (PyCFunction)lock_locked_lock, - METH_NOARGS, locked_doc}, - {"__enter__", (PyCFunction)lock_PyThread_acquire_lock, - METH_VARARGS, acquire_doc}, - {"__exit__", (PyCFunction)lock_PyThread_release_lock, - METH_VARARGS, release_doc}, - {NULL, NULL} /* sentinel */ -}; - -static PyObject * -lock_getattr(lockobject *self, char *name) -{ - return Py_FindMethod(lock_methods, (PyObject *)self, name); -} - -static PyTypeObject Locktype = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - "thread.lock", /*tp_name*/ - sizeof(lockobject), /*tp_size*/ - 0, /*tp_itemsize*/ - /* methods */ - (destructor)lock_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - (getattrfunc)lock_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ -}; - -static lockobject * -newlockobject(void) -{ - lockobject *self; - self = PyObject_New(lockobject, &Locktype); - if (self == NULL) - return NULL; - self->lock_lock = PyThread_allocate_lock(); - if (self->lock_lock == NULL) { - PyObject_Del(self); - self = NULL; - PyErr_SetString(ThreadError, "can't allocate lock"); - } - return self; -} - -/* Thread-local objects */ - -#include "structmember.h" - -typedef struct { - PyObject_HEAD - PyObject *key; - PyObject *args; - PyObject *kw; - PyObject *dict; -} localobject; - -static PyObject * -local_new(PyTypeObject *type, PyObject *args, PyObject *kw) -{ - localobject *self; - PyObject *tdict; - - if (type->tp_init == PyBaseObject_Type.tp_init - && ((args && PyObject_IsTrue(args)) - || (kw && PyObject_IsTrue(kw)))) { - PyErr_SetString(PyExc_TypeError, - "Initialization arguments are not supported"); - return NULL; - } - - self = (localobject *)type->tp_alloc(type, 0); - if (self == NULL) - return NULL; - - Py_XINCREF(args); - self->args = args; - Py_XINCREF(kw); - self->kw = kw; - self->dict = NULL; /* making sure */ - self->key = PyUnicode_FromFormat("thread.local.%p", self); - if (self->key == NULL) - goto err; - - self->dict = PyDict_New(); - if (self->dict == NULL) - goto err; - - tdict = PyThreadState_GetDict(); - if (tdict == NULL) { - PyErr_SetString(PyExc_SystemError, - "Couldn't get thread-state dictionary"); - goto err; - } - - if (PyDict_SetItem(tdict, self->key, self->dict) < 0) - goto err; - - return (PyObject *)self; - - err: - Py_DECREF(self); - return NULL; -} - -static int -local_traverse(localobject *self, visitproc visit, void *arg) -{ - Py_VISIT(self->args); - Py_VISIT(self->kw); - Py_VISIT(self->dict); - return 0; -} - -static int -local_clear(localobject *self) -{ - Py_CLEAR(self->key); - Py_CLEAR(self->args); - Py_CLEAR(self->kw); - Py_CLEAR(self->dict); - return 0; -} - -static void -local_dealloc(localobject *self) -{ - PyThreadState *tstate; - if (self->key - && (tstate = PyThreadState_Get()) - && tstate->interp) { - for(tstate = PyInterpreterState_ThreadHead(tstate->interp); - tstate; - tstate = PyThreadState_Next(tstate)) - if (tstate->dict && - PyDict_GetItem(tstate->dict, self->key)) - PyDict_DelItem(tstate->dict, self->key); - } - - local_clear(self); - Py_TYPE(self)->tp_free((PyObject*)self); -} - -static PyObject * -_ldict(localobject *self) -{ - PyObject *tdict, *ldict; - - tdict = PyThreadState_GetDict(); - if (tdict == NULL) { - PyErr_SetString(PyExc_SystemError, - "Couldn't get thread-state dictionary"); - return NULL; - } - - ldict = PyDict_GetItem(tdict, self->key); - if (ldict == NULL) { - ldict = PyDict_New(); /* we own ldict */ - - if (ldict == NULL) - return NULL; - else { - int i = PyDict_SetItem(tdict, self->key, ldict); - Py_DECREF(ldict); /* now ldict is borrowed */ - if (i < 0) - return NULL; - } - - Py_CLEAR(self->dict); - Py_INCREF(ldict); - self->dict = ldict; /* still borrowed */ - - if (Py_TYPE(self)->tp_init != PyBaseObject_Type.tp_init && - Py_TYPE(self)->tp_init((PyObject*)self, - self->args, self->kw) < 0) { - /* we need to get rid of ldict from thread so - we create a new one the next time we do an attr - acces */ - PyDict_DelItem(tdict, self->key); - return NULL; - } - - } - else if (self->dict != ldict) { - Py_CLEAR(self->dict); - Py_INCREF(ldict); - self->dict = ldict; - } - - return ldict; -} - -static int -local_setattro(localobject *self, PyObject *name, PyObject *v) -{ - PyObject *ldict; - - ldict = _ldict(self); - if (ldict == NULL) - return -1; - - return PyObject_GenericSetAttr((PyObject *)self, name, v); -} - -static PyObject * -local_getdict(localobject *self, void *closure) -{ - if (self->dict == NULL) { - PyErr_SetString(PyExc_AttributeError, "__dict__"); - return NULL; - } - - Py_INCREF(self->dict); - return self->dict; -} - -static PyGetSetDef local_getset[] = { - {"__dict__", (getter)local_getdict, (setter)NULL, - "Local-data dictionary", NULL}, - {NULL} /* Sentinel */ -}; - -static PyObject *local_getattro(localobject *, PyObject *); - -static PyTypeObject localtype = { - PyVarObject_HEAD_INIT(NULL, 0) - /* tp_name */ "thread._local", - /* tp_basicsize */ sizeof(localobject), - /* tp_itemsize */ 0, - /* tp_dealloc */ (destructor)local_dealloc, - /* tp_print */ 0, - /* tp_getattr */ 0, - /* tp_setattr */ 0, - /* tp_compare */ 0, - /* tp_repr */ 0, - /* tp_as_number */ 0, - /* tp_as_sequence */ 0, - /* tp_as_mapping */ 0, - /* tp_hash */ 0, - /* tp_call */ 0, - /* tp_str */ 0, - /* tp_getattro */ (getattrofunc)local_getattro, - /* tp_setattro */ (setattrofunc)local_setattro, - /* tp_as_buffer */ 0, - /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - /* tp_doc */ "Thread-local data", - /* tp_traverse */ (traverseproc)local_traverse, - /* tp_clear */ (inquiry)local_clear, - /* tp_richcompare */ 0, - /* tp_weaklistoffset */ 0, - /* tp_iter */ 0, - /* tp_iternext */ 0, - /* tp_methods */ 0, - /* tp_members */ 0, - /* tp_getset */ local_getset, - /* tp_base */ 0, - /* tp_dict */ 0, /* internal use */ - /* tp_descr_get */ 0, - /* tp_descr_set */ 0, - /* tp_dictoffset */ offsetof(localobject, dict), - /* tp_init */ 0, - /* tp_alloc */ 0, - /* tp_new */ local_new, - /* tp_free */ 0, /* Low-level free-mem routine */ - /* tp_is_gc */ 0, /* For PyObject_IS_GC */ -}; - -static PyObject * -local_getattro(localobject *self, PyObject *name) -{ - PyObject *ldict, *value; - - ldict = _ldict(self); - if (ldict == NULL) - return NULL; - - if (Py_TYPE(self) != &localtype) - /* use generic lookup for subtypes */ - return PyObject_GenericGetAttr((PyObject *)self, name); - - /* Optimization: just look in dict ourselves */ - value = PyDict_GetItem(ldict, name); - if (value == NULL) - /* Fall back on generic to get __class__ and __dict__ */ - return PyObject_GenericGetAttr((PyObject *)self, name); - - Py_INCREF(value); - return value; -} - -/* Module functions */ - -struct bootstate { - PyInterpreterState *interp; - PyObject *func; - PyObject *args; - PyObject *keyw; -}; - -static void -t_bootstrap(void *boot_raw) -{ - struct bootstate *boot = (struct bootstate *) boot_raw; - PyThreadState *tstate; - PyObject *res; - - tstate = PyThreadState_New(boot->interp); - - PyEval_AcquireThread(tstate); - res = PyEval_CallObjectWithKeywords( - boot->func, boot->args, boot->keyw); - if (res == NULL) { - if (PyErr_ExceptionMatches(PyExc_SystemExit)) - PyErr_Clear(); - else { - PyObject *file; - PySys_WriteStderr( - "Unhandled exception in thread started by "); - file = PySys_GetObject("stderr"); - if (file != NULL && file != Py_None) - PyFile_WriteObject(boot->func, file, 0); - else - PyObject_Print(boot->func, stderr, 0); - PySys_WriteStderr("\n"); - PyErr_PrintEx(0); - } - } - else - Py_DECREF(res); - Py_DECREF(boot->func); - Py_DECREF(boot->args); - Py_XDECREF(boot->keyw); - PyMem_DEL(boot_raw); - PyThreadState_Clear(tstate); - PyThreadState_DeleteCurrent(); - PyThread_exit_thread(); -} - -static PyObject * -thread_PyThread_start_new_thread(PyObject *self, PyObject *fargs) -{ - PyObject *func, *args, *keyw = NULL; - struct bootstate *boot; - long ident; - - if (!PyArg_UnpackTuple(fargs, "start_new_thread", 2, 3, - &func, &args, &keyw)) - return NULL; - if (!PyCallable_Check(func)) { - PyErr_SetString(PyExc_TypeError, - "first arg must be callable"); - return NULL; - } - if (!PyTuple_Check(args)) { - PyErr_SetString(PyExc_TypeError, - "2nd arg must be a tuple"); - return NULL; - } - if (keyw != NULL && !PyDict_Check(keyw)) { - PyErr_SetString(PyExc_TypeError, - "optional 3rd arg must be a dictionary"); - return NULL; - } - boot = PyMem_NEW(struct bootstate, 1); - if (boot == NULL) - return PyErr_NoMemory(); - boot->interp = PyThreadState_GET()->interp; - boot->func = func; - boot->args = args; - boot->keyw = keyw; - Py_INCREF(func); - Py_INCREF(args); - Py_XINCREF(keyw); - PyEval_InitThreads(); /* Start the interpreter's thread-awareness */ - ident = PyThread_start_new_thread(t_bootstrap, (void*) boot); - if (ident == -1) { - PyErr_SetString(ThreadError, "can't start new thread"); - Py_DECREF(func); - Py_DECREF(args); - Py_XDECREF(keyw); - PyMem_DEL(boot); - return NULL; - } - return PyLong_FromLong(ident); -} - -PyDoc_STRVAR(start_new_doc, -"start_new_thread(function, args[, kwargs])\n\ -(start_new() is an obsolete synonym)\n\ -\n\ -Start a new thread and return its identifier. The thread will call the\n\ -function with positional arguments from the tuple args and keyword arguments\n\ -taken from the optional dictionary kwargs. The thread exits when the\n\ -function returns; the return value is ignored. The thread will also exit\n\ -when the function raises an unhandled exception; a stack trace will be\n\ -printed unless the exception is SystemExit.\n"); - -static PyObject * -thread_PyThread_exit_thread(PyObject *self) -{ - PyErr_SetNone(PyExc_SystemExit); - return NULL; -} - -PyDoc_STRVAR(exit_doc, -"exit()\n\ -(PyThread_exit_thread() is an obsolete synonym)\n\ -\n\ -This is synonymous to ``raise SystemExit''. It will cause the current\n\ -thread to exit silently unless the exception is caught."); - -static PyObject * -thread_PyThread_interrupt_main(PyObject * self) -{ - PyErr_SetInterrupt(); - Py_INCREF(Py_None); - return Py_None; -} - -PyDoc_STRVAR(interrupt_doc, -"interrupt_main()\n\ -\n\ -Raise a KeyboardInterrupt in the main thread.\n\ -A subthread can use this function to interrupt the main thread." -); - -#ifndef NO_EXIT_PROG -static PyObject * -thread_PyThread_exit_prog(PyObject *self, PyObject *args) -{ - int sts; - if (!PyArg_ParseTuple(args, "i:exit_prog", &sts)) - return NULL; - Py_Exit(sts); /* Calls PyThread_exit_prog(sts) or _PyThread_exit_prog(sts) */ - for (;;) { } /* Should not be reached */ -} -#endif - -static lockobject *newlockobject(void); - -static PyObject * -thread_PyThread_allocate_lock(PyObject *self) -{ - return (PyObject *) newlockobject(); -} - -PyDoc_STRVAR(allocate_doc, -"allocate_lock() -> lock object\n\ -(allocate() is an obsolete synonym)\n\ -\n\ -Create a new lock object. See LockType.__doc__ for information about locks."); - -static PyObject * -thread_get_ident(PyObject *self) -{ - long ident; - ident = PyThread_get_thread_ident(); - if (ident == -1) { - PyErr_SetString(ThreadError, "no current thread ident"); - return NULL; - } - return PyLong_FromLong(ident); -} - -PyDoc_STRVAR(get_ident_doc, -"get_ident() -> integer\n\ -\n\ -Return a non-zero integer that uniquely identifies the current thread\n\ -amongst other threads that exist simultaneously.\n\ -This may be used to identify per-thread resources.\n\ -Even though on some platforms threads identities may appear to be\n\ -allocated consecutive numbers starting at 1, this behavior should not\n\ -be relied upon, and the number should be seen purely as a magic cookie.\n\ -A thread's identity may be reused for another thread after it exits."); - -static PyObject * -thread_stack_size(PyObject *self, PyObject *args) -{ - size_t old_size; - Py_ssize_t new_size = 0; - int rc; - - if (!PyArg_ParseTuple(args, "|n:stack_size", &new_size)) - return NULL; - - if (new_size < 0) { - PyErr_SetString(PyExc_ValueError, - "size must be 0 or a positive value"); - return NULL; - } - - old_size = PyThread_get_stacksize(); - - rc = PyThread_set_stacksize((size_t) new_size); - if (rc == -1) { - PyErr_Format(PyExc_ValueError, - "size not valid: %zd bytes", - new_size); - return NULL; - } - if (rc == -2) { - PyErr_SetString(ThreadError, - "setting stack size not supported"); - return NULL; - } - - return PyLong_FromSsize_t((Py_ssize_t) old_size); -} - -PyDoc_STRVAR(stack_size_doc, -"stack_size([size]) -> size\n\ -\n\ -Return the thread stack size used when creating new threads. The\n\ -optional size argument specifies the stack size (in bytes) to be used\n\ -for subsequently created threads, and must be 0 (use platform or\n\ -configured default) or a positive integer value of at least 32,768 (32k).\n\ -If changing the thread stack size is unsupported, a ThreadError\n\ -exception is raised. If the specified size is invalid, a ValueError\n\ -exception is raised, and the stack size is unmodified. 32k bytes\n\ - currently the minimum supported stack size value to guarantee\n\ -sufficient stack space for the interpreter itself.\n\ -\n\ -Note that some platforms may have particular restrictions on values for\n\ -the stack size, such as requiring a minimum stack size larger than 32kB or\n\ -requiring allocation in multiples of the system memory page size\n\ -- platform documentation should be referred to for more information\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 PyMethodDef thread_methods[] = { - {"start_new_thread", (PyCFunction)thread_PyThread_start_new_thread, - METH_VARARGS, - start_new_doc}, - {"start_new", (PyCFunction)thread_PyThread_start_new_thread, - METH_VARARGS, - start_new_doc}, - {"allocate_lock", (PyCFunction)thread_PyThread_allocate_lock, - METH_NOARGS, allocate_doc}, - {"allocate", (PyCFunction)thread_PyThread_allocate_lock, - METH_NOARGS, allocate_doc}, - {"exit_thread", (PyCFunction)thread_PyThread_exit_thread, - METH_NOARGS, exit_doc}, - {"exit", (PyCFunction)thread_PyThread_exit_thread, - METH_NOARGS, exit_doc}, - {"interrupt_main", (PyCFunction)thread_PyThread_interrupt_main, - METH_NOARGS, interrupt_doc}, - {"get_ident", (PyCFunction)thread_get_ident, - METH_NOARGS, get_ident_doc}, - {"stack_size", (PyCFunction)thread_stack_size, - METH_VARARGS, - stack_size_doc}, -#ifndef NO_EXIT_PROG - {"exit_prog", (PyCFunction)thread_PyThread_exit_prog, - METH_VARARGS}, -#endif - {NULL, NULL} /* sentinel */ -}; - - -/* Initialization function */ - -PyDoc_STRVAR(thread_doc, -"This module provides primitive operations to write multi-threaded programs.\n\ -The 'threading' module provides a more convenient interface."); - -PyDoc_STRVAR(lock_doc, -"A lock object is a synchronization primitive. To create a lock,\n\ -call the PyThread_allocate_lock() function. Methods are:\n\ -\n\ -acquire() -- lock the lock, possibly blocking until it can be obtained\n\ -release() -- unlock of the lock\n\ -locked() -- test whether the lock is currently locked\n\ -\n\ -A lock is not owned by the thread that locked it; another thread may\n\ -unlock it. A thread attempting to lock a lock that it has already locked\n\ -will block until another thread unlocks it. Deadlocks may ensue."); - -PyMODINIT_FUNC -initthread(void) -{ - PyObject *m, *d; - - /* Initialize types: */ - if (PyType_Ready(&localtype) < 0) - return; - if (PyType_Ready(&Locktype) < 0) - return; - - /* Create the module and add the functions */ - m = Py_InitModule3("thread", thread_methods, thread_doc); - if (m == NULL) - return; - - /* Add a symbolic constant */ - d = PyModule_GetDict(m); - ThreadError = PyErr_NewException("thread.error", NULL, NULL); - PyDict_SetItemString(d, "error", ThreadError); - Locktype.tp_doc = lock_doc; - Py_INCREF(&Locktype); - PyDict_SetItemString(d, "LockType", (PyObject *)&Locktype); - - Py_INCREF(&localtype); - if (PyModule_AddObject(m, "_local", (PyObject *)&localtype) < 0) - return; - - /* Initialize the C thread library */ - PyThread_init_thread(); -} diff --git a/PC/VC6/pythoncore.dsp b/PC/VC6/pythoncore.dsp index 84a5bb5..035a11d 100644 --- a/PC/VC6/pythoncore.dsp +++ b/PC/VC6/pythoncore.dsp @@ -679,7 +679,7 @@ SOURCE=..\..\Python\thread.c # End Source File # Begin Source File -SOURCE=..\..\Modules\threadmodule.c +SOURCE=..\..\Modules\_threadmodule.c # End Source File # Begin Source File diff --git a/PC/VS7.1/pythoncore.vcproj b/PC/VS7.1/pythoncore.vcproj index 38982f4..8692e7a 100644 --- a/PC/VS7.1/pythoncore.vcproj +++ b/PC/VS7.1/pythoncore.vcproj @@ -768,7 +768,7 @@ RelativePath="..\..\Python\thread.c"> + RelativePath="..\..\Modules\_threadmodule.c"> diff --git a/PC/VS8.0/pythoncore.vcproj b/PC/VS8.0/pythoncore.vcproj index 75414ce..c2eb356 100644 --- a/PC/VS8.0/pythoncore.vcproj +++ b/PC/VS8.0/pythoncore.vcproj @@ -1127,7 +1127,7 @@ >