diff options
author | Dong Uk, Kang <nailbrainz@gmail.com> | 2022-11-23 18:39:04 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-11-23 18:39:04 (GMT) |
commit | b191bc35f53904783d14df7fc37b0d9cad0f67b0 (patch) | |
tree | 15cbd99190c0e63630227d8dd1c3b9e640698ef8 /Lib | |
parent | 40a4b40bb1a8b27c1490fcbb5937d2e2cffa280c (diff) | |
download | cpython-b191bc35f53904783d14df7fc37b0d9cad0f67b0.zip cpython-b191bc35f53904783d14df7fc37b0d9cad0f67b0.tar.gz cpython-b191bc35f53904783d14df7fc37b0d9cad0f67b0.tar.bz2 |
[3.10] gh-88863: Clear ref cycles to resolve leak when asyncio.open_connection raises (GH-95739) (#99722)
Break reference cycles to resolve memory leak, by
removing local exception and future instances from the frame.
(cherry picked from commit 995f6170c78570eca818f7e7dbd8a7661c171a81)
Co-authored-by: Dong Uk, Kang <nailbrainz@gmail.com>
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/asyncio/base_events.py | 27 | ||||
-rw-r--r-- | Lib/asyncio/selector_events.py | 10 | ||||
-rw-r--r-- | Lib/asyncio/windows_events.py | 8 |
3 files changed, 33 insertions, 12 deletions
diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 2384985..9a05abf 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -971,6 +971,8 @@ class BaseEventLoop(events.AbstractEventLoop): if sock is not None: sock.close() raise + finally: + exceptions = my_exceptions = None async def create_connection( self, protocol_factory, host=None, port=None, @@ -1063,17 +1065,20 @@ class BaseEventLoop(events.AbstractEventLoop): if sock is None: exceptions = [exc for sub in exceptions for exc in sub] - 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 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: @@ -1862,6 +1867,8 @@ class BaseEventLoop(events.AbstractEventLoop): 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 diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py index 572d4a8..8282f28 100644 --- a/Lib/asyncio/selector_events.py +++ b/Lib/asyncio/selector_events.py @@ -497,7 +497,11 @@ class BaseSelectorEventLoop(base_events.BaseEventLoop): 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() @@ -519,6 +523,8 @@ class BaseSelectorEventLoop(base_events.BaseEventLoop): 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(): @@ -542,6 +548,8 @@ class BaseSelectorEventLoop(base_events.BaseEventLoop): fut.set_exception(exc) else: fut.set_result(None) + finally: + fut = None async def sock_accept(self, sock): """Accept a connection. diff --git a/Lib/asyncio/windows_events.py b/Lib/asyncio/windows_events.py index da81ab4..ed1cd19 100644 --- a/Lib/asyncio/windows_events.py +++ b/Lib/asyncio/windows_events.py @@ -439,7 +439,11 @@ class IocpProactor: 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() @@ -821,6 +825,8 @@ class IocpProactor: else: f.set_result(value) self._results.append(f) + finally: + f = None # Remove unregistered futures for ov in self._unregistered: |