8000 gh-112175: Add `eval_breaker` to `PyThreadState` by swtaarrs · Pull Request #115194 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

gh-112175: Add eval_breaker to PyThreadState #115194

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

Merged
merged 13 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Review comments from Sam
  • Loading branch information
swtaarrs committed Feb 9, 2024
commit f78c505783ba5961734f104e3b95384e9cabb807
7 changes: 1 addition & 6 deletions Include/internal/pycore_ceval_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,8 @@ struct _ceval_state {
/* This single variable holds the global instrumentation version and some
* interpreter-global requests to break out of the fast path in the eval
* loop. PyThreadState also contains an eval_breaker, which is the source
* of truth when a thread is running.
*
* It is by far the hottest field in this struct and should be placed at
* the beginning. */
* of truth when a thread is running. */
uintptr_t interp_eval_breaker;
/* Avoid false sharing */
int64_t padding[7];
int recursion_limit;
struct _gil_runtime_state *gil;
int own_gil;
Expand Down
1 change: 1 addition & 0 deletions Modules/signalmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1768,6 +1768,7 @@ PyErr_CheckSignals(void)
allows long running native code to clean cycles created using the C-API
even if it doesn't run the evaluation loop */
if (_PyThreadState_IsSignalled(tstate, _PY_GC_SCHEDULED_BIT)) {
_PyThreadState_Unsignal(tstate, _PY_GC_SCHEDULED_BIT);
_Py_RunGC(tstate);
}

Expand Down
12 changes: 8 additions & 4 deletions Python/ceval_gil.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ update_thread_eval_breaker(PyInterpreterState *interp, PyThreadState *tstate)
// When detaching a thread, transfer _PY_GC_SCHEDULED_BIT to its interpreter,
// in case a GC was scheduled but not processed yet.
static inline void
update_interp_eval_breaker(PyThreadState *tstate, PyInterpreterState *interp) {
update_interp_eval_breaker(PyThreadState *tstate, PyInterpreterState *interp)
{
#ifdef Py_GIL_DISABLED
// Free-threaded builds eagerly update the eval_breaker on *all* threads as
// needed, so this func 8000 tion doesn't apply.
Expand Down Expand Up @@ -661,7 +662,7 @@ PyEval_RestoreThread(PyThreadState *tstate)
*/

void
_PyEval_SignalReceived()
_PyEval_SignalReceived(void)
{
_PyThreadState_Signal(_PyRuntime.main_tstate, _PY_SIGNALS_PENDING_BIT);
}
Expand Down Expand Up @@ -715,6 +716,7 @@ _pop_pending_call(struct _pending_calls *pending,
}
}

#ifndef Py_GIL_DISABLED
static void
signal_active_thread(PyInterpreterState *interp, uintptr_t bit)
{
Expand All @@ -732,6 +734,7 @@ signal_active_thread(PyInterpreterState *interp, uintptr_t bit)
}
MUTEX_UNLOCK(gil->mutex);
}
#endif

/* This implementation is thread-safe. It allows
scheduling to be made from any thread, and even from an executing
Expand All @@ -756,7 +759,8 @@ _PyEval_AddPendingCall(PyInterpreterState *interp,

if (main_only) {
_PyThreadState_Signal(_PyRuntime.main_tstate, _PY_CALLS_TO_DO_BIT);
} else {
}
else {
#ifdef Py_GIL_DISABLED
_PyInterpreterState_SignalAll(interp, _PY_CALLS_TO_DO_BIT);
#else
Expand Down Expand Up @@ -1014,7 +1018,6 @@ _PyEval_InitState(PyInterpreterState *interp)
int
_Py_HandlePending(PyThreadState *tstate)
{
PyInterpreterState *interp = tstate->interp;
uintptr_t breaker = _Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker);

/* Stop-the-world */
Expand Down Expand Up @@ -1042,6 +1045,7 @@ _Py_HandlePending(PyThreadState *tstate)

/* GC scheduled to run */
if ((breaker & _PY_GC_SCHEDULED_BIT) != 0) {
_PyThreadState_Unsignal(tstate, _PY_GC_SCHEDULED_BIT);
_Py_RunGC(tstate);
}

Expand Down
1 change: 0 additions & 1 deletion Python/gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1796,7 +1796,6 @@ _PyObject_GC_Link(PyObject *op)
void
_Py_RunGC(PyThreadState *tstate)
{
_PyThreadState_Unsignal(tstate, _PY_GC_SCHEDULED_BIT);
gc_collect_main(tstate, GENERATION_AUTO, _Py_GC_REASON_HEAP);
}

Expand Down
3 changes: 1 addition & 2 deletions Python/gc_free_threading.c
Original file line number Diff line number Diff line change
Expand Up @@ -1482,7 +1482,7 @@ PyObject_IS_GC(PyObject *obj)
void
_Py_ScheduleGC(PyThreadState *tstate)
{
_PyInterpreterState_SignalAll(tstate->interp, _PY_GC_SCHEDULED_BIT);
_PyThreadState_Signal(tstate, _PY_GC_SCHEDULED_BIT);
}

void
Expand All @@ -1502,7 +1502,6 @@ _PyObject_GC_Link(PyObject *op)
void
_Py_RunGC(PyThreadState *tstate)
{
_PyInterpreterState_UnsignalAll(tstate->interp, _PY_GC_SCHEDULED_BIT);
gc_collect_main(tstate, 0, _Py_GC_REASON_HEAP);
}

Expand Down
7 changes: 4 additions & 3 deletions Python/instrumentation.c
Original file line number Diff line number Diff line change
Expand Up @@ -898,7 +898,7 @@ static void
set_version_raw(uintptr_t *breaker, uint32_t version)
{
uintptr_t old = _Py_atomic_load_uintptr(breaker);
intptr_t new;
uintptr_t new;
do {
new = (old & _PY_EVAL_EVENTS_MASK) | version;
} while (!_Py_atomic_compare_exchange_uintptr(breaker, &old, new));
Expand All @@ -909,13 +909,14 @@ set_global_version(PyThreadState *tstate, uint32_t version)
{
assert((version & _PY_EVAL_EVENTS_MASK) == 0);
PyInterpreterState *interp = tstate->interp;
set_version_raw(&tstate->interp->ceval.interp_eval_breaker, version);
set_version_raw(&interp->ceval.interp_eval_breaker, version);

#ifdef Py_GIL_DISABLED
// Set the version on all threads in free-threaded builds.
_PyRuntimeState *runtime = &_PyRuntime;
HEAD_LOCK(runtime);
for (; tstate; tstate = PyThreadState_Next(tstate)) {
for (tstate = interp->threads.head; tstate;
tstate = PyThreadState_Next(tstate)) {
set_version_raw(&tstate->eval_breaker, version);
};
HEAD_UNLOCK(runtime);
Expand Down
3 changes: 2 additions & 1 deletion Python/pystate.c
Original file line number Diff line number Diff line change
Expand Up @@ -1306,7 +1306,8 @@ init_threadstate(_PyThreadStateImpl *_tstate,

assert(interp != NULL);
tstate->interp = interp;
tstate->eval_breaker = interp->ceval.interp_eval_breaker;
tstate->eval_breaker =
_Py_atomic_load_uintptr_relaxed(&interp->ceval.interp_eval_breaker);

// next/prev are set in add_threadstate().
assert(tstate->next == NULL);
Expand Down
0