Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

gh-87135: Raise PythonFinalizationError when joining a blocked daemon thread#130402

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
encukou merged 9 commits intopython:mainfromencukou:no-join-in-finalize
Apr 28, 2025

Conversation

encukou
Copy link
Member

@encukouencukou commentedFeb 21, 2025
edited by github-actionsbot
Loading

IfPy_IsFinalizing() is true, non-daemon threads (other than the current one) are done, and daemon threads are prevented from acquiring GIL (or thread state), so they cannot finalize themselves and become done. Joining them without timeout would block forever.

Raise PythonFinalizationError instead of hanging.

Seegh-123940 for a real-world use case: callingjoin() from__del__.
Doing this is still ill-advised, but an exception should at least make the issue easier to diagnose.


📚 Documentation preview 📚:https://cpython-previews--130402.org.readthedocs.build/

…daemon threadIf `Py_IsFinalizing()` is true, non-daemon threads (other than the current one)are done, and daemon threads are prevented from running, so theycannot finalize themselves and become done. Joining them without timeoutwould block forever.Raise PythonFinalizationError instead of hanging.Seepythongh-123940 for a use case: calling `join()` from `__del__`. This isill-advised, but an exception should at least make it easier to diagnose.
@colesbury
Copy link
Contributor

This seems like a good idea to me.

  • What doesthread.is_alive() return?
  • I think the exception should not be conditional on not having a timeout specified. There's no way it can succeed, so we should just raise the exception immediately like we do when trying to join your own thread. You can also end up withthreading.join(timeout=...) calls in aninfinite loop.

@vstinner
Copy link
Member

I think the exception should not be conditional on not having a timeout specified

I agree with Sam.

@encukou
Copy link
MemberAuthor

What doesthread.is_alive() return?

True. Threads that are already done can be joined normally.

I think the exception should not be conditional on not having a timeout specified.

In a finalizer, wouldn't it be OK to wait a bit for graceful termination (usingjoin with a timeout), and then do some teardown regardless of whether the thread survived?
(If Python is being finalized, the thread would of course always survive -- but you might not be writing the code only for that case.)

Raising an exception would mean you skip that teardown, unless you have atry/except aroundjoin.

IOW, to me, the reasoning is not as clear-cut here as in the “hang the only Python thread that can run” case.

You can also end up with threading.join(timeout=...) calls in aninfinite loop.

Well... You can even write an infinite loop without any join at all! :)
I guess I'm not trying toprevent hangs entirely, just make them easier to diagnose. IMO, awhile thread.is_alive() loop is much easier to grok than an internal lock becoming un-acquirable.


Iwrote the code to hang even with timeout; I'll update the PR if you still think that's the way to go.

@encukou
Copy link
MemberAuthor

Updated to raise even with timeout.

if (ThreadHandle_ident(self) == PyThread_get_thread_ident_ex()) {
// PyThread_join_thread() would deadlock or error out.
PyErr_SetString(ThreadError, "Cannot join current thread");
return -1;
}
if (Py_IsFinalizing()) {
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 code path taken by all threads, or only daemon threads?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

It's taken by the thread that calledPy_FinalizeEx.
WhenPy_IsFinalizing is true, all other threads than the one that calledPy_FinalizeEx are daemonic and they cannot call Python API (includingThreadHandle_join).
So,self must be a daemon thread.

@encukou
Copy link
MemberAuthor

I'll merge on ~Friday if there are no objections.

@encukou
Copy link
MemberAuthor

... And I went offline for a month after writing that.

I'm back now; merging.

@encukouencukou merged commit4ebbfcf intopython:mainApr 28, 2025
42 checks passed
@encukouencukou deleted the no-join-in-finalize branchApril 28, 2025 13:48
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@vstinnervstinnervstinner left review comments

Assignees
No one assigned
Labels
None yet
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

3 participants
@encukou@colesbury@vstinner

[8]ページ先頭

©2009-2025 Movatter.jp