-
-
Notifications
You must be signed in to change notification settings - Fork 31.4k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
gh-131466: concurrent.futures.Executor.map
: avoid temporarily exceeding buffersize
while collecting the next result
#131467
base: main
Are you sure you want to change the base?
gh-131466: concurrent.futures.Executor.map
: avoid temporarily exceeding buffersize
while collecting the next result
#131467
Conversation
…llecting the next result
…Test.test_free_reference
concurrent.futures.Executor.map
: avoid temporarily exceeding buffersize
while collecting the next resultconcurrent.futures.Executor.map
: avoid temporarily exceeding buffersize
while collecting the next result
Lib/concurrent/futures/_base.py
Outdated
yield _result_or_cancel(fs.pop(), end_time - time.monotonic()) | ||
|
||
# Yield the awaited result | ||
yield fs.pop().result() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to be discussed: this could be replaced by a lighter yield fs.pop()._result
because the prior call to _result_or_cancel
guarantees that at this point the result is available.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I understand that we could possibly exceed buffersize
while collecting the next result, is there a real-word use case where it would really cause an issue? the reason is that we access to fs[-1]
and then do fs.pop()
.
I see that have a del fut
in _result_or_cancel()
but can you confirm that it's sufficient to not hold any reference to the yet-to-be-popped future?
Asking Gregory as well since he's the mp expert c: |
@picnixz sorry I re-asked your review because you made me realize that we actually don't need
I'm digging deeper into #95169 's context to check if I miss any non-tested scenario, especially regarding this:
|
yes, that's what I wanted to ask, but I'm not an expert here so i'll let you investigate first c: |
fut.cancel() | ||
finally: | ||
# Break a reference cycle with the exception in self._exception | ||
del fut |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @graingert!
Context:
As a side effect, this PR may remove the need for _result_or_cancel
(introduced in #95169). If fetching the next result raises a TimeoutError
, its future will still be in fs
and will be properly cancelled by the result_iterator
's finally
block.
Question:
Do you remember in which scenario the del fut
was required? Removing it in the current main
does not break any tests 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is if fut.result() raises an exception there's a reference cycle where fut.exception().__traceback__ -> fut.exception()
Probably worth adding a test, a git grep for no_other_refs
will find a similar one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! Will add the test 🫡
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fyi #131701 adds the test @graingert @picnixz 🙏🏻
Context recap:
If we have:
What happens when calling
next(results)
:arg
frominterable
and put a task forfn(arg)
in the buffer-> During step 2. there is
buffersize + 1
buffered tasks.This PR swaps steps 1. and 2. so that
buffersize
is never exceeded, even duringnext
.concurrent.futures.Executor.map
temporarily exceeds itsbuffersize
while collecting the next result #131466