Skip to content

Conversation

@vstinner
Copy link
Member

@vstinner vstinner commented Dec 6, 2018

If a multiprocessing.Pool is not joined explicitly, it now emits a
ResourceWarning warning.

  • Add INIT state
  • Pool state now initialized to INIT and only set to RUN when
    Pool.init() completes successfully.
  • Initialize some attributes earlier int Pool.init() to ensure
    that they exist in Pool.del()

https://bugs.python.org/issue35424

Copy link
Member

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?

Copy link
Member Author

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.

Copy link
Member

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()

Copy link
Member Author

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.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Contributor

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?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Read previous comments.

Copy link
Member

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.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this "#" on purpose?

Copy link
Member Author

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?

Copy link
Member

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 '#'.

Copy link
Member Author

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?

Copy link
Member

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 '#' :)

Copy link
Member

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".

@vstinner
Copy link
Member Author

vstinner commented Dec 7, 2018

tzickel:

This only works if you've called pool.close() or pool.terminate(). just deleting the pool references will not call to del due to a resource leak.

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 :-)

@vstinner vstinner changed the title [WIP] bpo-35424: multiprocessing.Pool emits ResourceWarning bpo-35424: multiprocessing.Pool emits ResourceWarning Dec 11, 2018
@vstinner vstinner requested a review from pitrou December 11, 2018 16:29
Copy link
Member

@pitrou pitrou left a 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.

Copy link
Member

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.

Copy link
Member

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".

@bedevere-bot
Copy link

When you're done making the requested changes, leave the comment: I have made the requested changes; please review again.

@vstinner
Copy link
Member Author

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.
@vstinner
Copy link
Member Author

Since I created this PR, multiple changes have been merged:

  • test_del_pool() has been removed
  • I added Pool.repr() and state constants are now strings
  • I fixed all multiprocessing tests to call .terminate() (use a context manager) and .join()

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__()
Copy link
Member

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)

Copy link
Member Author

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.

Copy link
Member

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...

Copy link
Member Author

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 :-)

Copy link
Member Author

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?

Copy link
Member

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:
Copy link
Member

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?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@pitrou pitrou changed the title bpo-35424: multiprocessing.Pool emits ResourceWarning bpo-35424: emit ResourceWarning at multiprocessing.Pool destruction Dec 20, 2018
@vstinner vstinner merged commit 9a8d1d7 into python:master Dec 20, 2018
@vstinner vstinner deleted the mp_pool_reswarn branch December 20, 2018 19:33
@vstinner
Copy link
Member Author

Thanks for the review @pitrou!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants