Skip to content

gh-99113: Share the GIL via PyInterpreterState.ceval.gil #104203

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 2 commits into from
May 5, 2023
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
Use PyInterpreterState.ceval.gil.
  • Loading branch information
ericsnowcurrently committed May 5, 2023
commit fc1e4bccfa1c50c149739e41e3771cdaa0cf659a
2 changes: 1 addition & 1 deletion Include/internal/pycore_ceval.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ _PyEval_Vector(PyThreadState *tstate,
PyObject* const* args, size_t argcount,
PyObject *kwnames);

extern int _PyEval_ThreadsInitialized(struct pyruntimestate *runtime);
extern int _PyEval_ThreadsInitialized(void);
extern PyStatus _PyEval_InitGIL(PyThreadState *tstate);
extern void _PyEval_FiniGIL(PyInterpreterState *interp);

Expand Down
2 changes: 2 additions & 0 deletions Include/internal/pycore_ceval_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ struct _ceval_runtime_state {
the main thread of the main interpreter can handle signals: see
_Py_ThreadCanHandleSignals(). */
_Py_atomic_int signals_pending;

/* This is (only) used indirectly through PyInterpreterState.ceval.gil. */
struct _gil_runtime_state gil;
};

Expand Down
1 change: 1 addition & 0 deletions Modules/_xxsubinterpretersmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,7 @@ interp_create(PyObject *self, PyObject *args, PyObject *kwds)

// Create and initialize the new interpreter.
PyThreadState *save_tstate = _PyThreadState_GET();
assert(save_tstate != NULL);
const PyInterpreterConfig config = isolated
? (PyInterpreterConfig)_PyInterpreterConfig_INIT
: (PyInterpreterConfig)_PyInterpreterConfig_LEGACY_INIT;
Expand Down
86 changes: 51 additions & 35 deletions Python/ceval_gil.c
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,9 @@ static void _gil_initialize(struct _gil_runtime_state *gil)

static int gil_created(struct _gil_runtime_state *gil)
{
if (gil == NULL) {
return 0;
}
return (_Py_atomic_load_explicit(&gil->locked, _Py_memory_order_acquire) >= 0);
}

Expand Down Expand Up @@ -273,10 +276,9 @@ static void recreate_gil(struct _gil_runtime_state *gil)
#endif

static void
drop_gil(struct _ceval_runtime_state *ceval, struct _ceval_state *ceval2,
PyThreadState *tstate)
drop_gil(struct _ceval_state *ceval, PyThreadState *tstate)
{
struct _gil_runtime_state *gil = &ceval->gil;
struct _gil_runtime_state *gil = ceval->gil;
if (!_Py_atomic_load_relaxed(&gil->locked)) {
Py_FatalError("drop_gil: GIL is not locked");
}
Expand All @@ -296,7 +298,7 @@ drop_gil(struct _ceval_runtime_state *ceval, struct _ceval_state *ceval2,
MUTEX_UNLOCK(gil->mutex);

#ifdef FORCE_SWITCHING
if (_Py_atomic_load_relaxed(&ceval2->gil_drop_request) && tstate != NULL) {
if (_Py_atomic_load_relaxed(&ceval->gil_drop_request) && tstate != NULL) {
MUTEX_LOCK(gil->switch_mutex);
/* Not switched yet => wait */
if (((PyThreadState*)_Py_atomic_load_relaxed(&gil->last_holder)) == tstate)
Expand Down Expand Up @@ -358,9 +360,8 @@ take_gil(PyThreadState *tstate)

assert(is_tstate_valid(tstate));
PyInterpreterState *interp = tstate->interp;
struct _ceval_runtime_state *ceval = &interp->runtime->ceval;
struct _ceval_state *ceval2 = &interp->ceval;
struct _gil_runtime_state *gil = &ceval->gil;
struct _ceval_state *ceval = &interp->ceval;
struct _gil_runtime_state *gil = ceval->gil;

/* Check that _PyEval_InitThreads() was called to create the lock */
assert(gil_created(gil));
Expand Down Expand Up @@ -434,12 +435,12 @@ take_gil(PyThreadState *tstate)
in take_gil() while the main thread called
wait_for_thread_shutdown() from Py_Finalize(). */
MUTEX_UNLOCK(gil->mutex);
drop_gil(ceval, ceval2, tstate);
drop_gil(ceval, tstate);
PyThread_exit_thread();
}
assert(is_tstate_valid(tstate));

if (_Py_atomic_load_relaxed(&ceval2->gil_drop_request)) {
if (_Py_atomic_load_relaxed(&ceval->gil_drop_request)) {
RESET_GIL_DROP_REQUEST(interp);
}
else {
Expand All @@ -448,7 +449,7 @@ take_gil(PyThreadState *tstate)
handle signals.

Note: RESET_GIL_DROP_REQUEST() calls COMPUTE_EVAL_BREAKER(). */
COMPUTE_EVAL_BREAKER(interp, ceval, ceval2);
COMPUTE_EVAL_BREAKER(interp, &_PyRuntime.ceval, ceval);
}

/* Don't access tstate if the thread must exit */
Expand All @@ -463,33 +464,47 @@ take_gil(PyThreadState *tstate)

void _PyEval_SetSwitchInterval(unsigned long microseconds)
{
struct _gil_runtime_state *gil = &_PyRuntime.ceval.gil;
/* XXX per-interpreter GIL */
PyInterpreterState *interp = _PyInterpreterState_Main();
struct _gil_runtime_state *gil = interp->ceval.gil;
assert(gil != NULL);
gil->interval = microseconds;
}

unsigned long _PyEval_GetSwitchInterval(void)
{
struct _gil_runtime_state *gil = &_PyRuntime.ceval.gil;
/* XXX per-interpreter GIL */
PyInterpreterState *interp = _PyInterpreterState_Main();
struct _gil_runtime_state *gil = interp->ceval.gil;
assert(gil != NULL);
return gil->interval;
}


int
_PyEval_ThreadsInitialized(_PyRuntimeState *runtime)
_PyEval_ThreadsInitialized(void)
{
return gil_created(&runtime->ceval.gil);
/* XXX per-interpreter GIL */
PyInterpreterState *interp = _PyInterpreterState_Main();
if (interp == NULL) {
return 0;
}
struct _gil_runtime_state *gil = interp->ceval.gil;
return gil_created(gil);
}

int
PyEval_ThreadsInitialized(void)
{
_PyRuntimeState *runtime = &_PyRuntime;
return _PyEval_ThreadsInitialized(runtime);
return _PyEval_ThreadsInitialized();
}

PyStatus
_PyEval_InitGIL(PyThreadState *tstate)
{
assert(tstate->interp->ceval.gil == NULL);

/* XXX per-interpreter GIL */
struct _gil_runtime_state *gil = &tstate->interp->runtime->ceval.gil;
if (!_Py_IsMainInterpreter(tstate->interp)) {
/* Currently, the GIL is shared by all interpreters,
Expand All @@ -504,16 +519,21 @@ _PyEval_InitGIL(PyThreadState *tstate)

PyThread_init_thread();
create_gil(gil);
take_gil(tstate);
assert(gil_created(gil));

tstate->interp->ceval.gil = gil;
take_gil(tstate);
return _PyStatus_OK();
}

void
_PyEval_FiniGIL(PyInterpreterState *interp)
{
if (interp->ceval.gil == NULL) {
/* It was already finalized (or hasn't been initialized yet). */
return;
}

/* XXX per-interpreter GIL */
struct _gil_runtime_state *gil = &interp->runtime->ceval.gil;
if (!_Py_IsMainInterpreter(interp)) {
/* Currently, the GIL is shared by all interpreters,
Expand Down Expand Up @@ -560,22 +580,19 @@ PyEval_AcquireLock(void)
void
PyEval_ReleaseLock(void)
{
_PyRuntimeState *runtime = &_PyRuntime;
PyThreadState *tstate = _PyThreadState_GET();
/* This function must succeed when the current thread state is NULL.
We therefore avoid PyThreadState_Get() which dumps a fatal error
in debug mode. */
struct _ceval_runtime_state *ceval = &runtime->ceval;
struct _ceval_state *ceval2 = &tstate->interp->ceval;
drop_gil(ceval, ceval2, tstate);
struct _ceval_state *ceval = &tstate->interp->ceval;
drop_gil(ceval, tstate);
}

void
_PyEval_ReleaseLock(PyThreadState *tstate)
{
struct _ceval_runtime_state *ceval = &tstate->interp->runtime->ceval;
struct _ceval_state *ceval2 = &tstate->interp->ceval;
drop_gil(ceval, ceval2, tstate);
struct _ceval_state *ceval = &tstate->interp->ceval;
drop_gil(ceval, tstate);
}

void
Expand All @@ -600,9 +617,8 @@ PyEval_ReleaseThread(PyThreadState *tstate)
if (new_tstate != tstate) {
Py_FatalError("wrong thread state");
}
struct _ceval_runtime_state *ceval = &runtime->ceval;
struct _ceval_state *ceval2 = &tstate->interp->ceval;
drop_gil(ceval, ceval2, tstate);
struct _ceval_state *ceval = &tstate->interp->ceval;
drop_gil(ceval, tstate);
}

#ifdef HAVE_FORK
Expand All @@ -612,9 +628,9 @@ PyEval_ReleaseThread(PyThreadState *tstate)
PyStatus
_PyEval_ReInitThreads(PyThreadState *tstate)
{
_PyRuntimeState *runtime = tstate->interp->runtime;
assert(tstate->interp == _PyInterpreterState_Main());

struct _gil_runtime_state *gil = &runtime->ceval.gil;
struct _gil_runtime_state *gil = tstate->interp->ceval.gil;
if (!gil_created(gil)) {
return _PyStatus_OK();
}
Expand Down Expand Up @@ -649,10 +665,9 @@ PyEval_SaveThread(void)
PyThreadState *tstate = _PyThreadState_Swap(runtime, NULL);
_Py_EnsureTstateNotNULL(tstate);

struct _ceval_runtime_state *ceval = &runtime->ceval;
struct _ceval_state *ceval2 = &tstate->interp->ceval;
assert(gil_created(&ceval->gil));
drop_gil(ceval, ceval2, tstate);
struct _ceval_state *ceval = &tstate->interp->ceval;
assert(gil_created(ceval->gil));
drop_gil(ceval, tstate);
return tstate;
}

Expand Down Expand Up @@ -911,6 +926,7 @@ Py_MakePendingCalls(void)
void
_PyEval_InitRuntimeState(struct _ceval_runtime_state *ceval)
{
/* XXX per-interpreter GIL */
_gil_initialize(&ceval->gil);
}

Expand Down Expand Up @@ -969,7 +985,7 @@ _Py_HandlePending(PyThreadState *tstate)
if (_PyThreadState_Swap(runtime, NULL) != tstate) {
Py_FatalError("tstate mix-up");
}
drop_gil(ceval, interp_ceval_state, tstate);
drop_gil(interp_ceval_state, tstate);

/* Other threads may run now */

Expand Down
2 changes: 1 addition & 1 deletion Python/pystate.c
Original file line number Diff line number Diff line change
Expand Up @@ -2186,7 +2186,7 @@ PyGILState_Ensure(void)

/* Ensure that _PyEval_InitThreads() and _PyGILState_Init() have been
called by Py_Initialize() */
assert(_PyEval_ThreadsInitialized(runtime));
assert(_PyEval_ThreadsInitialized());
assert(gilstate_tss_initialized(runtime));
assert(runtime->gilstate.autoInterpreterState != NULL);

Expand Down