GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-109793: Allow Switching Interpreters During Finalization by ericsnowcurrently · Pull Request #109794 · python/cpython

Conversation

@ericsnowcurrently

@ericsnowcurrently ericsnowcurrently commented Sep 23, 2023 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

Essentially, we should check the thread ID rather than the thread state pointer.

  • Issue: PyThreadState_Swap() During Finalization Causes Immediate Exit (AKA Daemon Threads Are Still the Worst!) #109793

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
ericsnowcurrently added 2 commits September 23, 2023 13:56
@ericsnowcurrently
Add a test.
5e2224f
@ericsnowcurrently
Also check the thread ID in _PyThreadState_MustExit().
a1014a9
@ericsnowcurrently ericsnowcurrently added the needs backport to 3.12 only security fixes label Sep 23, 2023
@bedevere-app bedevere-app Bot mentioned this pull request Sep 23, 2023
PyThreadState_Swap() During Finalization Causes Immediate Exit (AKA Daemon Threads Are Still the Worst!) #109793
Closed
ericsnowcurrently added 4 commits September 23, 2023 14:10
@ericsnowcurrently
Use _PyRuntime directly in _Py_IsMainInterpreterFinalizing().
d46103d
@ericsnowcurrently
Add _PyRuntimeState._finalizing_id and _PyInterpreterState._finalizin…
2e13a75
…g_id.
@ericsnowcurrently
Add verbose output to the test.
756cc3b
@ericsnowcurrently
Add a NEWS entry.
ad312b9
@ericsnowcurrently ericsnowcurrently mentioned this pull request Sep 25, 2023
sys.path[0] Is Set Differently From the Rest of sys.path #109853
Open
@ericsnowcurrently
Skip the assert for now.
8feeb4c
@ericsnowcurrently
ericsnowcurrently marked this pull request as ready for review September 25, 2023 17:19
@bedevere-app bedevere-app Bot added the awaiting review label Sep 25, 2023
markshannon
markshannon reviewed Sep 26, 2023
View reviewed changes
Comment thread Include/internal/pycore_atomic.h Outdated
atomic_uintptr_t _value;
} _Py_atomic_address;

typedef struct _Py_atomic_ulong {

@markshannon markshannon Sep 26, 2023

Copy link
Copy Markdown
Member

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.

Could you use either _Py_atomic_load_uint32 or _Py_atomic_load_uint64 instead of defining your own.
Since thread id is an unsigned long, you'll need to use a macro, but it seems better to use the pre-existing functions.

#if SIZEOF_LONG == 8
#define _Py_atomic_load_ulong _Py_atomic_load_uint64
#elif SIZEOF_LONG == 4
#define _Py_atomic_load_ulong _Py_atomic_load_uint32
#else
#error "long must be 4 or 8 bytes in size"
#endif

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

ericsnowcurrently reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

@ericsnowcurrently ericsnowcurrently Sep 27, 2023

Copy link
Copy Markdown
Member Author

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

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
ericsnowcurrently added 2 commits September 27, 2023 10:04
@ericsnowcurrently
Use the new _Py_atomic API.
9e589a7
@ericsnowcurrently
Fix the NEWS entry.
9829315
@ericsnowcurrently
ericsnowcurrently merged commit 32466c9 into python:main Sep 27, 2023
@miss-islington

miss-islington commented Sep 27, 2023

Copy link
Copy Markdown
Contributor

Thanks @ericsnowcurrently for the PR 🌮🎉.. I'm working now to backport this PR to: 3.12.
🐍🍒⛏🤖

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-app bedevere-app Bot removed the awaiting review label Sep 27, 2023
@miss-islington

miss-islington commented Sep 27, 2023

Copy link
Copy Markdown
Contributor

Sorry, @ericsnowcurrently, I could not cleanly backport this to 3.12 due to a conflict.
Please backport using cherry_picker on command line.
cherry_picker 32466c97c06ee5812923d695195394c736eeb707 3.12

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@miss-islington miss-islington assigned ericsnowcurrently Sep 27, 2023
@ericsnowcurrently
ericsnowcurrently deleted the fix-finalization-other-tstate branch September 27, 2023 19:49
csm10495 pushed a commit to csm10495/cpython that referenced this pull request Sep 28, 2023
@ericsnowcurrently @csm10495
pythongh-109793: Allow Switching Interpreters During Finalization (py…
93da346
…thongh-109794)

Essentially, we should check the thread ID rather than the thread state pointer.
@sobolevn sobolevn mentioned this pull request Sep 28, 2023
Warning: incompatible-pointer-types in pycore_pylifecycle.h #110024
Closed
@ericsnowcurrently ericsnowcurrently mentioned this pull request Sep 28, 2023
gh-110024: Fix Pointer Type Warnings #110053
Merged
@vstinner

vstinner commented Sep 28, 2023

Copy link
Copy Markdown
Member

Sorry, @ericsnowcurrently, I could not cleanly backport this to 3.12 due to a conflict.
Please backport using cherry_picker on command line.
cherry_picker 32466c9 3.12

Is it really reasonable to backport this major change in a stable branch? IMO it's too late to change that, no?


This change introduced a regression: Python started to crash again randomly at exit when they are daemon threads: see issue gh-110052. I confirmed (by manual testing) that the commit 32466c9 introduced the regression:

  • Before this commit, I cannot reproduce the issue in 5 minutes.
  • At this commit, I easily reproduce the issue in less than a minute.

@ericsnowcurrently: Please have a look. If you don't have the bandwidth to repair the regression, I will revert soon the change.

This code is very fragile :-( I fixed multiple bugs to try to handle all cases:

  • https://vstinner.github.io/gil-bugfixes-daemon-threads-python39.html
  • https://vstinner.github.io/daemon-threads-python-finalization-python32.html

Note: By the way, recently, I fixed a race condition in _thread.start_new_thread(), but it doesn't seem to be related: commit 517cd82.


Before the commit, the test was fine:

vstinner@mona$ ./python -m test test_threading -m test_4_daemon_threads -j50 -F --fail-env-changed  
(...)
0:07:34 load avg: 91.03 [1640] test_threading passed
0:07:34 load avg: 91.03 [1641] test_threading passed
^C

A system load of 91.03 is high knowning that my laptop CPU has 6 cores / 12 threads.


I'm not comfortable with this change which has comments like:

        // XXX This isn't completely safe from daemon thraeds,
        // since tstate might be a dangling pointer.

Well, if it's not safe: don't do it.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Sep 28, 2023

Copy link
Copy Markdown
Member

Note: _PyRuntimeState_GetFinalizing() uses the "old" pycore_atomic.h for API, whereas _PyRuntimeState_GetFinalizingID() uses the "new" pyatomic.h API.

static inline PyThreadState*
_PyRuntimeState_GetFinalizing(_PyRuntimeState *runtime) {
    // pycore_atomic.h                                                    
    return (PyThreadState*)_Py_atomic_load_relaxed(&runtime->_finalizing);     
}

static inline unsigned long
_PyRuntimeState_GetFinalizingID(_PyRuntimeState *runtime) {        
    // pyatomic.h
    return _Py_atomic_load_ulong_relaxed(&runtime->_finalizing_id);         
}                  
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner vstinner mentioned this pull request Sep 28, 2023
test_threading.test_4_daemon_threads() crash randomly #110052
Closed
@ericsnowcurrently

ericsnowcurrently commented Sep 28, 2023

Copy link
Copy Markdown
Member Author

I'm looking into the crashes right now.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ericsnowcurrently

ericsnowcurrently commented Sep 28, 2023

Copy link
Copy Markdown
Member Author

At this commit, I easily reproduce the issue in less than a minute.

I'm going to follow up on gh-110052.

I'm not comfortable with this change which has comments like:

   // XXX This isn't completely safe from daemon thraeds,
  // since tstate might be a dangling pointer.

Well, if it's not safe: don't do it.

That's a comment I added about existing code. 😄

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Sep 28, 2023

Copy link
Copy Markdown
Member

That's a comment I added about existing code. 😄

In Python 3.11, _PyThreadState_MustExit() does not deference tstate because it can be a dangling pointer: it's explained in the comment.

// Check if a Python thread must exit immediately, rather than taking the GIL
// if Py_Finalize() has been called.
//              
// When this function is called by a daemon thread after Py_Finalize() has been
// called, the GIL does no longer exist.
//              
// tstate can be a dangling pointer (point to freed memory): only tstate value
// is used, the pointer is not deferenced.
//              
// tstate must be non-NULL.
int         
_PyThreadState_MustExit(PyThreadState *tstate)
{           
    /* bpo-39877: Access _PyRuntime directly rather than using
       tstate->interp->runtime to support calls from Python daemon threads.
       After Py_Finalize() has been called, tstate can be a dangling pointer:
       point to PyThreadState freed memory. */
    PyThreadState *finalizing = _PyRuntimeState_GetFinalizing(&_PyRuntime);
    return (finalizing != NULL && finalizing != tstate);
}

By the way, in the main branch, the function still has this comment:

// tstate can be a dangling pointer (point to freed memory): only tstate value
// is used, the pointer is not deferenced.
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ericsnowcurrently

ericsnowcurrently commented Sep 28, 2023

Copy link
Copy Markdown
Member Author

Is it really reasonable to backport this major change in a stable branch? IMO it's too late to change that, no?

The main motivation is to address the situation where a subinterpreter gets cleaned up while the runtime is finalizing. Without this fix, the process immediately exits with an exitcode of 0, even if it would have exited with 1 (and the rest of runtime finalization is skipped). That seems like it makes a backport worth it.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ericsnowcurrently

ericsnowcurrently commented Sep 28, 2023

Copy link
Copy Markdown
Member Author

In Python 3.11, _PyThreadState_MustExit() does not deference tstate because it can be a dangling pointer

I added the _Py_IsInterpreterFinalizing(tstate->interp) check in 26baa74 (May 2023). A dangling pointer with that check isn't an issue during runtime finalization since we just verified that the runtime isn't finalizing yet.

The cases where it could be a problem are strictly where a subinterpreter has daemon threads. That is unlikely since subinterpreters have daemon threads disabled by default.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

ericsnowcurrently added a commit that referenced this pull request Sep 29, 2023
@ericsnowcurrently
gh-110024: Fix Pointer Type Warnings (gh-110053)
6364873
The warnings were introduced by gh-109794 (for gh-109793).
@ericsnowcurrently

ericsnowcurrently commented Sep 29, 2023

Copy link
Copy Markdown
Member Author

FYI, any backport will need to include the fix in gh-110053 (6364873). Likewise, any revert will need to revert that change.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

ericsnowcurrently added a commit to ericsnowcurrently/cpython that referenced this pull request Oct 11, 2023
@ericsnowcurrently
pythongh-109793: Allow Switching Interpreters During Finalization (py…
3765c6f
…thongh-109794)

Essentially, we should check the thread ID rather than the thread state pointer.
@bedevere-app

bedevere-app Bot commented Oct 11, 2023

Copy link
Copy Markdown

GH-110705 is a backport of this pull request to the 3.12 branch.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-app bedevere-app Bot removed the needs backport to 3.12 only security fixes label Oct 11, 2023
ericsnowcurrently added a commit that referenced this pull request Nov 28, 2023
@ericsnowcurrently
[3.12] gh-109793: Allow Switching Interpreters During Finalization (g…
82ae5a6
…h-109794) (gh-110705)

Essentially, we should check the thread ID rather than the thread state pointer.
@neonene neonene mentioned this pull request Aug 13, 2024
asserting that the tstate pointer is non-NULL using assert #122928
Closed
Glyphack pushed a commit to Glyphack/cpython that referenced this pull request Sep 2, 2024
@ericsnowcurrently @Glyphack
pythongh-109793: Allow Switching Interpreters During Finalization (py…
7a9bedd
…thongh-109794)

Essentially, we should check the thread ID rather than the thread state pointer.
Glyphack pushed a commit to Glyphack/cpython that referenced this pull request Sep 2, 2024
@ericsnowcurrently @Glyphack
pythongh-110024: Fix Pointer Type Warnings (pythongh-110053)
1271a90
The warnings were introduced by pythongh-109794 (for pythongh-109793).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@markshannon markshannon markshannon left review comments

Uh oh!

There was an error while loading. Please reload this page.

Assignees

@ericsnowcurrently ericsnowcurrently

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

4 participants

@ericsnowcurrently @miss-islington @vstinner @markshannon
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.