Skip to content

Commit

Permalink
gh-88863: Clear ref cycles to resolve leak when asyncio.open_connecti…
Browse files Browse the repository at this point in the history
…on raises (#95739)

Break reference cycles to resolve memory leak, by
removing local exception and future instances from the frame
  • Loading branch information
frostbyte134 authored Nov 22, 2022
1 parent 9a91182 commit 995f617
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 14 deletions.
31 changes: 19 additions & 12 deletions Lib/asyncio/base_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -986,6 +986,8 @@ async def _connect_sock(self, exceptions, addr_info, local_addr_infos=None):
if sock is not None:
sock.close()
raise
finally:
exceptions = my_exceptions = None

async def create_connection(
self, protocol_factory, host=None, port=None,
Expand Down Expand Up @@ -1084,19 +1086,22 @@ async def create_connection(

if sock is None:
exceptions = [exc for sub in exceptions for exc in sub]
if all_errors:
raise ExceptionGroup("create_connection failed", exceptions)
if len(exceptions) == 1:
raise exceptions[0]
else:
# If they all have the same str(), raise one.
model = str(exceptions[0])
if all(str(exc) == model for exc in exceptions):
try:
if all_errors:
raise ExceptionGroup("create_connection failed", exceptions)
if len(exceptions) == 1:
raise exceptions[0]
# Raise a combined exception so the user can see all
# the various error messages.
raise OSError('Multiple exceptions: {}'.format(
', '.join(str(exc) for exc in exceptions)))
else:
# If they all have the same str(), raise one.
model = str(exceptions[0])
if all(str(exc) == model for exc in exceptions):
raise exceptions[0]
# Raise a combined exception so the user can see all
# the various error messages.
raise OSError('Multiple exceptions: {}'.format(
', '.join(str(exc) for exc in exceptions)))
finally:
exceptions = None

else:
if sock is None:
Expand Down Expand Up @@ -1904,6 +1909,8 @@ def _run_once(self):

event_list = self._selector.select(timeout)
self._process_events(event_list)
# Needed to break cycles when an exception occurs.
event_list = None

# Handle 'later' callbacks that are ready.
end_time = self.time() + self._clock_resolution
Expand Down
10 changes: 9 additions & 1 deletion Lib/asyncio/selector_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,11 @@ async def sock_connect(self, sock, address):

fut = self.create_future()
self._sock_connect(fut, sock, address)
return await fut
try:
return await fut
finally:
# Needed to break cycles when an exception occurs.
fut = None

def _sock_connect(self, fut, sock, address):
fd = sock.fileno()
Expand All @@ -655,6 +659,8 @@ def _sock_connect(self, fut, sock, address):
fut.set_exception(exc)
else:
fut.set_result(None)
finally:
fut = None

def _sock_write_done(self, fd, fut, handle=None):
if handle is None or not handle.cancelled():
Expand All @@ -678,6 +684,8 @@ def _sock_connect_cb(self, fut, sock, address):
fut.set_exception(exc)
else:
fut.set_result(None)
finally:
fut = None

async def sock_accept(self, sock):
"""Accept a connection.
Expand Down
8 changes: 7 additions & 1 deletion Lib/asyncio/windows_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,11 @@ def select(self, timeout=None):
self._poll(timeout)
tmp = self._results
self._results = []
return tmp
try:
return tmp
finally:
# Needed to break cycles when an exception occurs.
tmp = None

def _result(self, value):
fut = self._loop.create_future()
Expand Down Expand Up @@ -793,6 +797,8 @@ def _poll(self, timeout=None):
else:
f.set_result(value)
self._results.append(f)
finally:
f = None

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

0 comments on commit 995f617

Please sign in to comment.