mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-10-03 17:15:19 -07:00
test(offline-guard): run the opposite-mode nesting check in a subprocess, not a thread
This commit is contained in:
@@ -12,6 +12,7 @@ parallelism (e.g. ``pytest-xdist`` with ``--dist=loadfile``/``loadscope``);
|
|||||||
run this file serially.
|
run this file serially.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import multiprocessing
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
@@ -25,6 +26,27 @@ sys.path.insert(0, str(Path(__file__).parent.parent))
|
|||||||
from utils.hf_offline_patch import force_offline_if_cached # noqa: E402
|
from utils.hf_offline_patch import force_offline_if_cached # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
def _nest_opposite_modes_in_subprocess(queue):
|
||||||
|
"""Module-level so it's picklable for multiprocessing's spawn start method.
|
||||||
|
|
||||||
|
Runs in a fresh child process rather than a thread of the test process,
|
||||||
|
so a real deadlock here (a regression of the guard this test exists for)
|
||||||
|
can't leave the shared _offline_cv state corrupted for every other test
|
||||||
|
in this run: the child either exits cleanly (guard raised) or gets
|
||||||
|
terminated by the parent after the timeout, and either way the test
|
||||||
|
process's own state was never touched.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with force_offline_if_cached(False, "outer-uncached"), force_offline_if_cached(
|
||||||
|
True, "inner-cached"
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
except Exception as exc:
|
||||||
|
queue.put(("exc", type(exc).__name__, str(exc)))
|
||||||
|
else:
|
||||||
|
queue.put(("ok", None, None))
|
||||||
|
|
||||||
|
|
||||||
def _hf_const():
|
def _hf_const():
|
||||||
import huggingface_hub.constants as hf_const
|
import huggingface_hub.constants as hf_const
|
||||||
|
|
||||||
@@ -161,29 +183,31 @@ def test_nesting_opposite_mode_on_same_thread_raises_instead_of_deadlocking():
|
|||||||
condition can only be cleared by the outer call's own exit, which can
|
condition can only be cleared by the outer call's own exit, which can
|
||||||
never run because it's blocked inside the inner call waiting for it.
|
never run because it's blocked inside the inner call waiting for it.
|
||||||
|
|
||||||
Runs in a background daemon thread with a bounded join so a regression
|
Runs in a spawned child process with a bounded join, terminated if it's
|
||||||
fails this test instead of hanging the whole suite.
|
still alive after the timeout, so a regression fails this test instead of
|
||||||
|
hanging the suite or leaving _offline_cv's shared state corrupted for
|
||||||
|
every other test in this run.
|
||||||
"""
|
"""
|
||||||
result: dict = {}
|
ctx = multiprocessing.get_context("spawn")
|
||||||
|
queue = ctx.Queue()
|
||||||
|
proc = ctx.Process(target=_nest_opposite_modes_in_subprocess, args=(queue,))
|
||||||
|
proc.start()
|
||||||
|
proc.join(timeout=5)
|
||||||
|
|
||||||
def run():
|
if proc.is_alive():
|
||||||
try:
|
proc.terminate()
|
||||||
with force_offline_if_cached(False, "outer-uncached"), force_offline_if_cached(True, "inner-cached"):
|
proc.join(timeout=2)
|
||||||
pass
|
if proc.is_alive():
|
||||||
except Exception as exc:
|
proc.kill()
|
||||||
result["exc"] = exc
|
proc.join(timeout=2)
|
||||||
else:
|
pytest.fail(
|
||||||
result["exc"] = None
|
"nesting the opposite mode on the same thread hung instead of raising, "
|
||||||
|
"this is the deadlock the per-thread mode-stack guard exists to prevent"
|
||||||
|
)
|
||||||
|
|
||||||
t = threading.Thread(target=run, daemon=True)
|
kind, exc_type, exc_msg = queue.get(timeout=2)
|
||||||
t.start()
|
assert kind == "exc", (kind, exc_type, exc_msg)
|
||||||
t.join(timeout=3)
|
assert exc_type == "RuntimeError", (kind, exc_type, exc_msg)
|
||||||
|
|
||||||
assert not t.is_alive(), (
|
|
||||||
"nesting the opposite mode on the same thread hung instead of raising -- "
|
|
||||||
"this is the deadlock the per-thread mode-stack guard exists to prevent"
|
|
||||||
)
|
|
||||||
assert isinstance(result.get("exc"), RuntimeError), result.get("exc")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user