diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2017-07-10 21:16:27 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-07-10 21:16:27 (GMT) |
commit | fe6e686c27ce3a3ecdf03803cff7e230dee5530d (patch) | |
tree | dea6f45e966e1d4a38044da15992a306759b0fa6 /Lib | |
parent | 044e156426825acac8b6c6d1ce14d5b7bcb20bc9 (diff) | |
download | cpython-fe6e686c27ce3a3ecdf03803cff7e230dee5530d.zip cpython-fe6e686c27ce3a3ecdf03803cff7e230dee5530d.tar.gz cpython-fe6e686c27ce3a3ecdf03803cff7e230dee5530d.tar.bz2 |
bpo-30891: Fix importlib _find_and_load() race condition (#2646) (#2651)
* Rewrite importlib _get_module_lock(): it is now responsible to hold
the imp lock directly.
* _find_and_load() now holds the module lock to check if name is in
sys.modules to prevent a race condition
(cherry picked from commit 4f9a446f3fb42f800e73cd9414dd1eccb3ca4fa7)
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/importlib/_bootstrap.py | 58 |
1 files changed, 30 insertions, 28 deletions
diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index f812a18..f7790c1 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -39,6 +39,7 @@ def _new_module(name): # Module-level locking ######################################################## # A dict mapping module names to weakrefs of _ModuleLock instances +# Dictionary protected by the global import lock _module_locks = {} # A dict mapping thread ids to _ModuleLock instances _blocking_on = {} @@ -144,10 +145,7 @@ class _ModuleLockManager: self._lock = None def __enter__(self): - try: - self._lock = _get_module_lock(self._name) - finally: - _imp.release_lock() + self._lock = _get_module_lock(self._name) self._lock.acquire() def __exit__(self, *args, **kwargs): @@ -159,31 +157,37 @@ class _ModuleLockManager: def _get_module_lock(name): """Get or create the module lock for a given module name. - Should only be called with the import lock taken.""" - lock = None + Acquire/release internally the global import lock to protect + _module_locks.""" + + _imp.acquire_lock() try: - lock = _module_locks[name]() - except KeyError: - pass - if lock is None: - if _thread is None: - lock = _DummyModuleLock(name) - else: - lock = _ModuleLock(name) - def cb(_): - del _module_locks[name] - _module_locks[name] = _weakref.ref(lock, cb) + try: + lock = _module_locks[name]() + except KeyError: + lock = None + + if lock is None: + if _thread is None: + lock = _DummyModuleLock(name) + else: + lock = _ModuleLock(name) + def cb(_): + del _module_locks[name] + _module_locks[name] = _weakref.ref(lock, cb) + finally: + _imp.release_lock() + return lock + def _lock_unlock_module(name): - """Release the global import lock, and acquires then release the - module lock for a given module name. + """Acquires then releases the module lock for a given module name. + This is used to ensure a module is completely initialized, in the event it is being imported by another thread. - - Should only be called with the import lock taken.""" + """ lock = _get_module_lock(name) - _imp.release_lock() try: lock.acquire() except _DeadlockError: @@ -587,7 +591,6 @@ def _module_repr_from_spec(spec): def _exec(spec, module): """Execute the spec's specified module in an existing module's namespace.""" name = spec.name - _imp.acquire_lock() with _ModuleLockManager(name): if sys.modules.get(name) is not module: msg = 'module {!r} not in sys.modules'.format(name) @@ -670,7 +673,6 @@ def _load(spec): clobbered. """ - _imp.acquire_lock() with _ModuleLockManager(spec.name): return _load_unlocked(spec) @@ -957,16 +959,16 @@ def _find_and_load_unlocked(name, import_): def _find_and_load(name, import_): """Find and load the module.""" - _imp.acquire_lock() - if name not in sys.modules: - with _ModuleLockManager(name): + with _ModuleLockManager(name): + if name not in sys.modules: return _find_and_load_unlocked(name, import_) + module = sys.modules[name] if module is None: - _imp.release_lock() message = ('import of {} halted; ' 'None in sys.modules'.format(name)) raise ModuleNotFoundError(message, name=name) + _lock_unlock_module(name) return module |