@@ -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