Visitar URL original
gh-91351: Support re-entrancy in importlib/_bootstrap.py by exarkun · Pull Request #94342 · python/cpython · GitHub
Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Move _blocking_on management into a context manager
This more clearly separates the logic for that management from the application
code that runs in this context.

It will also make subsequent changes to improve that logic more clear.
  • Loading branch information
exarkun committed Jun 27, 2022
commit 8df98a0bd8f25698cd35dc5f70530f4ae12d20b7
38 changes: 33 additions & 5 deletions Lib/importlib/_bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,37 @@ def _new_module(name):
_blocking_on = {}


class _BlockingOnManager:
"""
A context manager responsible to updating ``_blocking_on`` to track which
threads are likely blocked on taking the import locks for which modules.
"""
def __init__(self, tid, lock):
# The id of the thread in which this manager is being used.
self.tid = tid
# The _ModuleLock for a certain module which the running thread wants
# to take.
self.lock = lock

def __enter__(self):
"""
Mark the running thread as waiting for the lock this manager knows
about.

:return: A context manager which can be used to take and release the
lock for the _ModuleLock this manager knows about.
"""
_blocking_on[self.tid] = self.lock
return self.lock.lock

def __exit__(self, *args, **kwargs):
"""
Mark the running thread as no longer waiting for the lock this manager
knows about.
"""
del _blocking_on[self.tid]


class _DeadlockError(RuntimeError):
pass

Expand Down Expand Up @@ -112,13 +143,12 @@ def acquire(self):
Otherwise, the lock is always acquired and True is returned.
"""
tid = _thread.get_ident()
_blocking_on[tid] = self
try:
with _BlockingOnManager(tid, self) as the_lock:
while True:
# Protect interaction with state on self with a per-module
# lock. This makes it safe for more than one thread to try to
# acquire the lock for a single module at the same time.
with self.lock:
with the_lock:
if self.count == 0 or self.owner == tid:
# If the lock for this module is unowned then we can
# take the lock immediately and succeed. If the lock
Expand Down Expand Up @@ -174,8 +204,6 @@ def acquire(self):
# give it up now. We'll take it non-blockingly again on the
# next iteration around this while loop.
self.wakeup.release()
finally:
del _blocking_on[tid]

def release(self):
tid = _thread.get_ident()
Expand Down
Loading