From 5b53e850ddddd5011ae53a25ce53f86a525d1eed Mon Sep 17 00:00:00 2001 From: Jatin K Malik Date: Fri, 27 Mar 2026 11:14:37 -0700 Subject: [PATCH] fix(engine): stop recognition before switching engines to prevent segfault (fixes #350) (#355) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When switching from whisper.cpp to VOSK via the settings dialog, the _auto_apply_settings path called reconfigure() without first stopping active recognition. This allowed background audio/transcription threads to continue using the old engine's native C resources (whisper.cpp model) while they were being freed by Python's GC, causing a segmentation fault. Changes: - Add stop_recognition() guard in reconfigure() itself when the engine or model changes and recognition is active — this makes the API safe regardless of which caller triggers the reconfiguration - Add defense-in-depth stop_recognition() call in _auto_apply_settings to match the existing pattern in _apply_settings_internal --- src/vocalinux/speech_recognition/recognition_manager.py | 9 +++++++++ src/vocalinux/ui/settings_dialog.py | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/src/vocalinux/speech_recognition/recognition_manager.py b/src/vocalinux/speech_recognition/recognition_manager.py index fe62667..25660e6 100644 --- a/src/vocalinux/speech_recognition/recognition_manager.py +++ b/src/vocalinux/speech_recognition/recognition_manager.py @@ -2073,6 +2073,15 @@ class SpeechRecognitionManager: if restart_needed: logger.info("Engine or model changed, re-initializing...") + + # Stop any active recognition before switching engines. + # This is critical to prevent segfaults when the old engine's + # native resources (e.g. whisper.cpp C model) are freed while + # a background thread is still using them. + if self.state != RecognitionState.IDLE: + logger.info("Stopping active recognition before engine switch...") + self.stop_recognition() + # When reconfiguring from UI, allow downloads old_defer = self._defer_download self._defer_download = not force_download diff --git a/src/vocalinux/ui/settings_dialog.py b/src/vocalinux/ui/settings_dialog.py index 8aa870a..8797728 100644 --- a/src/vocalinux/ui/settings_dialog.py +++ b/src/vocalinux/ui/settings_dialog.py @@ -1916,6 +1916,13 @@ class SettingsDialog(Gtk.Dialog): self.config_manager.update_speech_recognition_settings(settings) self.config_manager.save_settings() + + # Stop recognition before reconfiguring to avoid segfaults when + # switching between engines with native resources (e.g. whisper.cpp) + was_running = self.speech_engine.state != RecognitionState.IDLE + if was_running: + self.speech_engine.stop_recognition() + self.speech_engine.reconfigure(**settings) logger.info("Settings auto-applied successfully") except Exception as e: