Interpreter shutdown can deadlock when a daemon thread holds the warnings lock #159485
Copy link
Copy link
Open
Labels
3.14bugs and security fixesbugs and security fixes3.15bugs and security fixesbugs and security fixes3.16new features, bugs and security fixesnew features, bugs and security fixesinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
Description
Originally reported by @krassowski in #158973.
A daemon thread can be stopped while holding the warnings lock. During interpreter finalization, garbage collection triggers a ResourceWarning, deadlocking the finalizing thread:
import threading
import time
import warnings
ready = threading.Event()
warnings.simplefilter("always", ResourceWarning)
class WarnAtShutdown:
def __init__(self):
self.self_ref = self # Defer __del__ until shutdown GC.
def __del__(self):
warnings.warn("late", ResourceWarning)
def showwarning(message, *args, **kwargs):
if str(message) == "hold":
ready.set()
time.sleep(0.1)
warnings.showwarning = showwarning
threading.Thread(
target=lambda: warnings.warn("hold", ResourceWarning),
daemon=True,
).start()
assert ready.wait(4)
WarnAtShutdown()Expected: the process exits normally. Actual: it hangs during interpreter shutdown.
- CPython versions tested on:
3.14.8,3.15.0. - Operating systems tested on:
macOS.
Linked PRs
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
3.14bugs and security fixesbugs and security fixes3.15bugs and security fixesbugs and security fixes3.16new features, bugs and security fixesnew features, bugs and security fixesinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
Activity
pythongh-159485: Fix warnings lock deadlock during interpreter shutdown
ngoldbaum commentedon Oct 16, 2026
I updated the reproducer script in the issue to avoid an unbounded wait (it now just does
time.sleep(0.1)liketest_threadingdoes). This is more realistic and is fixed by @ZeroIntensity's InterpreterGuard approach. There's no need to guard against waiting forever in a callback like my original reproducer was arguing for. There's still a deadlock even without that and the InterpreterGuard fixes it.pythongh-159485: Hold an interpreter guard while using the warnings lock
pythongh-159485: Use the public interpreter guard API for the warning…