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-99113: Share the GIL via PyInterpreterState.ceval.gil #104203

Merged
merged 2 commits into from
May 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
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
3 changes: 3 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 Expand Up @@ -83,6 +85,7 @@ struct _pending_calls {

struct _ceval_state {
int recursion_limit;
struct _gil_runtime_state *gil;
/* This single variable consolidates all requests to break out of
the fast path in the eval loop. */
_Py_atomic_int eval_breaker;
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
97 changes: 59 additions & 38 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,63 +464,86 @@ 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,
and only the main interpreter is responsible to create
and destroy it. */
assert(gil_created(gil));
tstate->interp->ceval.gil = gil;
return _PyStatus_OK();
}

struct _gil_runtime_state *gil = &tstate->interp->runtime->ceval.gil;
assert(!gil_created(gil));

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,
and only the main interpreter is responsible to create
and destroy it. */
assert(interp->ceval.gil == gil);
interp->ceval.gil = NULL;
return;
}

struct _gil_runtime_state *gil = &interp->runtime->ceval.gil;
if (!gil_created(gil)) {
/* First Py_InitializeFromConfig() call: the GIL doesn't exist
yet: do nothing. */
Expand All @@ -528,6 +552,7 @@ _PyEval_FiniGIL(PyInterpreterState *interp)

destroy_gil(gil);
assert(!gil_created(gil));
interp->ceval.gil = NULL;
}

void
Expand Down Expand Up @@ -555,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 @@ -595,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 @@ -607,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 @@ -644,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 @@ -906,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 @@ -964,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