#!/usr/bin/env python3 import os import sys import time import importlib import signal delay = 2 # # In production, we've seen trackbacks like: # # File "/home/pokybuild/yocto-worker/oe-selftest-fedora/build/meta/lib/oe/package_manager/__init__.py", line 17, in : # import string # >from oe.gpg_sign import get_signer # import hashlib # File "", line 1004, in _find_and_load(name='oe.gpg_sign', import_=) # File "", line 158, in _ModuleLockManager.__enter__() # File "", line 110, in _ModuleLock.acquire() # KeyError: 139622474778432 # # and # # File "/home/pokybuild/yocto-worker/oe-selftest-centos/build/meta/classes/base.bbclass", line 30, in oe_import(d=): # # Make a python object accessible from the metadata # > bb.utils._context[toimport.split(".", 1)[0]] = __import__(toimport) # except AttributeError as e: # File "", line 1004, in _find_and_load(name='oe.path', import_=) # File "", line 158, in _ModuleLockManager.__enter__() # File "", line 110, in _ModuleLock.acquire() # KeyError: 140438942700992 # # # This reproduction script shows that if an import XXX is in progress and waiting at the wrong # point when an interrupt arrives (in this case a signal) and triggers it's own import YYY, # _blocking_on[tid] gets overwritten and lost, triggering the traceback we see above upon # exit of the second import. I don't know what the interrupt is in our production case, I just # use a signal handler here to prove it is possible. # implib = sys.modules["_frozen_importlib"] _thread = sys.modules["_thread"] _blocking_on = implib._blocking_on # This is acquire from importlib/_bootstrap.py with a delay inserted between self being # added to _blocking_on and obtaining the lock. It needs to match your python install def acquire(self): global delay tid = _thread.get_ident() if tid in _blocking_on: print("TID already in blocking_on from %s but called by %s" % (_blocking_on[tid].name, self.name)) delay = 0 _blocking_on[tid] = self if delay: time.sleep(delay) try: while True: with self.lock: if self.count == 0 or self.owner == tid: self.owner = tid self.count += 1 return True if self.has_deadlock(): raise _DeadlockError('deadlock detected by %r' % self) if self.wakeup.acquire(False): self.waiters += 1 # Wait for a release() call self.wakeup.acquire() self.wakeup.release() finally: del _blocking_on[tid] implib._ModuleLock.acquire = acquire def handler(signum, frame): print("Handler starting import") delay = 0 import glob print("Handler completed import") return signal.signal(signal.SIGALRM, handler) signal.alarm(1) print("Process starting import") import datetime print("Process completed import")