Skip to content

Commit ccbd2b9

Browse files
committed
gh-138045: Fix importing numpy in legacy subinterpreters by skipping switch to main interpreter when not needed
(singlephase == NULL) check in update_global_state_for_extension could be removed because it's only called with pointers to stack objects.
1 parent 4aef138 commit ccbd2b9

File tree

1 file changed

+56
-61
lines changed

1 file changed

+56
-61
lines changed

Python/import.c

Lines changed: 56 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1760,57 +1760,48 @@ update_global_state_for_extension(PyThreadState *tstate,
17601760
PyObject *m_dict = NULL;
17611761

17621762
/* Set up for _extensions_cache_set(). */
1763-
if (singlephase == NULL) {
1764-
assert(def->m_base.m_init == NULL);
1763+
if (singlephase->m_init != NULL) {
1764+
assert(singlephase->m_dict == NULL);
1765+
assert(def->m_base.m_copy == NULL);
1766+
assert(def->m_size >= 0);
1767+
/* Remember pointer to module init function. */
1768+
// XXX If two modules share a def then def->m_base will
1769+
// reflect the last one added (here) to the global cache.
1770+
// We should prevent this somehow. The simplest solution
1771+
// is probably to store m_copy/m_init in the cache along
1772+
// with the def, rather than within the def.
1773+
m_init = singlephase->m_init;
1774+
}
1775+
else if (singlephase->m_dict == NULL) {
1776+
/* It must be a core builtin module. */
1777+
assert(is_core_module(tstate->interp, name, path));
1778+
assert(def->m_size == -1);
17651779
assert(def->m_base.m_copy == NULL);
1780+
assert(def->m_base.m_init == NULL);
17661781
}
17671782
else {
1768-
if (singlephase->m_init != NULL) {
1769-
assert(singlephase->m_dict == NULL);
1770-
assert(def->m_base.m_copy == NULL);
1771-
assert(def->m_size >= 0);
1772-
/* Remember pointer to module init function. */
1773-
// XXX If two modules share a def then def->m_base will
1774-
// reflect the last one added (here) to the global cache.
1775-
// We should prevent this somehow. The simplest solution
1776-
// is probably to store m_copy/m_init in the cache along
1777-
// with the def, rather than within the def.
1778-
m_init = singlephase->m_init;
1779-
}
1780-
else if (singlephase->m_dict == NULL) {
1781-
/* It must be a core builtin module. */
1782-
assert(is_core_module(tstate->interp, name, path));
1783-
assert(def->m_size == -1);
1784-
assert(def->m_base.m_copy == NULL);
1785-
assert(def->m_base.m_init == NULL);
1786-
}
1787-
else {
1788-
assert(PyDict_Check(singlephase->m_dict));
1789-
// gh-88216: Extensions and def->m_base.m_copy can be updated
1790-
// when the extension module doesn't support sub-interpreters.
1791-
assert(def->m_size == -1);
1792-
assert(!is_core_module(tstate->interp, name, path));
1793-
assert(PyUnicode_CompareWithASCIIString(name, "sys") != 0);
1794-
assert(PyUnicode_CompareWithASCIIString(name, "builtins") != 0);
1795-
m_dict = singlephase->m_dict;
1796-
}
1783+
assert(PyDict_Check(singlephase->m_dict));
1784+
// gh-88216: Extensions and def->m_base.m_copy can be updated
1785+
// when the extension module doesn't support sub-interpreters.
1786+
assert(def->m_size == -1);
1787+
assert(!is_core_module(tstate->interp, name, path));
1788+
assert(PyUnicode_CompareWithASCIIString(name, "sys") != 0);
1789+
assert(PyUnicode_CompareWithASCIIString(name, "builtins") != 0);
1790+
m_dict = singlephase->m_dict;
17971791
}
17981792

17991793
/* Add the module's def to the global cache. */
1800-
// XXX Why special-case the main interpreter?
1801-
if (_Py_IsMainInterpreter(tstate->interp) || def->m_size == -1) {
18021794
#ifndef NDEBUG
1803-
cached = _extensions_cache_get(path, name);
1804-
assert(cached == NULL || cached->def == def);
1795+
cached = _extensions_cache_get(path, name);
1796+
assert(cached == NULL || cached->def == def);
18051797
#endif
1806-
cached = _extensions_cache_set(
1807-
path, name, def, m_init, singlephase->m_index, m_dict,
1808-
singlephase->origin, singlephase->md_requires_gil);
1809-
if (cached == NULL) {
1810-
// XXX Ignore this error? Doing so would effectively
1811-
// mark the module as not loadable.
1812-
return NULL;
1813-
}
1798+
cached = _extensions_cache_set(
1799+
path, name, def, m_init, singlephase->m_index, m_dict,
1800+
singlephase->origin, singlephase->md_requires_gil);
1801+
if (cached == NULL) {
1802+
// XXX Ignore this error? Doing so would effectively
1803+
// mark the module as not loadable.
1804+
return NULL;
18141805
}
18151806

18161807
return cached;
@@ -2082,21 +2073,22 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0,
20822073
* and then continue loading like normal. */
20832074

20842075
bool switched = false;
2085-
/* We *could* leave in place a legacy interpreter here
2086-
* (one that shares obmalloc/GIL with main interp),
2087-
* but there isn't a big advantage, we anticipate
2088-
* such interpreters will be increasingly uncommon,
2089-
* and the code is a bit simpler if we always switch
2090-
* to the main interpreter. */
2091-
PyThreadState *main_tstate = switch_to_main_interpreter(tstate);
2092-
if (main_tstate == NULL) {
2093-
return NULL;
2094-
}
2095-
else if (main_tstate != tstate) {
2096-
switched = true;
2097-
/* In the switched case, we could play it safe
2098-
* by getting the main interpreter's import lock here.
2099-
* It's unlikely to matter though. */
2076+
PyThreadState *main_tstate = tstate;
2077+
/* For non-isolated, legacy interpreters they share the GIL and allocator
2078+
* with the main thread anyway, so there's no need to switch to the main
2079+
* interpreter. If we would still switch in that case in would result in
2080+
* calling the init function of single-phase modules that have cyclic
2081+
* references twice, see issue #138045. */
2082+
if (tstate->interp->ceval.own_gil) {
2083+
main_tstate = switch_to_main_interpreter(tstate);
2084+
if (main_tstate == NULL) {
2085+
return NULL;
2086+
} else if (main_tstate != tstate) {
2087+
switched = true;
2088+
/* In the switched case, we could play it safe
2089+
* by getting the main interpreter's import lock here.
2090+
* It's unlikely to matter though. */
2091+
}
21002092
}
21012093

21022094
struct _Py_ext_module_loader_result res;
@@ -2280,9 +2272,12 @@ clear_singlephase_extension(PyInterpreterState *interp,
22802272
/* We must use the main interpreter to clean up the cache.
22812273
* See the note in import_run_extension(). */
22822274
PyThreadState *tstate = PyThreadState_GET();
2283-
PyThreadState *main_tstate = switch_to_main_interpreter(tstate);
2284-
if (main_tstate == NULL) {
2285-
return -1;
2275+
PyThreadState *main_tstate = tstate;
2276+
if (tstate->interp->ceval.own_gil) {
2277+
main_tstate = switch_to_main_interpreter(tstate);
2278+
if (main_tstate == NULL) {
2279+
return -1;
2280+
}
22862281
}
22872282

22882283
/* Clear the cached module def. */

0 commit comments

Comments
 (0)