]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-99113: Add a check for Py_MOD_PER_INTERPRETER_GIL_SUPPORTED (gh-104206)
authorEric Snow <ericsnowcurrently@gmail.com>
Sat, 6 May 2023 21:57:35 +0000 (15:57 -0600)
committerGitHub <noreply@github.com>
Sat, 6 May 2023 21:57:35 +0000 (21:57 +0000)
Py_MOD_PER_INTERPRETER_GIL_SUPPORTED is a new supported value for Py_mod_multiple_interpreters, added in gh-104205.

Lib/test/test_import/__init__.py
Lib/test/test_importlib/extension/test_loader.py
Misc/NEWS.d/next/Core and Builtins/2023-05-05-13-18-56.gh-issue-99113.hT1ajK.rst [new file with mode: 0644]
Modules/_testmultiphase.c
Objects/moduleobject.c

index 773b7094c6b8cebe9f285ca1ea2b0c18f004a17c..e2384a08ecaa901d9685991c42bb54a1c297e9f8 100644 (file)
@@ -1861,6 +1861,26 @@ class SubinterpImportTests(unittest.TestCase):
         with self.subTest(f'{modname}: not strict'):
             self.check_compatible_here(modname, filename, strict=False)
 
+    @unittest.skipIf(_testmultiphase is None, "test requires _testmultiphase module")
+    def test_multi_init_extension_per_interpreter_gil_compat(self):
+        modname = '_test_shared_gil_only'
+        filename = _testmultiphase.__file__
+        loader = ExtensionFileLoader(modname, filename)
+        spec = importlib.util.spec_from_loader(modname, loader)
+        module = importlib.util.module_from_spec(spec)
+        loader.exec_module(module)
+        sys.modules[modname] = module
+
+        require_extension(module)
+        with self.subTest(f'{modname}: isolated, strict'):
+            self.check_incompatible_here(modname, filename, isolated=True)
+        with self.subTest(f'{modname}: not isolated, strict'):
+            self.check_compatible_here(modname, filename,
+                                       strict=True, isolated=False)
+        with self.subTest(f'{modname}: not isolated, not strict'):
+            self.check_compatible_here(modname, filename,
+                                       strict=False, isolated=False)
+
     def test_python_compat(self):
         module = 'threading'
         require_pure_python(module)
index 3bf2bbdcdcc4e6be640c0a89d438392f09a54bd5..3a74b821eaee495a66f7d9e32e9935885f31a8e7 100644 (file)
@@ -348,6 +348,8 @@ class MultiPhaseExtensionModuleTests(abc.LoaderTests):
                 'exec_err',
                 'exec_raise',
                 'exec_unreported_exception',
+                'multiple_create_slots',
+                'multiple_multiple_interpreters_slots',
                 ]:
             with self.subTest(name_base):
                 name = self.name + '_' + name_base
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-05-05-13-18-56.gh-issue-99113.hT1ajK.rst b/Misc/NEWS.d/next/Core and Builtins/2023-05-05-13-18-56.gh-issue-99113.hT1ajK.rst
new file mode 100644 (file)
index 0000000..afd2675
--- /dev/null
@@ -0,0 +1,11 @@
+Multi-phase init extension modules may now indicate that they support
+running in subinterpreters that have their own GIL.  This is done by using
+``Py_MOD_PER_INTERPRETER_GIL_SUPPORTED`` as the value for the
+``Py_mod_multiple_interpreters`` module def slot.  Otherwise the module, by
+default, cannot be imported in such subinterpreters.  (This does not affect
+the main interpreter or subinterpreters that do not have their own GIL.)  In
+addition to the isolation that multi-phase init already normally requires,
+support for per-interpreter GIL involves one additional constraint:
+thread-safety.  If the module has external (linked) dependencies and those
+libraries have any state that isn't thread-safe then the module must do the
+additional work to add thread-safety.  This should be an uncommon case.
index 58b064bb17cd871174b13da333204b56611e0fcc..ca71b6156b005d37285c9f985d8282f2dd528940 100644 (file)
@@ -681,6 +681,27 @@ PyInit__testmultiphase_export_unreported_exception(void)
     return PyModuleDef_Init(&main_def);
 }
 
+static PyObject*
+createfunc_noop(PyObject *spec, PyModuleDef *def)
+{
+    return PyModule_New("spam");
+}
+
+static PyModuleDef_Slot slots_multiple_create_slots[] = {
+    {Py_mod_create, createfunc_noop},
+    {Py_mod_create, createfunc_noop},
+    {0, NULL},
+};
+
+static PyModuleDef def_multiple_create_slots = TEST_MODULE_DEF(
+    "_testmultiphase_multiple_create_slots", slots_multiple_create_slots, NULL);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_multiple_create_slots(void)
+{
+    return PyModuleDef_Init(&def_multiple_create_slots);
+}
+
 static PyObject*
 createfunc_null(PyObject *spec, PyModuleDef *def)
 {
@@ -892,7 +913,24 @@ PyInit__test_module_state_shared(void)
 }
 
 
-/* multiple interpreters supports */
+/* multiple interpreters support */
+
+static PyModuleDef_Slot slots_multiple_multiple_interpreters_slots[] = {
+    {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
+    {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
+    {0, NULL},
+};
+
+static PyModuleDef def_multiple_multiple_interpreters_slots = TEST_MODULE_DEF(
+    "_testmultiphase_multiple_multiple_interpreters_slots",
+    slots_multiple_multiple_interpreters_slots,
+    NULL);
+
+PyMODINIT_FUNC
+PyInit__testmultiphase_multiple_multiple_interpreters_slots(void)
+{
+    return PyModuleDef_Init(&def_multiple_multiple_interpreters_slots);
+}
 
 static PyModuleDef_Slot non_isolated_slots[] = {
     {Py_mod_exec, execfunc},
@@ -909,3 +947,23 @@ PyInit__test_non_isolated(void)
 {
     return PyModuleDef_Init(&non_isolated_def);
 }
+
+
+static PyModuleDef_Slot shared_gil_only_slots[] = {
+    {Py_mod_exec, execfunc},
+    /* Note that Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED is the default.
+       We put it here explicitly to draw attention to the contrast
+       with Py_MOD_PER_INTERPRETER_GIL_SUPPORTED. */
+    {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED},
+    {0, NULL},
+};
+
+static PyModuleDef shared_gil_only_def = TEST_MODULE_DEF("_test_shared_gil_only",
+                                                         shared_gil_only_slots,
+                                                         testexport_methods);
+
+PyMODINIT_FUNC
+PyInit__test_shared_gil_only(void)
+{
+    return PyModuleDef_Init(&shared_gil_only_def);
+}
index c100d018d3f0df8eaf152f10e00972cf74eba023..985be58d02c78477ef03b179c94747795315f0c5 100644 (file)
@@ -323,7 +323,13 @@ PyModule_FromDefAndSpec2(PyModuleDef* def, PyObject *spec, int module_api_versio
             goto error;
         }
     }
-    // XXX Do a similar check once we have PyInterpreterState.ceval.own_gil.
+    else if (multiple_interpreters != Py_MOD_PER_INTERPRETER_GIL_SUPPORTED
+             && interp->ceval.own_gil
+             && !_Py_IsMainInterpreter(interp)
+             && _PyImport_CheckSubinterpIncompatibleExtensionAllowed(name) < 0)
+    {
+        goto error;
+    }
 
     if (create) {
         m = create(spec, def);