]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-106558: break ref cycles through exceptions in multiprocessing manager (#106559)
authorAndrew Geng <pteromys@gmail.com>
Fri, 11 Aug 2023 17:44:18 +0000 (13:44 -0400)
committerGitHub <noreply@github.com>
Fri, 11 Aug 2023 17:44:18 +0000 (17:44 +0000)
Lib/multiprocessing/managers.py
Lib/test/_test_multiprocessing.py
Misc/NEWS.d/next/Library/2023-07-09-00-36-33.gh-issue-106558.Zqsj6F.rst [new file with mode: 0644]

index b6534939b4d98bc8f39cdb0750af08d7ea649f1a..273c22a7654f05315794fa90cf3b25d8bee61f95 100644 (file)
@@ -90,7 +90,10 @@ def dispatch(c, id, methodname, args=(), kwds={}):
     kind, result = c.recv()
     if kind == '#RETURN':
         return result
-    raise convert_to_error(kind, result)
+    try:
+        raise convert_to_error(kind, result)
+    finally:
+        del result  # break reference cycle
 
 def convert_to_error(kind, result):
     if kind == '#ERROR':
@@ -833,7 +836,10 @@ class BaseProxy(object):
             conn = self._Client(token.address, authkey=self._authkey)
             dispatch(conn, None, 'decref', (token.id,))
             return proxy
-        raise convert_to_error(kind, result)
+        try:
+            raise convert_to_error(kind, result)
+        finally:
+            del result   # break reference cycle
 
     def _getvalue(self):
         '''
index c1f9487ae805118bf8bf09230af5752008b55b9f..f881a5d4674699af263328fc3f996c0e6b4543a1 100644 (file)
@@ -3149,6 +3149,44 @@ class _TestManagerRestart(BaseTestCase):
             if hasattr(manager, "shutdown"):
                 self.addCleanup(manager.shutdown)
 
+
+class FakeConnection:
+    def send(self, payload):
+        pass
+
+    def recv(self):
+        return '#ERROR', pyqueue.Empty()
+
+class TestManagerExceptions(unittest.TestCase):
+    # Issue 106558: Manager exceptions avoids creating cyclic references.
+    def setUp(self):
+        self.mgr = multiprocessing.Manager()
+
+    def tearDown(self):
+        self.mgr.shutdown()
+        self.mgr.join()
+
+    def test_queue_get(self):
+        queue = self.mgr.Queue()
+        if gc.isenabled():
+            gc.disable()
+            self.addCleanup(gc.enable)
+        try:
+            queue.get_nowait()
+        except pyqueue.Empty as e:
+            wr = weakref.ref(e)
+        self.assertEqual(wr(), None)
+
+    def test_dispatch(self):
+        if gc.isenabled():
+            gc.disable()
+            self.addCleanup(gc.enable)
+        try:
+            multiprocessing.managers.dispatch(FakeConnection(), None, None)
+        except pyqueue.Empty as e:
+            wr = weakref.ref(e)
+        self.assertEqual(wr(), None)
+
 #
 #
 #
diff --git a/Misc/NEWS.d/next/Library/2023-07-09-00-36-33.gh-issue-106558.Zqsj6F.rst b/Misc/NEWS.d/next/Library/2023-07-09-00-36-33.gh-issue-106558.Zqsj6F.rst
new file mode 100644 (file)
index 0000000..8fe677f
--- /dev/null
@@ -0,0 +1,3 @@
+Remove ref cycle in callers of
+:func:`~multiprocessing.managers.convert_to_error` by deleting ``result``
+from scope in a ``finally`` block.