bpo-30891: Fix importlib _find_and_load() race condition (#2646) (#2651) · python/cpython@fe6e686
@@ -39,6 +39,7 @@ def _new_module(name):
3939# Module-level locking ########################################################
40404141# A dict mapping module names to weakrefs of _ModuleLock instances
42+# Dictionary protected by the global import lock
4243_module_locks = {}
4344# A dict mapping thread ids to _ModuleLock instances
4445_blocking_on = {}
@@ -144,10 +145,7 @@ def __init__(self, name):
144145self._lock = None
145146146147def __enter__(self):
147-try:
148-self._lock = _get_module_lock(self._name)
149-finally:
150-_imp.release_lock()
148+self._lock = _get_module_lock(self._name)
151149self._lock.acquire()
152150153151def __exit__(self, *args, **kwargs):
@@ -159,31 +157,37 @@ def __exit__(self, *args, **kwargs):
159157def _get_module_lock(name):
160158"""Get or create the module lock for a given module name.
161159162- Should only be called with the import lock taken."""
163-lock = None
160+ Acquire/release internally the global import lock to protect
161+ _module_locks."""
162+163+_imp.acquire_lock()
164164try:
165-lock = _module_locks[name]()
166-except KeyError:
167-pass
168-if lock is None:
169-if _thread is None:
170-lock = _DummyModuleLock(name)
171-else:
172-lock = _ModuleLock(name)
173-def cb(_):
174-del _module_locks[name]
175-_module_locks[name] = _weakref.ref(lock, cb)
165+try:
166+lock = _module_locks[name]()
167+except KeyError:
168+lock = None
169+170+if lock is None:
171+if _thread is None:
172+lock = _DummyModuleLock(name)
173+else:
174+lock = _ModuleLock(name)
175+def cb(_):
176+del _module_locks[name]
177+_module_locks[name] = _weakref.ref(lock, cb)
178+finally:
179+_imp.release_lock()
180+176181return lock
177182183+178184def _lock_unlock_module(name):
179-"""Release the global import lock, and acquires then release the
180- module lock for a given module name.
185+"""Acquires then releases the module lock for a given module name.
186+181187 This is used to ensure a module is completely initialized, in the
182188 event it is being imported by another thread.
183-184- Should only be called with the import lock taken."""
189+ """
185190lock = _get_module_lock(name)
186-_imp.release_lock()
187191try:
188192lock.acquire()
189193except _DeadlockError:
@@ -587,7 +591,6 @@ def _module_repr_from_spec(spec):
587591def _exec(spec, module):
588592"""Execute the spec's specified module in an existing module's namespace."""
589593name = spec.name
590-_imp.acquire_lock()
591594with _ModuleLockManager(name):
592595if sys.modules.get(name) is not module:
593596msg = 'module {!r} not in sys.modules'.format(name)
@@ -670,7 +673,6 @@ def _load(spec):
670673 clobbered.
671674672675 """
673-_imp.acquire_lock()
674676with _ModuleLockManager(spec.name):
675677return _load_unlocked(spec)
676678@@ -957,16 +959,16 @@ def _find_and_load_unlocked(name, import_):
957959958960def _find_and_load(name, import_):
959961"""Find and load the module."""
960-_imp.acquire_lock()
961-if name not in sys.modules:
962-with _ModuleLockManager(name):
962+with _ModuleLockManager(name):
963+if name not in sys.modules:
963964return _find_and_load_unlocked(name, import_)
965+964966module = sys.modules[name]
965967if module is None:
966-_imp.release_lock()
967968message = ('import of {} halted; '
968969'None in sys.modules'.format(name))
969970raise ModuleNotFoundError(message, name=name)
971+970972_lock_unlock_module(name)
971973return module
972974