diff -r edfa42003c34 Lib/test/test_threading.py --- a/Lib/test/test_threading.py Thu Mar 27 11:35:52 2008 +0100 +++ b/Lib/test/test_threading.py Thu Mar 27 17:29:30 2008 +0100 @@ -2,6 +2,7 @@ import test.test_support from test.test_support import verbose +import gc import random import sys import threading @@ -272,17 +273,36 @@ class ThreadTests(unittest.TestCase): if self.should_raise: raise SystemExit - cyclic_object = RunSelfFunction(should_raise=False) - weak_cyclic_object = weakref.ref(cyclic_object) - cyclic_object.thread.join() - del cyclic_object - self.assertEquals(None, weak_cyclic_object()) + def assertNoCycles(self): + self.assertFalse(hasattr(self.thread, "__kwargs")) + self.assertFalse(hasattr(self.thread, "__args")) - raising_cyclic_object = RunSelfFunction(should_raise=True) - weak_raising_cyclic_object = weakref.ref(raising_cyclic_object) - raising_cyclic_object.thread.join() - del raising_cyclic_object - self.assertEquals(None, weak_raising_cyclic_object()) + try: + # Disable the gc during the tests, to guarantee that refcounting + # is sufficient to deallocate the RunSelfFunction object. + gc.disable() + + cyclic_object = RunSelfFunction(should_raise=False) + weak_cyclic_object = weakref.ref(cyclic_object) + cyclic_object.thread.join() + del cyclic_object + # Here we give the OS a chance to switch threads and allow the + # concurrent thread to finally release the references it holds to + # the Thread object and the various function arguments. + # XXX this is still flaky of course + time.sleep(0.2) + self.assertEquals(None, weak_cyclic_object()) + + raising_cyclic_object = RunSelfFunction(should_raise=True) + weak_raising_cyclic_object = weakref.ref(raising_cyclic_object) + raising_cyclic_object.thread.join() + del raising_cyclic_object + # See above. + time.sleep(0.2) + self.assertEquals(None, weak_raising_cyclic_object()) + + finally: + gc.enable() class ThreadingExceptionTests(unittest.TestCase): diff -r edfa42003c34 Lib/threading.py --- a/Lib/threading.py Thu Mar 27 11:35:52 2008 +0100 +++ b/Lib/threading.py Thu Mar 27 17:29:30 2008 +0100 @@ -392,6 +392,7 @@ class Thread(_Verbose): # shutdown and thus raises an exception about trying to perform some # operation on/with a NoneType __exc_info = _sys.exc_info + __exc_clear = _sys.exc_clear def __init__(self, group=None, target=None, name=None, args=(), kwargs=None, verbose=None): @@ -489,6 +490,7 @@ class Thread(_Verbose): try: self.run() except SystemExit: + self.__exc_clear() if __debug__: self._note("%s.__bootstrap(): raised SystemExit", self) except: @@ -499,8 +501,11 @@ class Thread(_Verbose): # _sys) in case sys.stderr was redefined since the creation of # self. if _sys: - _sys.stderr.write("Exception in thread %s:\n%s\n" % - (self.getName(), _format_exc())) + try: + _sys.stderr.write("Exception in thread %s:\n%s\n" % + (self.getName(), _format_exc())) + finally: + self.__exc_clear() else: # Do the best job possible w/o a huge amt. of code to # approximate a traceback (code ideas from @@ -524,6 +529,7 @@ class Thread(_Verbose): # hog; deleting everything else is just for thoroughness finally: del exc_type, exc_value, exc_tb + self.__exc_clear() else: if __debug__: self._note("%s.__bootstrap(): normal return", self)