summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorArmin Rigo <armin.rigo@gmail.com>2020-03-03 01:37:25 (GMT)
committerGitHub <noreply@github.com>2020-03-03 01:37:25 (GMT)
commit6daa37fd42c5d5300172728e8b4de74fe0b319fc (patch)
treefea0f80ed7f996670197dc3b60896b306b29dc9b /Lib
parentce3a4984089b8e0ce5422ca32d75ad057b008074 (diff)
downloadcpython-6daa37fd42c5d5300172728e8b4de74fe0b319fc.zip
cpython-6daa37fd42c5d5300172728e8b4de74fe0b319fc.tar.gz
cpython-6daa37fd42c5d5300172728e8b4de74fe0b319fc.tar.bz2
bpo-38091: Import deadlock detection causes deadlock (GH-17518)
Automerge-Triggered-By: @brettcannon
Diffstat (limited to 'Lib')
-rw-r--r--Lib/importlib/_bootstrap.py9
-rw-r--r--Lib/test/test_import/__init__.py8
2 files changed, 17 insertions, 0 deletions
diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py
index 7b74e88..e00b27e 100644
--- a/Lib/importlib/_bootstrap.py
+++ b/Lib/importlib/_bootstrap.py
@@ -67,6 +67,7 @@ class _ModuleLock:
# Deadlock avoidance for concurrent circular imports.
me = _thread.get_ident()
tid = self.owner
+ seen = set()
while True:
lock = _blocking_on.get(tid)
if lock is None:
@@ -74,6 +75,14 @@ class _ModuleLock:
tid = lock.owner
if tid == me:
return True
+ if tid in seen:
+ # bpo 38091: the chain of tid's we encounter here
+ # eventually leads to a fixpoint or a cycle, but
+ # does not reach 'me'. This means we would not
+ # actually deadlock. This can happen if other
+ # threads are at the beginning of acquire() below.
+ return False
+ seen.add(tid)
def acquire(self):
"""
diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py
index 482fe6a..d50befc 100644
--- a/Lib/test/test_import/__init__.py
+++ b/Lib/test/test_import/__init__.py
@@ -436,16 +436,24 @@ class ImportTests(unittest.TestCase):
os.does_not_exist
def test_concurrency(self):
+ # bpo 38091: this is a hack to slow down the code that calls
+ # has_deadlock(); the logic was itself sometimes deadlocking.
+ def delay_has_deadlock(frame, event, arg):
+ if event == 'call' and frame.f_code.co_name == 'has_deadlock':
+ time.sleep(0.1)
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'data'))
try:
exc = None
def run():
+ sys.settrace(delay_has_deadlock)
event.wait()
try:
import package
except BaseException as e:
nonlocal exc
exc = e
+ sys.settrace(None)
for i in range(10):
event = threading.Event()