Skip to content

Commit fff193b

Browse files
gh-99113: Add a check for Py_MOD_PER_INTERPRETER_GIL_SUPPORTED (gh-104206)
Py_MOD_PER_INTERPRETER_GIL_SUPPORTED is a new supported value for Py_mod_multiple_interpreters, added in gh-104205.
1 parent 3b14b51 commit fff193b

File tree

5 files changed

+99
-2
lines changed

5 files changed

+99
-2
lines changed

Lib/test/test_import/__init__.py

+20
Original file line numberDiff line numberDiff line change
@@ -1861,6 +1861,26 @@ def test_multi_init_extension_non_isolated_compat(self):
18611861
with self.subTest(f'{modname}: not strict'):
18621862
self.check_compatible_here(modname, filename, strict=False)
18631863

1864+
@unittest.skipIf(_testmultiphase is None, "test requires _testmultiphase module")
1865+
def test_multi_init_extension_per_interpreter_gil_compat(self):
1866+
modname = '_test_shared_gil_only'
1867+
filename = _testmultiphase.__file__
1868+
loader = ExtensionFileLoader(modname, filename)
1869+
spec = importlib.util.spec_from_loader(modname, loader)
1870+
module = importlib.util.module_from_spec(spec)
1871+
loader.exec_module(module)
1872+
sys.modules[modname] = module
1873+
1874+
require_extension(module)
1875+
with self.subTest(f'{modname}: isolated, strict'):
1876+
self.check_incompatible_here(modname, filename, isolated=True)
1877+
with self.subTest(f'{modname}: not isolated, strict'):
1878+
self.check_compatible_here(modname, filename,
1879+
strict=True, isolated=False)
1880+
with self.subTest(f'{modname}: not isolated, not strict'):
1881+
self.check_compatible_here(modname, filename,
1882+
strict=False, isolated=False)
1883+
18641884
def test_python_compat(self):
18651885
module = 'threading'
18661886
require_pure_python(module)

Lib/test/test_importlib/extension/test_loader.py

+2
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,8 @@ def test_bad_modules(self):
348348
'exec_err',
349349
'exec_raise',
350350
'exec_unreported_exception',
351+
'multiple_create_slots',
352+
'multiple_multiple_interpreters_slots',
351353
]:
352354
with self.subTest(name_base):
353355
name = self.name + '_' + name_base
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Multi-phase init extension modules may now indicate that they support
2+
running in subinterpreters that have their own GIL. This is done by using
3+
``Py_MOD_PER_INTERPRETER_GIL_SUPPORTED`` as the value for the
4+
``Py_mod_multiple_interpreters`` module def slot. Otherwise the module, by
5+
default, cannot be imported in such subinterpreters. (This does not affect
6+
the main interpreter or subinterpreters that do not have their own GIL.) In
7+
addition to the isolation that multi-phase init already normally requires,
8+
support for per-interpreter GIL involves one additional constraint:
9+
thread-safety. If the module has external (linked) dependencies and those
10+
libraries have any state that isn't thread-safe then the module must do the
11+
additional work to add thread-safety. This should be an uncommon case.

Modules/_testmultiphase.c

+59-1
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,27 @@ PyInit__testmultiphase_export_unreported_exception(void)
681681
return PyModuleDef_Init(&main_def);
682682
}
683683

684+
static PyObject*
685+
createfunc_noop(PyObject *spec, PyModuleDef *def)
686+
{
687+
return PyModule_New("spam");
688+
}
689+
690+
static PyModuleDef_Slot slots_multiple_create_slots[] = {
691+
{Py_mod_create, createfunc_noop},
692+
{Py_mod_create, createfunc_noop},
693+
{0, NULL},
694+
};
695+
696+
static PyModuleDef def_multiple_create_slots = TEST_MODULE_DEF(
697+
"_testmultiphase_multiple_create_slots", slots_multiple_create_slots, NULL);
698+
699+
PyMODINIT_FUNC
700+
PyInit__testmultiphase_multiple_create_slots(void)
701+
{
702+
return PyModuleDef_Init(&def_multiple_create_slots);
703+
}
704+
684705
static PyObject*
685706
createfunc_null(PyObject *spec, PyModuleDef *def)
686707
{
@@ -892,7 +913,24 @@ PyInit__test_module_state_shared(void)
892913
}
893914

894915

895-
/* multiple interpreters supports */
916+
/* multiple interpreters support */
917+
918+
static PyModuleDef_Slot slots_multiple_multiple_interpreters_slots[] = {
919+
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
920+
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
921+
{0, NULL},
922+
};
923+
924+
static PyModuleDef def_multiple_multiple_interpreters_slots = TEST_MODULE_DEF(
925+
"_testmultiphase_multiple_multiple_interpreters_slots",
926+
slots_multiple_multiple_interpreters_slots,
927+
NULL);
928+
929+
PyMODINIT_FUNC
930+
PyInit__testmultiphase_multiple_multiple_interpreters_slots(void)
931+
{
932+
return PyModuleDef_Init(&def_multiple_multiple_interpreters_slots);
933+
}
896934

897935
static PyModuleDef_Slot non_isolated_slots[] = {
898936
{Py_mod_exec, execfunc},
@@ -909,3 +947,23 @@ PyInit__test_non_isolated(void)
909947
{
910948
return PyModuleDef_Init(&non_isolated_def);
911949
}
950+
951+
952+
static PyModuleDef_Slot shared_gil_only_slots[] = {
953+
{Py_mod_exec, execfunc},
954+
/* Note that Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED is the default.
955+
We put it here explicitly to draw attention to the contrast
956+
with Py_MOD_PER_INTERPRETER_GIL_SUPPORTED. */
957+
{Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED},
958+
{0, NULL},
959+
};
960+
961+
static PyModuleDef shared_gil_only_def = TEST_MODULE_DEF("_test_shared_gil_only",
962+
shared_gil_only_slots,
963+
testexport_methods);
964+
965+
PyMODINIT_FUNC
966+
PyInit__test_shared_gil_only(void)
967+
{
968+
return PyModuleDef_Init(&shared_gil_only_def);
969+
}

Objects/moduleobject.c

+7-1
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,13 @@ PyModule_FromDefAndSpec2(PyModuleDef* def, PyObject *spec, int module_api_versio
323323
goto error;
324324
}
325325
}
326-
// XXX Do a similar check once we have PyInterpreterState.ceval.own_gil.
326+
else if (multiple_interpreters != Py_MOD_PER_INTERPRETER_GIL_SUPPORTED
327+
&& interp->ceval.own_gil
328+
&& !_Py_IsMainInterpreter(interp)
329+
&& _PyImport_CheckSubinterpIncompatibleExtensionAllowed(name) < 0)
330+
{
331+
goto error;
332+
}
327333

328334
if (create) {
329335
m = create(spec, def);

0 commit comments

Comments
 (0)