From 711906e0c26c010600221da50aa930008b8a9447 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Sat, 8 Jan 2005 07:30:42 +0000 Subject: threading._DummyThread.__init__(): document obscure new code. test_threading.test_foreign_thread(): new test does a basic check that "foreign" threads can using the threading module, and that they create a _DummyThread instance in at least one use case. This isn't a very good test, since a thread created by thread.start_new_thread() isn't particularly "foreign". --- Lib/test/test_threading.py | 23 ++++++++++++++++++++++- Lib/threading.py | 12 +++++++++--- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py index 8b17460..7eb9758 100644 --- a/Lib/test/test_threading.py +++ b/Lib/test/test_threading.py @@ -4,6 +4,7 @@ import test.test_support from test.test_support import verbose import random import threading +import thread import time import unittest @@ -78,11 +79,31 @@ class ThreadTests(unittest.TestCase): if verbose: print 'waiting for all tasks to complete' for t in threads: - t.join() + t.join(NUMTASKS) + self.assert_(not t.isAlive()) if verbose: print 'all tasks done' self.assertEqual(numrunning.get(), 0) + def test_foreign_thread(self): + # Check that a "foreign" thread can use the threading module. + def f(mutex): + # Acquiring an RLock forces an entry for the foreign + # thread to get made in the threading._active map. + r = threading.RLock() + r.acquire() + r.release() + mutex.release() + + mutex = threading.Lock() + mutex.acquire() + tid = thread.start_new_thread(f, (mutex,)) + # Wait for the thread to finish. + mutex.acquire() + self.assert_(tid in threading._active) + self.assert_(isinstance(threading._active[tid], + threading._DummyThread)) + del threading._active[tid] def test_main(): test.test_support.run_unittest(ThreadTests) diff --git a/Lib/threading.py b/Lib/threading.py index 61c25b6..1aed551 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -358,7 +358,7 @@ def _newname(template="Thread-%d"): # Active thread administration _active_limbo_lock = _allocate_lock() -_active = {} +_active = {} # maps thread id to Thread object _limbo = {} @@ -643,8 +643,9 @@ def _pickSomeNonDaemonThread(): # Dummy thread class to represent threads not started here. -# These aren't garbage collected when they die, -# nor can they be waited for. +# These aren't garbage collected when they die, nor can they be waited for. +# If they invoke anything in threading.py that calls currentThread(), they +# leave an entry in the _active dict forever after. # Their purpose is to return *something* from currentThread(). # They are marked as daemon threads so we won't wait for them # when we exit (conform previous semantics). @@ -653,7 +654,12 @@ class _DummyThread(Thread): def __init__(self): Thread.__init__(self, name=_newname("Dummy-%d")) + + # Thread.__block consumes an OS-level locking primitive, which + # can never be used by a _DummyThread. Since a _DummyThread + # instance is immortal, that's bad, so release this resource. del self._Thread__block + self._Thread__started = True _active_limbo_lock.acquire() _active[_get_ident()] = self -- cgit v0.12