Skip to content

gh-110205: Fix asyncio ThreadedChildWatcher._join_threads() #110790

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

Closed
wants to merge 3 commits into from

Conversation

vstinner
Copy link
Member

@vstinner vstinner commented Oct 13, 2023

ThreadedChildWatcher._join_threads() now clears references to completed threads.

test_asyncio.utils.TestCase now calls _join_threads() of the watcher, uses SHORT_TIMEOUT to join a thread, and then raises an exception if there are still running threads.

Rename also ThreadedChildWatcher threads to add "asyncio-" prefix to the name.

ThreadedChildWatcher._join_threads() now clears references to
completed threads.

test_asyncio.utils.TestCase now calls _join_threads() of the watcher,
uses SHORT_TIMEOUT to join a thread, and then raises an exception if
there are still running threads.

Rename also ThreadedChildWatcher threads to add "asyncio-" prefix to
the name.
Copy link
Member

@gvanrossum gvanrossum left a comment

Choose a reason for hiding this comment

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

Is there any advantage to using the internal API _join_threads() instead of the internal variable _threads? You could just add the timeout arg to the join() call in the existing test cleanup code.

Comment on lines 1384 to 1385
self.threads = [thread for thread in list(self._threads.values())
if thread.is_alive() and not thread.daemon]
Copy link
Member

Choose a reason for hiding this comment

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

Why are you creating a new instance variable self.threads here? Did you intend to clean out completed threads from the dict self._threads?

Copy link
Member Author

@vstinner vstinner Oct 13, 2023

Choose a reason for hiding this comment

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

I used the list comprehension above as an example, for me it looks easier to remove completed threads rather than having a regular list and removing items from the list.

The purpose of this line is to remove completed threads to save memory and to fix the dangling threads issue.

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 mean that you prefer to not create a new list?

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 modified my PR to use self.threads[:] = ..., so the self.threads list object is not replacd.

Copy link
Member

Choose a reason for hiding this comment

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

Do you mean that you prefer to not create a new list?

No I meant that the tests fail because self._threads is a dict, not a list.

@vstinner
Copy link
Member Author

Is there any advantage to using the internal API _join_threads() instead of the internal variable _threads?

It fix the issue related to the PR: dangling thread. The warning can be emitted when the Python object still exist, even if the thread is no longer running. I don't think that copy/paste the _join_threads() logic in the test is worth it.

@gvanrossum
Copy link
Member

Please take a break and read the comments and the code. There is no variable self.threads.

@vstinner
Copy link
Member Author

Please take a break and read the comments and the code. There is no variable self.threads.

Ah, I misunderstood your comment. I fixed the typo, the code now sets self._threads.

@vstinner vstinner closed this Oct 13, 2023
@vstinner vstinner deleted the asyncio_clear_watcher_threads branch October 13, 2023 17:53
Comment on lines +1384 to +1385
self._threads[:] = [thread for thread in list(self._threads.values())
if thread.is_alive() and not thread.daemon]
Copy link
Member

Choose a reason for hiding this comment

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

As I was trying to let you see for yourself, self._threads is a dict, not a list. If you want to update it using a comprehension you can use something like this:

self._threads = {key: thread for key, thread in self._threads.items() if thread.daemon or thread.is_alive()}

Please test locally before merging.

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

Successfully merging this pull request may close these issues.

2 participants