57 lines
1.6 KiB
Python
57 lines
1.6 KiB
Python
from PyQt6.QtCore import QObject, QTimer, QElapsedTimer
|
|
import logging
|
|
import time
|
|
|
|
from config import Config
|
|
|
|
class EventLoopJitterMonitor(QObject):
|
|
def __init__(
|
|
self,
|
|
parent=None,
|
|
interval_ms: int = 20,
|
|
jitter_threshold_ms: int = 100,
|
|
log_cooldown_s: float = 1.0,
|
|
):
|
|
super().__init__(parent)
|
|
self._interval = interval_ms
|
|
self._jitter_threshold = jitter_threshold_ms
|
|
self._log_cooldown_s = log_cooldown_s
|
|
|
|
self._timer = QTimer(self)
|
|
self._timer.setInterval(self._interval)
|
|
self._timer.timeout.connect(self._on_timeout)
|
|
|
|
self._elapsed = QElapsedTimer()
|
|
self._elapsed.start()
|
|
self._last = self._elapsed.elapsed()
|
|
|
|
# child logger: e.g. "musicmuster.jitter"
|
|
self._log = logging.getLogger(f"{Config.LOG_NAME}.jitter")
|
|
self._last_log_time = 0.0
|
|
|
|
def start(self) -> None:
|
|
self._timer.start()
|
|
|
|
def _on_timeout(self) -> None:
|
|
now_ms = self._elapsed.elapsed()
|
|
delta = now_ms - self._last
|
|
self._last = now_ms
|
|
|
|
if delta > (self._interval + self._jitter_threshold):
|
|
self._log_jitter(now_ms, delta)
|
|
|
|
def _log_jitter(self, now_ms: int, gap_ms: int) -> None:
|
|
now = time.monotonic()
|
|
|
|
# simple rate limit: only one log every log_cooldown_s
|
|
if now - self._last_log_time < self._log_cooldown_s:
|
|
return
|
|
self._last_log_time = now
|
|
|
|
self._log.warning(
|
|
"Event loop gap detected: t=%d ms, gap=%d ms (interval=%d ms)",
|
|
now_ms,
|
|
gap_ms,
|
|
self._interval,
|
|
)
|