@@ -3712,20 +3712,25 @@ _PyImport_LoadLazyImportTstate(PyThreadState *tstate, PyObject *lazy_import)
37123712 assert (PyLazyImport_CheckExact (lazy_import ));
37133713
37143714 PyLazyImportObject * lz = (PyLazyImportObject * )lazy_import ;
3715+ PyInterpreterState * interp = tstate -> interp ;
3716+
3717+ // Acquire the global import lock to serialize reification
3718+ _PyImport_AcquireLock (interp );
37153719
37163720 // Check if we are already importing this module, if so, then we want to return an error
37173721 // that indicates we've hit a cycle which will indicate the value isn't yet available.
3718- PyInterpreterState * interp = tstate -> interp ;
37193722 PyObject * importing = interp -> imports .lazy_importing_modules ;
37203723 if (importing == NULL ) {
37213724 importing = interp -> imports .lazy_importing_modules = PySet_New (NULL );
37223725 if (importing == NULL ) {
3726+ _PyImport_ReleaseLock (interp );
37233727 return NULL ;
37243728 }
37253729 }
37263730
37273731 int is_loading = PySet_Contains (importing , lazy_import );
37283732 if (is_loading < 0 ) {
3733+ _PyImport_ReleaseLock (interp );
37293734 return NULL ;
37303735 } else if (is_loading == 1 ) {
37313736 PyObject * name = _PyLazyImport_GetName (lazy_import );
@@ -3735,8 +3740,10 @@ _PyImport_LoadLazyImportTstate(PyThreadState *tstate, PyObject *lazy_import)
37353740 PyErr_SetImportErrorSubclass (PyExc_ImportCycleError , errmsg , lz -> lz_from , NULL );
37363741 Py_XDECREF (errmsg );
37373742 Py_XDECREF (name );
3743+ _PyImport_ReleaseLock (interp );
37383744 return NULL ;
37393745 } else if (PySet_Add (importing , lazy_import ) < 0 ) {
3746+ _PyImport_ReleaseLock (interp );
37403747 goto error ;
37413748 }
37423749
@@ -3889,6 +3896,9 @@ _PyImport_LoadLazyImportTstate(PyThreadState *tstate, PyObject *lazy_import)
38893896 obj = NULL ;
38903897 }
38913898
3899+ // Release the global import lock
3900+ _PyImport_ReleaseLock (interp );
3901+
38923902 Py_XDECREF (fromlist );
38933903 Py_XDECREF (import_func );
38943904 return obj ;
@@ -4125,14 +4135,25 @@ register_lazy_on_parent(PyThreadState *tstate, PyObject *name, PyObject *builtin
41254135 PyObject * child = NULL ;
41264136 PyObject * parent_module = NULL ;
41274137 PyObject * parent_dict = NULL ;
4128- PyObject * lazy_modules = tstate -> interp -> imports .lazy_modules ;
4138+
4139+ // Acquire import lock to safely initialize lazy_modules if needed
4140+ // This prevents a race where multiple threads could create different dicts
4141+ PyInterpreterState * interp = tstate -> interp ;
4142+ _PyImport_AcquireLock (interp );
4143+
4144+ PyObject * lazy_modules = interp -> imports .lazy_modules ;
41294145 if (lazy_modules == NULL ) {
4130- lazy_modules = tstate -> interp -> imports .lazy_modules = PyDict_New ();
4146+ lazy_modules = interp -> imports .lazy_modules = PyDict_New ();
41314147 if (lazy_modules == NULL ) {
4148+ _PyImport_ReleaseLock (interp );
41324149 return -1 ;
41334150 }
41344151 }
41354152
4153+ // Release the lock - we only needed it for initialization
4154+ // The dict operations below are thread-safe on their own
4155+ _PyImport_ReleaseLock (interp );
4156+
41364157 Py_INCREF (name );
41374158 while (true) {
41384159 Py_ssize_t dot = PyUnicode_FindChar (name , '.' , 0 , PyUnicode_GET_LENGTH (name ), -1 );
0 commit comments