New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
multiprocessing Manager exceptions create cyclic references #106558
Comments
Shorter example: #!/usr/bin/env python
import gc
import multiprocessing
import queue
import weakref
def main():
gc.disable()
q = multiprocessing.Manager().Queue()
try:
q.get_nowait()
except queue.Empty as e:
w = weakref.ref(e)
else:
raise RuntimeError("queue.Empty not raised when expected")
print(type(w())) # queue.Empty if it has circular refs, NoneType if not
gc.enable()
if __name__ == '__main__':
main() |
I'm not 100% supporting this change for the following reasons:
|
3 is a fair point, and I've made my peace with the risk of having to give up on eliminating reference cycles once that problem becomes hard. For now, the difficult cases are rare enough that one of my microservices already runs without gc activity past an initial warmup period. I actually partly agree with (2) also—in general, the tradeoff that I'd be happiest making is this: saving an exception in a local variable is going deep enough that if the library user does it, then we have no responsibility to help them avoid reference cycles. But if it's library code doing it, there's prior art of us trying just a bit harder—not only that it's documented behavior that
The linked PR is similarly low-hanging fruit—I would hope we can add two |
Hmm, if we have done this in our code base before then it's a different story. That means the core devs have already determined that a little bit lost of readability is worth it to break the reference cycle. In that case, I would ask @iritkatriel for the PR review as she's the expert in Exceptions (and I think she was the reviewer for a similar PR). |
Bug report
multiprocessing.managers
usesconvert_to_error(kind, result)
to make a raisable exception out ofresult
when a call has responded with some sort of error. Ifkind == "#ERROR"
, thenresult
is already an exception and the caller raises it directly—but becauseresult
was created in a frame at or under the caller, this creates a reference cycleresult
→result.__traceback__
→ (some frame).f_locals['result']
.In particular, every time I've used a manager queue I've expected frequent occurrences of
queue.Empty
, and the buildup of reference cycles sporadically wakes up the garbage collector and wrecks my hopes of consistent latency.I'm including an example script below. PR coming in a moment, so please let me know if I should expand the example into a test and bundle that in. (Please also feel free to tell me if this is a misuse of queue.Empty and I should buzz off.)
Your environment
uname -a
saysLinux delia 6.3.2-arch1-1 #1 SMP PREEMPT_DYNAMIC Thu, 11 May 2023 16:40:42 +0000 x86_64 GNU/Linux
Minimal example
Output
Script
Linked PRs
The text was updated successfully, but these errors were encountered: