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-124622: Add PyThreadState_Ensure() function by vstinner · Pull Request #130012 · python/cpython

Conversation

@vstinner

@vstinner vstinner commented Feb 11, 2025 •
edited by github-actions Bot
Loading

Uh oh!

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

Copy link
Copy Markdown
Member

Add PyThreadState_Ensure() and PyThreadState_Release() functions.

Add new "OrFail" internal functions:

  • _PyEval_AcquireLockOrFail()
  • _PyEval_RestoreThreadOrFail()
  • _PyThreadState_AttachOrFail()
  • take_gil_or_fail()
  • Issue: Fail PyGILState_Ensure() If Finalizing? #124622

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

Sorry, something went wrong.

Uh oh!

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

All reactions
@vstinner
pythongh-124622: Add PyThreadState_Ensure() function
e46b723
Add PyThreadState_Ensure() and PyThreadState_Release() functions.

Add new "OrFail" internal functions:

* _PyEval_AcquireLockOrFail()
* _PyEval_RestoreThreadOrFail()
* _PyThreadState_AttachOrFail()
* take_gil_or_fail()
@vstinner
vstinner requested review from a team, encukou and ericsnowcurrently as code owners February 11, 2025 18:56
@bedevere-app bedevere-app Bot added the awaiting core review label Feb 11, 2025
@bedevere-app bedevere-app Bot mentioned this pull request Feb 11, 2025
Fail PyGILState_Ensure() If Finalizing? #124622
Open
@vstinner vstinner mentioned this pull request Feb 11, 2025
gh-124622: Add PyGILState_EnsureOrFail() function #129688
Closed
ZeroIntensity
ZeroIntensity reviewed Feb 11, 2025
View reviewed changes

@ZeroIntensity ZeroIntensity left a comment

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.

I'm a lot happier with this 😄! When this was first discussed a few months ago, Petr suggested using PyStatus as the return value to differentiate between failure reasons. So, something like this:

static int
my_thread(PyInterpreterState *interp)
{
    int state;
    PyStatus status = PyThreadState_Ensure(interp, &state);
    if (PyStatus_Exception(status)) {
        // Log the error somehow
        /* ... */
    }
    PyThreadState_Release(state);
}

I think that could be handy, especially for applications that want to continue running instead of just bailing out of the thread.

Sorry, something went wrong.

Uh oh!

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

All reactions
Comment thread Python/pystate.c Outdated
PyGILState_STATE
PyGILState_Ensure(void)
{
PyInterpreterState *interp = _PyRuntime.gilstate.autoInterpreterState;

@ZeroIntensity ZeroIntensity Feb 11, 2025 •
edited
Loading

Uh oh!

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

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.

If it's NULL, we should probably just fatal error or hang the thread.

Sorry, something went wrong.

Uh oh!

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

All reactions

@rruuaanng rruuaanng Feb 14, 2025

Copy link
Copy Markdown
Contributor

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.

Maybe we could add an assert.

Sorry, something went wrong.

Uh oh!

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

All reactions

@ZeroIntensity ZeroIntensity Feb 14, 2025

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.

That doesn't help much. Release builds will crash at finalization with spurious NULL derefs if we don't add a case for it.

Sorry, something went wrong.

Uh oh!

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

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

@vstinner vstinner Feb 16, 2025

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.

If possible, I would prefer to not change PyGILState_Ensure() in this PR which ("only") adds PyThreadState_Ensure().

Sorry, something went wrong.

Uh oh!

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

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

@ZeroIntensity ZeroIntensity Feb 17, 2025

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.

I'd be happy to do that in a follow-up then.

Sorry, something went wrong.

Uh oh!

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

vstinner reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
Comment thread Python/pystate.c Resolved

Uh oh!

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

Comment thread Python/pystate.c Outdated


void
PyThreadState_Release(int oldstate)

@ZeroIntensity ZeroIntensity Feb 11, 2025

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.

Thinking out loud here: instead of manually passing an int around indicating the number of PyThreadState_Ensure calls, why not just store that number on the thread state itself?

Sorry, something went wrong.

Uh oh!

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

All reactions

@vstinner vstinner Feb 16, 2025

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.

The oldstate is either PyGILState_UNLOCKED or PyGILState_LOCKED, it's not a counter.

Sorry, something went wrong.

Uh oh!

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

All reactions

@ZeroIntensity ZeroIntensity Feb 17, 2025 •
edited
Loading

Uh oh!

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

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.

Oh, hm. Why do we need PyGILState here at all? The thread state should pretty much manage itself, there just needs to be a call to PyThreadState_Clear and PyThreadState_DeleteCurrent at the end (which is why I thought it would be done with a counter of calls).

Sorry, something went wrong.

Uh oh!

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

All reactions

@vstinner vstinner Feb 17, 2025

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.

I understand that PyThreadState_Release() does basically nothing if oldstate is PyGILState_LOCKED: if the thread already hold the GIL when PyThreadState_Ensure() was called.

Sorry, something went wrong.

Uh oh!

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

All reactions

@ZeroIntensity ZeroIntensity Feb 17, 2025

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.

I might just be used to dealing with thread states the subinterpreter way, but I think it could be worth avoiding any ints entirely in PyThreadState_Ensure. If we add a new field to PyThreadState (e.g., ensured) which is incremented for a call to PyThreadState_Ensure and then decremented upon a call to PyThreadState_Release, we could automatically clear and delete the thread state when that number hits zero from PyThreadState_Release. I think we should discuss this elsewhere, though. I think I'll create the WG discussion.

Sorry, something went wrong.

Uh oh!

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

All reactions

@vstinner vstinner Feb 27, 2025

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.

The problem is that you can have nested calls to PyThreadState_Ensure(). How do you store such stack of states in PyThreadState?

Sorry, something went wrong.

Uh oh!

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

All reactions

@ZeroIntensity ZeroIntensity Feb 27, 2025 •
edited
Loading

Uh oh!

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

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.

I was thinking we could set up a field for a linked list, so PyThreadState_Ensure/PyThreadState_Release would look something along the lines of:

int
PyThreadState_Ensure(PyInterpreterState *interp)
{
    PyThreadState *current = current_fast_get();
    if (current == NULL) {
        // Make a new thread state for *interp* and return it
        /* ... */
    }
    else {
        PyThreadState *new_tstate = _PyThreadState_NewBound(interp);
        assert(new_tstate->ensured == NULL);
        new_tstate->ensured = current;
        _PyThreadState_Detach(current);
        /* AttachOrFail(new_tstate) ... */
    }
}

void
PyThreadState_Release()
{
    PyThreadState *current = current_fast_get();
    _Py_EnsureTstateNotNULL(current);
    PyThreadState *to_restore = current->ensured;
    _PyThreadState_Detach(current);
    if (to_restore != NULL) {
        /* AttachOrFail(to_restore) */
    }
    // Nothing to restore, leave no attached tstate
    assert(current_fast_get() == NULL);
}
  • We probably need a return value for PyThreadState_Release because it should be able to reattach to the previous thread state if it didn't have the requested interpreter (regardless of any linked list).
  • If we go with my linked list idea, it would be nice to have a fast-path which does nothing if there's already an existing thread state that matches interp.

Sorry, something went wrong.

Uh oh!

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

All reactions
@vstinner
Use current_fast_get()
af5b1b1
@ZeroIntensity ZeroIntensity mentioned this pull request Feb 17, 2025
A new API for ensuring/releasing thread states capi-workgroup/decisions#58
Closed
vstinner added 3 commits February 18, 2025 16:38
@vstinner
should => must
eaa125f
@vstinner
Add 'errmsg' parameter
1e3762a
@vstinner
Document errmsg
7455fd5
@vstinner

vstinner commented Feb 27, 2025

Copy link
Copy Markdown
Member Author

I changed the API to int PyThreadState_Ensure(PyInterpreterState *interp, const char **errmsg): set an error message (if errmsg is not NULL) on error.

Currently, the only error message is: "Python is being finalized".

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

Sorry, something went wrong.

Uh oh!

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

ZeroIntensity
ZeroIntensity reviewed Feb 27, 2025
View reviewed changes

@ZeroIntensity ZeroIntensity left a comment

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.

Thanks for doing this, by the way :)

Sorry, something went wrong.

Uh oh!

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

All reactions
Comment thread Python/pystate.c Resolved

Uh oh!

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

Comment thread Python/pystate.c Resolved

Uh oh!

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

vstinner added 6 commits March 10, 2025 13:49
@vstinner
start by setting *errmsg to NULL
1c3669a
@vstinner
Reject tstate from another interpreter
178507e
@vstinner
No longer share code with PyGILState
325309c
Add PyThreadState.ensure_depth member.
@vstinner
Add tests
820d9fe
@vstinner
Complete test
7ba7aa0
@vstinner
Update comment
783d91f
colesbury
colesbury reviewed Mar 11, 2025
View reviewed changes
Comment thread Python/pystate.c
Comment on lines +2874 to +2876
if (tcur != NULL && tcur->interp != interp) {
// The current thread state is from another interpreter
tcur = NULL;

@colesbury colesbury Mar 11, 2025

Copy link
Copy Markdown
Contributor

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 think PyThreadState_Release is missing code to restore the previous thread state for this case where the active thread state was from a different interpreter.

Sorry, something went wrong.

Uh oh!

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

All reactions

@ZeroIntensity ZeroIntensity Mar 11, 2025

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.

It is, but the design is still being discussed. This PR is, more or less, a draft until the C API WG approves it.

Sorry, something went wrong.

Uh oh!

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

All reactions
@vstinner

vstinner commented Mar 20, 2025

Copy link
Copy Markdown
Member Author

I prefer to close this PR for now until we agree on the API semantics.

ZeroIntensity and rruuaanng reacted with thumbs up emoji
All reactions
  • 👍 2 reactions

Sorry, something went wrong.

Uh oh!

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

@vstinner vstinner closed this Mar 20, 2025
@vstinner
vstinner deleted the tstate_ensure branch March 20, 2025 11:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@colesbury colesbury colesbury left review comments
@ZeroIntensity ZeroIntensity ZeroIntensity left review comments
@encukou encukou Awaiting requested review from encukou encukou is a code owner
@ericsnowcurrently ericsnowcurrently Awaiting requested review from ericsnowcurrently ericsnowcurrently is a code owner
+1 more reviewer
@rruuaanng rruuaanng rruuaanng left review comments
Reviewers whose approvals may not affect merge requirements

Uh oh!

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

Assignees

No one assigned

Labels

awaiting core review

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

@vstinner @colesbury @ZeroIntensity @rruuaanng
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.