Skip to content

Commit 13faf0f

Browse files
committed
gh-141780: Make PyModule_FromSlotsAndSpec enable GIL if needed
1 parent e265ce8 commit 13faf0f

3 files changed

Lines changed: 52 additions & 21 deletions

File tree

Include/internal/pycore_import.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,18 @@ PyAPI_FUNC(int) _PyImport_ClearExtension(PyObject *name, PyObject *filename);
128128
// state of the module argument:
129129
// - If module is NULL or a PyModuleObject with md_gil == Py_MOD_GIL_NOT_USED,
130130
// call _PyEval_DisableGIL().
131-
// - Otherwise, call _PyEval_EnableGILPermanent(). If the GIL was not already
132-
// enabled permanently, issue a warning referencing the module's name.
131+
// - Otherwise, call _PyImport_EnableGILAndWarn
133132
//
134133
// This function may raise an exception.
135134
extern int _PyImport_CheckGILForModule(PyObject *module, PyObject *module_name);
135+
// Assuming that the GIL is enabled from a call to
136+
// _PyEval_EnableGILTransient(), call _PyEval_EnableGILPermanent().
137+
// If the GIL was not already enabled permanently, issue a warning referencing
138+
// the module's name.
139+
// Leave a message in verbose mode.
140+
//
141+
// This function may raise an exception.
142+
extern int _PyImport_EnableGILAndWarn(PyThreadState *, PyObject *module_name);
136143
#endif
137144

138145
#ifdef __cplusplus

Objects/moduleobject.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "Python.h"
44
#include "pycore_call.h" // _PyObject_CallNoArgs()
5+
#include "pycore_ceval.h" // _PyEval_EnableGILTransient()
56
#include "pycore_dict.h" // _PyDict_EnablePerThreadRefcounting()
67
#include "pycore_fileutils.h" // _Py_wgetcwd
78
#include "pycore_import.h" // _PyImport_GetNextModuleIndex()
@@ -522,6 +523,22 @@ module_from_def_and_spec(
522523
#undef COPY_COMMON_SLOT
523524
}
524525

526+
#ifdef Py_GIL_DISABLED
527+
// For modules created directly from slots (not from a def), we enable
528+
// the GIL here (pairing `_PyEval_EnableGILTransient` with
529+
// an immediate `_PyImport_EnableGILAndWarn`).
530+
// For modules created from a def, the caller is responsible for this.
531+
if (!original_def && requires_gil) {
532+
PyThreadState *tstate = _PyThreadState_GET();
533+
if (_PyEval_EnableGILTransient(tstate) < 0) {
534+
goto error;
535+
}
536+
if (_PyImport_EnableGILAndWarn(tstate, nameobj) < 0) {
537+
goto error;
538+
}
539+
}
540+
#endif
541+
525542
/* By default, multi-phase init modules are expected
526543
to work under multiple interpreters. */
527544
if (!has_multiple_interpreters_slot) {

Python/import.c

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1550,25 +1550,9 @@ _PyImport_CheckGILForModule(PyObject* module, PyObject *module_name)
15501550
if (!PyModule_Check(module) ||
15511551
((PyModuleObject *)module)->md_requires_gil)
15521552
{
1553-
if (_PyEval_EnableGILPermanent(tstate)) {
1554-
int warn_result = PyErr_WarnFormat(
1555-
PyExc_RuntimeWarning,
1556-
1,
1557-
"The global interpreter lock (GIL) has been enabled to load "
1558-
"module '%U', which has not declared that it can run safely "
1559-
"without the GIL. To override this behavior and keep the GIL "
1560-
"disabled (at your own risk), run with PYTHON_GIL=0 or -Xgil=0.",
1561-
module_name
1562-
);
1563-
if (warn_result < 0) {
1564-
return warn_result;
1565-
}
1566-
}
1567-
1568-
const PyConfig *config = _PyInterpreterState_GetConfig(tstate->interp);
1569-
if (config->enable_gil == _PyConfig_GIL_DEFAULT && config->verbose) {
1570-
PySys_FormatStderr("# loading module '%U', which requires the GIL\n",
1571-
module_name);
1553+
assert(((PyModuleObject *)module)->md_token_is_def);
1554+
if (_PyImport_EnableGILAndWarn(tstate, module_name) < 0) {
1555+
return -1;
15721556
}
15731557
}
15741558
else {
@@ -1577,6 +1561,27 @@ _PyImport_CheckGILForModule(PyObject* module, PyObject *module_name)
15771561

15781562
return 0;
15791563
}
1564+
1565+
int
1566+
_PyImport_EnableGILAndWarn(PyThreadState *tstate, PyObject *module_name) {
1567+
if (_PyEval_EnableGILPermanent(tstate)) {
1568+
return PyErr_WarnFormat(
1569+
PyExc_RuntimeWarning,
1570+
1,
1571+
"The global interpreter lock (GIL) has been enabled to load "
1572+
"module '%U', which has not declared that it can run safely "
1573+
"without the GIL. To override this behavior and keep the GIL "
1574+
"disabled (at your own risk), run with PYTHON_GIL=0 or -Xgil=0.",
1575+
module_name
1576+
);
1577+
}
1578+
const PyConfig *config = _PyInterpreterState_GetConfig(tstate->interp);
1579+
if (config->enable_gil == _PyConfig_GIL_DEFAULT && config->verbose) {
1580+
PySys_FormatStderr("# loading module '%U', which requires the GIL\n",
1581+
module_name);
1582+
}
1583+
return 0;
1584+
}
15801585
#endif
15811586

15821587
static PyThreadState *
@@ -4785,6 +4790,8 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file)
47854790
_PyImport_GetModuleExportHooks(&info, fp, &p0, &ex0);
47864791
if (ex0) {
47874792
mod = import_run_modexport(tstate, ex0, &info, spec);
4793+
// Modules created from slots handle GIL enablement (Py_mod_gil slot)
4794+
// when they're created.
47884795
goto cleanup;
47894796
}
47904797
if (p0 == NULL) {

0 commit comments

Comments
 (0)