feat(perf): LEDMATRIX_STALL_WATCHDOG_MS lowers the stall watchdog's threshold

250ms catches freezes; the hitches left on hdpi are frames 2-5 refreshes
late, which look like the render thread waiting for the GIL. At 30ms the
watchdog dumps those too, naming what the other threads were running when
the frame missed. It polls at a third of the threshold so a stall one poll
long is still seen, which costs some GIL time of its own: a diagnostic
setting, not one to soak with.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Chuck
2026-09-24 14:26:04 -04:00
co-authored by Claude Opus 5.5
parent f79618d4f7
commit b67818d5c5
3 changed files with 51 additions and 2 deletions
+20
View File
@@ -9,6 +9,8 @@ import sys
import time
from pathlib import Path
import pytest
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from src.common import frame_timing # noqa: E402
@@ -303,6 +305,24 @@ def test_watchdog_rate_limits_its_dumps(caplog):
assert dog.stalls == 2
def test_watchdog_threshold_can_be_lowered_for_a_diagnostic_run(monkeypatch):
monkeypatch.delenv("LEDMATRIX_STALL_WATCHDOG_MS", raising=False)
assert frame_timing.watchdog_settings() == {}
monkeypatch.setenv("LEDMATRIX_STALL_WATCHDOG_MS", "nonsense")
assert frame_timing.watchdog_settings() == {}
monkeypatch.setenv("LEDMATRIX_STALL_WATCHDOG_MS", "30")
settings = frame_timing.watchdog_settings()
assert settings["threshold"] == pytest.approx(0.030)
assert settings["poll"] == pytest.approx(0.010) # sees a stall one poll long
# ...and the recorder starts its watchdog with them.
rec = frame_timing.FrameTimingRecorder(path=None)
rec.scrolling_now = lambda: True
monkeypatch.setattr(frame_timing.StallWatchdog, "start", lambda self: None)
rec.record(0.001, 0.009, 1, True, 1.0)
assert rec.watchdog.threshold == pytest.approx(0.030)
# --- measuring the panel, and runs that never locked -------------------------
# The refresh measurement and the "not locked" cases came from the first
# version of scripts/render_bench.py, which graded runs with its own module.