Skip to content
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

gh-104812: Run Pending Calls in any Thread #104813

Merged

Conversation

ericsnowcurrently
Copy link
Member

@ericsnowcurrently ericsnowcurrently commented May 23, 2023

For a while now, pending calls only run in the main thread (in the main interpreter). This PR changes things to allow any thread run a pending call, unless the pending call was explicitly added for the main thread to run.

(For reviewers: almost all of the significant change is in ceval_gil.c. The bulk of this PR's diff is comprised of tests and a long comment about the eval breaker.)

@ericsnowcurrently

This comment was marked as outdated.

@ericsnowcurrently ericsnowcurrently added the 🔨 test-with-buildbots Test PR w/ buildbots; report in status section label Jun 2, 2023
@bedevere-bot
Copy link

🤖 New build scheduled with the buildbot fleet by @ericsnowcurrently for commit fdde46d 🤖

If you want to schedule another build, you need to add the 🔨 test-with-buildbots label again.

@bedevere-bot bedevere-bot removed the 🔨 test-with-buildbots Test PR w/ buildbots; report in status section label Jun 2, 2023
@ericsnowcurrently
Copy link
Member Author

@markshannon, did you have a chance to look this over?

@ericsnowcurrently ericsnowcurrently added the needs backport to 3.12 bug and security fixes label Jun 7, 2023
Copy link
Member

@markshannon markshannon left a comment

Choose a reason for hiding this comment

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

This seems fine, but I'm not an expert on this, so another review would be appreciated

Python/ceval.c Outdated
* - signal handling (only in the main thread)
*
* When the need for one of the above is detected, the eval loop
* calls _Py_HandlePending() (from ceval_gil.c). Then, if that
Copy link
Member

Choose a reason for hiding this comment

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

I wouldn't explain how it works in terms of specific functions and files, as the explanation will be out of date all too soon.

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed

Python/ceval.c Outdated
*
* One consequence of this approach is that it can be tricky
* to force any specific thread to pick up the eval breaker,
* or for any specific thread to not pick it up.
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 true? The delay from setting the eval_breaker to the running thread responding to it should be in the order of a microsecond in the interpreter.

The problem is long running C extensions that don't check for interrupts. But that was a problem historically 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.

I wrote from experience. 😄 It wasn't obvious to me how to write tests that ran pending calls in specific threads. I'll elaborate.

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

Python/ceval.c Outdated
* "handle_eval_breaker" label. Some instructions come here
* explicitly (goto) and some indirectly via the CHECK_EVAL_BREAKER
* macro (see ceval_macros.h). Notably, the check happens at the
* end of the JUMP_BACKWARD instruction, which pretty much applies
Copy link
Member

Choose a reason for hiding this comment

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

Say, "on back edges in the control flow graph", rather than mentioning the specific bytecode.

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

&& _Py_ThreadCanHandlePendingCalls())
| (_Py_atomic_load_relaxed_int32(&ceval2->pending.calls_to_do))
| (_Py_IsMainThread() && _Py_IsMainInterpreter(interp)
&&_Py_atomic_load_relaxed_int32(&ceval->pending_mainthread.calls_to_do))
Copy link
Member

Choose a reason for hiding this comment

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

COMPUTE_EVAL_BREAKER is getting unwieldy. Any thoughts on how to clean it up? (for another PR)

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 have any recommendations at the moment, but agree it would be worth at least thinking of alternatives. That said, the current code is fairly straightforward (a mostly flat enumeration of conditions), which helps readers to follow it at a glance.

interp = _PyInterpreterState_Main();
}
return _PyEval_AddPendingCall(interp, func, arg);
/* Legacy users of this API will continue to target the main thread. */
Copy link
Member

Choose a reason for hiding this comment

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

Maybe clarify?
This means the "the main thread of the main interpreter", not the "the main thread of the current interpreter"?

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

@@ -95,11 +96,11 @@ RESET_GIL_DROP_REQUEST(PyInterpreterState *interp)


static inline void
SIGNAL_PENDING_CALLS(PyInterpreterState *interp)
SIGNAL_PENDING_CALLS(struct _pending_calls *pending, PyInterpreterState *interp)
Copy link
Member

Choose a reason for hiding this comment

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

Want to make this and the unsignal version less SHOUTY, as it isn't a macro anymore?

Copy link
Member

Choose a reason for hiding this comment

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

Also, the name "SIGNAL" is a bit confusing as this doesn't handle signals, but code around here does.

Copy link
Member Author

Choose a reason for hiding this comment

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

The same applies to all the other static inline functions that used to be macros. I'd rather leave renaming them all to a separate change. I'm not sure the churn is worth it either.

calls are still only done in the main thread, ergo in the main interpreter.
This change does not affect the existing public C-API
(``Py_AddPendingCall()``) which still only targets the main thread. The new
functionality is meant strictly for internal use. This change brings the
Copy link
Member

Choose a reason for hiding this comment

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

Why is it "meant strictly for internal use"? What bad things happen if a third party uses 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.

Pending calls can run anything and cause state changes outside the normal flow of execution. Consequently, it needs to be used carefully because it isn't clear what the potential risks are. So until we take the time to figure all that out, we are keeping it strictly internal-only.

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've clarified the note.

@ericsnowcurrently
Copy link
Member Author

Unless there are any objections, I plan on merging this later today.

@ericsnowcurrently ericsnowcurrently merged commit 757b402 into python:main Jun 13, 2023
19 checks passed
@miss-islington
Copy link
Contributor

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

@ericsnowcurrently ericsnowcurrently deleted the per-interpreter-pending-calls branch June 13, 2023 21:02
@ericsnowcurrently ericsnowcurrently added needs backport to 3.12 bug and security fixes and removed needs backport to 3.12 bug and security fixes labels Jun 13, 2023
@miss-islington
Copy link
Contributor

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

@miss-islington
Copy link
Contributor

Sorry @ericsnowcurrently, I had trouble checking out the 3.12 backport branch.
Please retry by removing and re-adding the "needs backport to 3.12" label.
Alternatively, you can backport using cherry_picker on the command line.
cherry_picker 757b402ea1c2c6b925a55a08fd844b065b6e082f 3.12

ericsnowcurrently added a commit to ericsnowcurrently/cpython that referenced this pull request Jun 13, 2023
For a while now, pending calls only run in the main thread (in the main interpreter).  This PR changes things to allow any thread run a pending call, unless the pending call was explicitly added for the main thread to run.
@bedevere-bot
Copy link

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

@bedevere-bot bedevere-bot removed the needs backport to 3.12 bug and security fixes label Jun 13, 2023
ericsnowcurrently added a commit to ericsnowcurrently/cpython that referenced this pull request Jun 13, 2023
For a while now, pending calls only run in the main thread (in the main interpreter).  This PR changes things to allow any thread run a pending call, unless the pending call was explicitly added for the main thread to run.
@ericsnowcurrently
Copy link
Member Author

FYI, I'm fixing the wasm buildbots.

ericsnowcurrently added a commit that referenced this pull request Jun 14, 2023
)

For a while now, pending calls only run in the main thread (in the main interpreter).  This PR changes things to allow any thread run a pending call, unless the pending call was explicitly added for the main thread to run.
(cherry picked from commit 757b402)
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.

None yet

4 participants