Eliminate GC refcounting cycle on _connect exception - #4195
Conversation
sean-kim05
left a comment
There was a problem hiding this comment.
This matches the idiom CPython uses in socket.create_connection itself (try: raise err finally: err = None), so it's well-precedented. 👍
One thing worth surfacing for reviewers: the added err = None just before return sock isn't redundant with the finally. It covers the case where an earlier address from getaddrinfo fails (populating err) and a later one succeeds — without it, the returning frame still holds that earlier exception, whose traceback references the frame, keeping the cycle alive until the GC runs. So both clears pull their weight; might be worth a one-line comment on that second one since its purpose is less obvious than the finally.
|
Hey @rschlaikjer, thank you for your contribution! I'll review it shortly. |
|
same pattern is live in NodesManager.initialize: |
petyaslavova
left a comment
There was a problem hiding this comment.
Hey @rschlaikjer, the change looks good!
Before merging, can you please also cover the case where the first sock.connect has failed, but the next one succeeds? It will be good to have this other flow covered as well...
|
@petyaslavova, thanks for the review. Added a test for the failure followed by success case. |
Description of change
Currently, in
_connect, if an exception is thrown while trying different address infos, it is cached to be thrown only if no connection succeeds. However, storing a caught exception inside the local stack frame creates a circular reference: the exception's traceback contains a pointer to the current frame, which contains theerrlocal, which points to the exception again. This creates garbage that can only be collected by the fallback mark and sweep python GC.Break this cycle by clearing the local exception value on re-raise, allowing the error to be reclaimed via refcounting. This is useful for applications that disable or otherwise limit the python garbage collector.
Pull Request check-list
Please make sure to review and check all of these items:
NOTE: these things are not required to open a PR and can be done
afterwards / while the PR is open.
Note
Low Risk
Localized TCP connect error-handling cleanup with no behavior change beyond memory/GC semantics; covered by new unit tests.
Overview
Fixes a reference cycle in TCP
Connection._connectwhen iteratinggetaddrinforesults: a failed attempt stores the caughtOSErrorinerr, and re-raising it leaves the traceback pointing at the frame that still holdserr, which only cyclic GC can collect.On successful connect after an earlier failure,
erris set toNonebefore returning the socket. When all addresses fail, the final re-raise usestry/finallyto clearerrafter propagation so refcounting can drop the exception immediately—relevant for apps that limit or disable the cyclic garbage collector.Adds tests that inspect the
_connectstack frame and asserterris cleared on total failure and when a later address succeeds.Reviewed by Cursor Bugbot for commit 9a152dd. Bugbot is set up for automated code reviews on this repo. Configure here.