From b50c816088ac7825e044430fe1ff804205fa402b Mon Sep 17 00:00:00 2001 From: Raju Date: Mon, 23 Mar 2026 15:52:56 +0530 Subject: [PATCH] fix[notask]: replace global streaming state with per-instance map in whispercpp The streaming processor used three process-global variables (g_streamingMtx, g_streamingInstance, g_streamingProcessor) which limited the entire process to a single streaming session and risked dangling-pointer access if the owning AddonJs instance was destroyed without cleanup. Replace with an unordered_map keyed by AddonJs* so each addon instance independently owns its streaming session, eliminating the race condition and enabling concurrent streaming across multiple instances. Made-with: Cursor --- .../addon/src/addon/AddonJs.hpp | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/packages/qvac-lib-infer-whispercpp/addon/src/addon/AddonJs.hpp b/packages/qvac-lib-infer-whispercpp/addon/src/addon/AddonJs.hpp index 09aeadd13b..a714b31cc3 100644 --- a/packages/qvac-lib-infer-whispercpp/addon/src/addon/AddonJs.hpp +++ b/packages/qvac-lib-infer-whispercpp/addon/src/addon/AddonJs.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -25,8 +26,9 @@ namespace qvac_lib_inference_addon_whisper { inline std::mutex g_streamingMtx; -inline qvac_lib_inference_addon_cpp::AddonJs* g_streamingInstance = nullptr; -inline std::unique_ptr g_streamingProcessor; +inline std::unordered_map< + qvac_lib_inference_addon_cpp::AddonJs*, + std::unique_ptr> g_streamingSessions; namespace js = qvac_lib_inference_addon_cpp::js; using qvac_lib_inference_addon_cpp::OutputQueue; @@ -245,15 +247,14 @@ startStreaming(js_env_t* env, js_callback_info_t* info) try { { std::lock_guard lock(g_streamingMtx); - if (g_streamingProcessor) { + if (g_streamingSessions.count(&instance) != 0) { throw std::runtime_error( - "Streaming session already active"); + "Streaming session already active for this instance"); } auto& whisperModel = dynamic_cast(instance.addonCpp->model.get()); - g_streamingInstance = &instance; - g_streamingProcessor = std::make_unique( + g_streamingSessions[&instance] = std::make_unique( whisperModel, instance.addonCpp->outputQueue, config); @@ -296,10 +297,11 @@ appendStreamingAudio(js_env_t* env, js_callback_info_t* info) try { StreamingProcessor* processor = nullptr; { std::lock_guard lock(g_streamingMtx); - if (!g_streamingProcessor || g_streamingInstance != &instance) { + auto it = g_streamingSessions.find(&instance); + if (it == g_streamingSessions.end()) { throw std::runtime_error("No active streaming session for this instance"); } - processor = g_streamingProcessor.get(); + processor = it->second.get(); } processor->appendAudio(std::move(samples)); @@ -316,11 +318,12 @@ cleanupStreamingSession( std::unique_ptr processor; { std::lock_guard lock(g_streamingMtx); - if (!g_streamingProcessor || g_streamingInstance != &instance) { + auto it = g_streamingSessions.find(&instance); + if (it == g_streamingSessions.end()) { return false; } - processor = std::move(g_streamingProcessor); - g_streamingInstance = nullptr; + processor = std::move(it->second); + g_streamingSessions.erase(it); } if (forceful) { processor->cancel(); @@ -340,10 +343,11 @@ cancelWithStreaming(js_env_t* env, js_callback_info_t* info) try { std::shared_ptr processor; { std::lock_guard lock(g_streamingMtx); - if (g_streamingProcessor && g_streamingInstance == &instance) { + auto it = g_streamingSessions.find(&instance); + if (it != g_streamingSessions.end()) { processor = std::shared_ptr( - std::move(g_streamingProcessor)); - g_streamingInstance = nullptr; + std::move(it->second)); + g_streamingSessions.erase(it); } }