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:
@@ -28,6 +28,9 @@ class AudioEngine:
|
|||||||
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."""
|
||||||
self._thread = threading.Thread(
|
self._thread = threading.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
|
||||||
|
|||||||
Reference in New Issue
Block a user