Skip to content

Commit 5f7d4ec

Browse files
authored
gh-106558: break ref cycles through exceptions in multiprocessing manager (#106559)
1 parent caa41a4 commit 5f7d4ec

File tree

3 files changed

+49
-2
lines changed

3 files changed

+49
-2
lines changed

Lib/multiprocessing/managers.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,10 @@ def dispatch(c, id, methodname, args=(), kwds={}):
9090
kind, result = c.recv()
9191
if kind == '#RETURN':
9292
return result
93-
raise convert_to_error(kind, result)
93+
try:
94+
raise convert_to_error(kind, result)
95+
finally:
96+
del result # break reference cycle
9497

9598
def convert_to_error(kind, result):
9699
if kind == '#ERROR':
@@ -833,7 +836,10 @@ def _callmethod(self, methodname, args=(), kwds={}):
833836
conn = self._Client(token.address, authkey=self._authkey)
834837
dispatch(conn, None, 'decref', (token.id,))
835838
return proxy
836-
raise convert_to_error(kind, result)
839+
try:
840+
raise convert_to_error(kind, result)
841+
finally:
842+
del result # break reference cycle
837843

838844
def _getvalue(self):
839845
'''

Lib/test/_test_multiprocessing.py

+38
Original file line numberDiff line numberDiff line change
@@ -3149,6 +3149,44 @@ def test_rapid_restart(self):
31493149
if hasattr(manager, "shutdown"):
31503150
self.addCleanup(manager.shutdown)
31513151

3152+
3153+
class FakeConnection:
3154+
def send(self, payload):
3155+
pass
3156+
3157+
def recv(self):
3158+
return '#ERROR', pyqueue.Empty()
3159+
3160+
class TestManagerExceptions(unittest.TestCase):
3161+
# Issue 106558: Manager exceptions avoids creating cyclic references.
3162+
def setUp(self):
3163+
self.mgr = multiprocessing.Manager()
3164+
3165+
def tearDown(self):
3166+
self.mgr.shutdown()
3167+
self.mgr.join()
3168+
3169+
def test_queue_get(self):
3170+
queue = self.mgr.Queue()
3171+
if gc.isenabled():
3172+
gc.disable()
3173+
self.addCleanup(gc.enable)
3174+
try:
3175+
queue.get_nowait()
3176+
except pyqueue.Empty as e:
3177+
wr = weakref.ref(e)
3178+
self.assertEqual(wr(), None)
3179+
3180+
def test_dispatch(self):
3181+
if gc.isenabled():
3182+
gc.disable()
3183+
self.addCleanup(gc.enable)
3184+
try:
3185+
multiprocessing.managers.dispatch(FakeConnection(), None, None)
3186+
except pyqueue.Empty as e:
3187+
wr = weakref.ref(e)
3188+
self.assertEqual(wr(), None)
3189+
31523190
#
31533191
#
31543192
#
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Remove ref cycle in callers of
2+
:func:`~multiprocessing.managers.convert_to_error` by deleting ``result``
3+
from scope in a ``finally`` block.

0 commit comments

Comments
 (0)