]> granicus.if.org Git - python/commitdiff
Prevent classes like:
authorJeffrey Yasskin <jyasskin@gmail.com>
Sat, 23 Feb 2008 19:40:54 +0000 (19:40 +0000)
committerJeffrey Yasskin <jyasskin@gmail.com>
Sat, 23 Feb 2008 19:40:54 +0000 (19:40 +0000)
    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.

Lib/test/test_threading.py
Lib/threading.py

index 4f49d7f1cbae124c1ffa3b98a5a41392ba56cb53..91f5a8b2df665ceb40aaf2675b45b2f1ea9f6729 100644 (file)
@@ -8,6 +8,7 @@ import threading
 import thread
 import time
 import unittest
+import weakref
 
 # A trivial mutable counter.
 class Counter(object):
@@ -253,6 +254,25 @@ class ThreadTests(unittest.TestCase):
         finally:
             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):
     # A RuntimeError should be raised if Thread.start() is called
index 409360dae2a0957aa8f4569d3de3eb90f6a910f0..2f472b47aa33cc854094385050cb8e4acd73afce 100644 (file)
@@ -444,6 +444,9 @@ class Thread(_Verbose):
     def run(self):
         if self.__target:
             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):
         # Wrapper around the real bootstrap code that ignores