Prevent classes like:

class RunSelfFunction(object):
        def __init__(self):
            self.thread = threading.Thread(target=self._run)
            self.thread.start()
        def _run(self):
            pass
from creating a permanent cycle between the object and the thread by having the
Thread delete its references to the object when it completes.

As an example of the effect of this bug, paramiko.Transport inherits from
Thread to avoid it.
This commit is contained in:
Jeffrey Yasskin 2008-02-23 19:40:54 +00:00
parent 1beea27299
commit 3414ea9ed9
2 changed files with 23 additions and 0 deletions

View file

@ -8,6 +8,7 @@ import threading
import thread import thread
import time import time
import unittest import unittest
import weakref
# A trivial mutable counter. # A trivial mutable counter.
class Counter(object): class Counter(object):
@ -253,6 +254,25 @@ class ThreadTests(unittest.TestCase):
finally: finally:
sys.setcheckinterval(old_interval) sys.setcheckinterval(old_interval)
def test_no_refcycle_through_target(self):
class RunSelfFunction(object):
def __init__(self):
# The links in this refcycle from Thread back to self
# should be cleaned up when the thread completes.
self.thread = threading.Thread(target=self._run,
args=(self,),
kwargs={'yet_another':self})
self.thread.start()
def _run(self, other_ref, yet_another):
pass
cyclic_object = RunSelfFunction()
weak_cyclic_object = weakref.ref(cyclic_object)
cyclic_object.thread.join()
del cyclic_object
self.assertEquals(None, weak_cyclic_object())
class ThreadingExceptionTests(unittest.TestCase): class ThreadingExceptionTests(unittest.TestCase):
# A RuntimeError should be raised if Thread.start() is called # A RuntimeError should be raised if Thread.start() is called

View file

@ -444,6 +444,9 @@ class Thread(_Verbose):
def run(self): def run(self):
if self.__target: if self.__target:
self.__target(*self.__args, **self.__kwargs) self.__target(*self.__args, **self.__kwargs)
# Avoid a refcycle if the thread is running a function with an
# argument that has a member that points to the thread.
del self.__target, self.__args, self.__kwargs
def __bootstrap(self): def __bootstrap(self):
# Wrapper around the real bootstrap code that ignores # Wrapper around the real bootstrap code that ignores