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):

144145

self._lock = None

145146146147

def __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)

151149

self._lock.acquire()

152150153151

def __exit__(self, *args, **kwargs):

@@ -159,31 +157,37 @@ def __exit__(self, *args, **kwargs):

159157

def _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()

164164

try:

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+176181

return lock

177182183+178184

def _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+

"""

185190

lock = _get_module_lock(name)

186-

_imp.release_lock()

187191

try:

188192

lock.acquire()

189193

except _DeadlockError:

@@ -587,7 +591,6 @@ def _module_repr_from_spec(spec):

587591

def _exec(spec, module):

588592

"""Execute the spec's specified module in an existing module's namespace."""

589593

name = spec.name

590-

_imp.acquire_lock()

591594

with _ModuleLockManager(name):

592595

if sys.modules.get(name) is not module:

593596

msg = 'module {!r} not in sys.modules'.format(name)

@@ -670,7 +673,6 @@ def _load(spec):

670673

clobbered.

671674672675

"""

673-

_imp.acquire_lock()

674676

with _ModuleLockManager(spec.name):

675677

return _load_unlocked(spec)

676678

@@ -957,16 +959,16 @@ def _find_and_load_unlocked(name, import_):

957959958960

def _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:

963964

return _find_and_load_unlocked(name, import_)

965+964966

module = sys.modules[name]

965967

if module is None:

966-

_imp.release_lock()

967968

message = ('import of {} halted; '

968969

'None in sys.modules'.format(name))

969970

raise ModuleNotFoundError(message, name=name)

971+970972

_lock_unlock_module(name)

971973

return module

972974