-
-
Notifications
You must be signed in to change notification settings - Fork 33.7k
bpo-35424: emit ResourceWarning at multiprocessing.Pool destruction #10974
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
Conversation
Lib/multiprocessing/pool.py
Outdated
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.
Should this be stopped instead of joined?
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.
I don't know how to describe the state here... close() or terminate() has been called. "was not joined" means that .join() should be called and it has not been called.
"stopped" implies that there is stop() method, no? But there is no stop() method.
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.
Sorry, I meant close() instead of stop()
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.
close() doesn't ensure that all resources are released. Currently, join() must be called to ensure that all processes and threads completed.
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.
👍
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.
I thing close is more clearly, isn't?
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.
Read previous comments.
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.
As long as the user has called terminate or close (or used a context manager), a warning shouldn't be emitted.
Lib/multiprocessing/pool.py
Outdated
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.
Is this "#" on purpose?
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.
It's on purpose. '#' means 'size' or 'count': it's the number of items (processes or threads) in the pool. It's not the right syntax?
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.
I understand now. I think I was confused about searching for some other meaning for the '#'.
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.
I can remove '#' if you prefer, I don't really care :-) Or rename to 'pool_size' maybe?
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.
I am happy with the '#' :)
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.
I'd rather have "pool size".
|
tzickel:
I wrote this PR more to ask if it's ok to start to emit ResourceWarning in the multiprocessing module. This module has multiple times, we cannot fix them all at once. This PR is a first step towards leaking less resources in corner cases / when the API is misused :-) |
pitrou
left a comment
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.
I'd like to see the semantics changed a bit. Legitimate uses shouldn't see a warning. Also I notice you didn't add any tests.
Lib/multiprocessing/pool.py
Outdated
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.
As long as the user has called terminate or close (or used a context manager), a warning shouldn't be emitted.
Lib/multiprocessing/pool.py
Outdated
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.
I'd rather have "pool size".
|
When you're done making the requested changes, leave the comment: |
|
It's now unclear to me if we should promote "with pool:" by issuing a ResourceWarning when terminate() is not called explicitly. See this discussion: [Python-Dev] Usage of the multiprocessing API and object lifetime. |
multiprocessing.Pool destructor now emits ResourceWarning if the pool is still running.
|
Since I created this PR, multiple changes have been merged:
I rewrote this PR to rebase it on master. I simplified my PR to only emit a warning if the pool is still running, but no longer if .join() has not been called. @pitrou: Would you mind to have a new look at this change? I added an unit test :-) |
| pool.terminate() | ||
| pool.join() | ||
|
|
||
| # force state to RUN to emit ResourceWarning in __del__() |
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.
Yuck. Why can't you test the actual situation? (start a pool then delete it)
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.
If you don't cleanup properly a pool, bad things happens. You can leak processes and threads and it's not easy to write a test which wait properly until they complete. If the test doesn't wait until they complete, the test can be marked as ENV_CHANGED and the whole test suite fails on buildbots. Leaking threads/processes have side effects on following tests which cause random failures. There is a "coverage" job on Travis CI which runs the full test suite sequentially for example.
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.
Well, there was a PR to clean up pools properly, but AFAIR you reverted it...
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.
I reverted https://bugs.python.org/issue34172 change because it introduced a backward incompatible change in stable branches. I also reverted the change in the master branch to open a more general discussion: https://mail.python.org/pipermail/python-dev/2018-December/155946.html
The reverted change isn't magic: it doesn't fix all issues. Even with this fix, the destructor still doesn't join many threads and so regrtest will still complain. The .join() method has to be called explicitly to really make sure that a test doesn't leak resources.
The reverted change had a test on "del pool" and this test caused issues on buildbots because it leaked resources (processes, threads). IMHO testing that the destructor calls terminate() deserves its own separated test.
In this PR, the test is restricted to check that the destructor emits a ResourceWarning.
Moreover, to come back to https://bugs.python.org/issue34172 maybe you haven't noticed, by this PR should also help https://bugs.python.org/issue34172 because the problematic example ("for x in multiprocessing.Pool().imap(int, ["4", "3"]): print(x)") should now emit a ResourceWarning (once the reference cycle will be broken again). It's better than nothing to warn developers that something is wrong :-) See also https://bugs.python.org/issue35378
... Sorry for the disgression, but it seems like you asked for it :-)
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.
Do you want me to change the test, or is it ok written like that, with the explanation in my previous comment?
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.
Fair enough, let's keep it like that :-)
| self._state = RUN | ||
|
|
||
| def __del__(self, _warn=warnings.warn): | ||
| if self._state == RUN: |
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.
Shouldn't you freeze RUN in the __del__ parameters as well?
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.
done
|
Thanks for the review @pitrou! |
If a multiprocessing.Pool is not joined explicitly, it now emits a
ResourceWarning warning.
Pool.init() completes successfully.
that they exist in Pool.del()
https://bugs.python.org/issue35424