Skip to content

Commit

Permalink
gh-106558: break ref cycles through exceptions in multiprocessing man…
Browse files Browse the repository at this point in the history
…ager (#106559)
  • Loading branch information
pteromys committed Aug 11, 2023
1 parent caa41a4 commit 5f7d4ec
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 2 deletions.
10 changes: 8 additions & 2 deletions Lib/multiprocessing/managers.py
Expand Up @@ -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':
Expand Down Expand Up @@ -833,7 +836,10 @@ def _callmethod(self, methodname, args=(), kwds={}):
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):
'''
Expand Down
38 changes: 38 additions & 0 deletions Lib/test/_test_multiprocessing.py
Expand Up @@ -3149,6 +3149,44 @@ def test_rapid_restart(self):
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)

#
#
#
Expand Down
@@ -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.

0 comments on commit 5f7d4ec

Please sign in to comment.