Skip to content

Commit 5e913d5

Browse files
committed
[3.10] pythongh-88863: Clear ref cycles to resolve leak when asyncio.open_connection raises (pythonGH-95739)
Break reference cycles to resolve memory leak, by removing local exception and future instances from the frame. (cherry picked from commit 995f617) Co-authored-by: Dong Uk, Kang <[email protected]>
1 parent b87d03d commit 5e913d5

File tree

4 files changed

+36
-12
lines changed

4 files changed

+36
-12
lines changed

Lib/asyncio/base_events.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -968,6 +968,8 @@ async def _connect_sock(self, exceptions, addr_info, local_addr_infos=None):
968968
if sock is not None:
969969
sock.close()
970970
raise
971+
finally:
972+
exceptions = my_exceptions = None
971973

972974
async def create_connection(
973975
self, protocol_factory, host=None, port=None,
@@ -1060,17 +1062,20 @@ async def create_connection(
10601062

10611063
if sock is None:
10621064
exceptions = [exc for sub in exceptions for exc in sub]
1063-
if len(exceptions) == 1:
1064-
raise exceptions[0]
1065-
else:
1066-
# If they all have the same str(), raise one.
1067-
model = str(exceptions[0])
1068-
if all(str(exc) == model for exc in exceptions):
1065+
try:
1066+
if len(exceptions) == 1:
10691067
raise exceptions[0]
1070-
# Raise a combined exception so the user can see all
1071-
# the various error messages.
1072-
raise OSError('Multiple exceptions: {}'.format(
1073-
', '.join(str(exc) for exc in exceptions)))
1068+
else:
1069+
# If they all have the same str(), raise one.
1070+
model = str(exceptions[0])
1071+
if all(str(exc) == model for exc in exceptions):
1072+
raise exceptions[0]
1073+
# Raise a combined exception so the user can see all
1074+
# the various error messages.
1075+
raise OSError('Multiple exceptions: {}'.format(
1076+
', '.join(str(exc) for exc in exceptions)))
1077+
finally:
1078+
exceptions = None
10741079

10751080
else:
10761081
if sock is None:
@@ -1859,6 +1864,8 @@ def _run_once(self):
18591864

18601865
event_list = self._selector.select(timeout)
18611866
self._process_events(event_list)
1867+
# Needed to break cycles when an exception occurs.
1868+
event_list = None
18621869

18631870
# Handle 'later' callbacks that are ready.
18641871
end_time = self.time() + self._clock_resolution

Lib/asyncio/selector_events.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,11 @@ async def sock_connect(self, sock, address):
497497

498498
fut = self.create_future()
499499
self._sock_connect(fut, sock, address)
500-
return await fut
500+
try:
501+
return await fut
502+
finally:
503+
# Needed to break cycles when an exception occurs.
504+
fut = None
501505

502506
def _sock_connect(self, fut, sock, address):
503507
fd = sock.fileno()
@@ -519,6 +523,8 @@ def _sock_connect(self, fut, sock, address):
519523
fut.set_exception(exc)
520524
else:
521525
fut.set_result(None)
526+
finally:
527+
fut = None
522528

523529
def _sock_write_done(self, fd, fut, handle=None):
524530
if handle is None or not handle.cancelled():
@@ -542,6 +548,8 @@ def _sock_connect_cb(self, fut, sock, address):
542548
fut.set_exception(exc)
543549
else:
544550
fut.set_result(None)
551+
finally:
552+
fut = None
545553

546554
async def sock_accept(self, sock):
547555
"""Accept a connection.

Lib/asyncio/windows_events.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,11 @@ def select(self, timeout=None):
439439
self._poll(timeout)
440440
tmp = self._results
441441
self._results = []
442-
return tmp
442+
try:
443+
return tmp
444+
finally:
445+
# Needed to break cycles when an exception occurs.
446+
tmp = None
443447

444448
def _result(self, value):
445449
fut = self._loop.create_future()
@@ -821,6 +825,8 @@ def _poll(self, timeout=None):
821825
else:
822826
f.set_result(value)
823827
self._results.append(f)
828+
finally:
829+
f = None
824830

825831
# Remove unregistered futures
826832
for ov in self._unregistered:
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
To avoid apparent memory leaks when :func:`asyncio.open_connection` raises,
2+
break reference cycles generated by local exception and future instances
3+
(which has exception instance as its member var). Patch by Dong Uk, Kang.

0 commit comments

Comments
 (0)