Update audio_engine.py

Summary

Add thread-safe locking for wake word reload logic

Problem

Wake word reload logic accessed self.state.wake_words_changed flag without synchronization, creating a potential race condition when wake words are dynamically switched via Home Assistant.

Changes

Added self._wake_words_lock = threading.Lock() in __init__
Wrapped wake word reload block with with self._wake_words_lock: context manager
Ensures atomic read-modify-write of wake_words_changed flag
Impact

Risk: Very low - purely defensive improvement
Benefit: Eliminates race condition in wake word switching
Performance: Negligible overhead (lock only held during reload check)
This commit is contained in:
imonlinux
2026-01-10 13:23:33 -08:00
committed by GitHub
parent fed867936a
commit 1da9ac1fac
+30 -25
View File
@@ -27,6 +27,9 @@ class AudioEngine:
self.mic = mic self.mic = mic
self.block_size = block_size self.block_size = block_size
self._thread: Optional[threading.Thread] = None self._thread: Optional[threading.Thread] = None
# CRITICAL FIX: Add lock for thread-safe wake word reload
self._wake_words_lock = threading.Lock()
def start(self): def start(self):
"""Starts the audio processing thread.""" """Starts the audio processing thread."""
@@ -113,33 +116,35 @@ class AudioEngine:
time.sleep(0.01) time.sleep(0.01)
continue continue
# Update active wake words if changed # CRITICAL FIX: Protect wake word reload with lock
if (not wake_words) or (self.state.wake_words_changed): with self._wake_words_lock:
self.state.wake_words_changed = False # Update active wake words if changed
wake_words = [ if (not wake_words) or (self.state.wake_words_changed):
ww self.state.wake_words_changed = False
for ww in self.state.wake_words.values() wake_words = [
if ww.id in self.state.active_wake_words ww
] for ww in self.state.wake_words.values()
has_oww = any( if ww.id in self.state.active_wake_words
isinstance(ww, OpenWakeWord) for ww in wake_words ]
) has_oww = any(
isinstance(ww, OpenWakeWord) for ww in wake_words
if has_oww and (oww_features is None):
_LOGGER.debug(
"Initializing OpenWakeWord features..."
) )
oww_features = OpenWakeWordFeatures.from_builtin()
_LOGGER.debug( if has_oww and (oww_features is None):
"Rebuilt wake_words list: %s (has_oww=%s, active_ids=%s)", _LOGGER.debug(
[ "Initializing OpenWakeWord features..."
getattr(ww, "wake_word", getattr(ww, "id", "?")) )
for ww in wake_words oww_features = OpenWakeWordFeatures.from_builtin()
],
has_oww, _LOGGER.debug(
self.state.active_wake_words, "Rebuilt wake_words list: %s (has_oww=%s, active_ids=%s)",
) [
getattr(ww, "wake_word", getattr(ww, "id", "?"))
for ww in wake_words
],
has_oww,
self.state.active_wake_words,
)
try: try:
# Feature extraction # Feature extraction