From 839ad76c5b062fb445a47564f362d199e2550f7b Mon Sep 17 00:00:00 2001 From: baiqing Date: Tue, 5 May 2026 09:49:33 +0800 Subject: [PATCH] =?UTF-8?q?fix(local-asr):=20=E7=94=A8=20tauri::async=5Fru?= =?UTF-8?q?ntime=20=E5=8F=96=E4=BB=A3=20tokio::spawn=20=E2=80=94=20?= =?UTF-8?q?=E4=BF=AE=E7=82=B9=E5=87=BB=E4=B8=8B=E8=BD=BD=E5=8D=B3=E5=B4=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 崩溃栈:local_asr_download_model → DownloadManager::start → tokio::spawn → panic("there is no reactor running")。 根因:Tauri 同步 command 在 main thread 上下文跑,没进 tokio runtime; tokio::spawn 直接 panic。换 tauri::async_runtime::spawn —— 它走 Tauri 持有的 runtime handle,不依赖调用方上下文。 同步把 local_provider.rs 的 spawn_blocking 也换成 tauri::async_runtime 版本(虽然现在在 async 路径上调没事,保持一致防回归)。 附带把 qwen_ffi.rs 的 `unsafe extern "C"` block 改回经典 extern block, 匹配 CLAUDE.md 声明的 rust-version = "1.77"(前者要 1.82+)。 --- openless-all/app/src-tauri/src/asr/local/download.rs | 6 +++++- openless-all/app/src-tauri/src/asr/local/local_provider.rs | 5 ++++- openless-all/app/src-tauri/src/asr/local/qwen_ffi.rs | 4 +++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/openless-all/app/src-tauri/src/asr/local/download.rs b/openless-all/app/src-tauri/src/asr/local/download.rs index e6ef4c2e..2a58e7a7 100644 --- a/openless-all/app/src-tauri/src/asr/local/download.rs +++ b/openless-all/app/src-tauri/src/asr/local/download.rs @@ -201,7 +201,11 @@ impl DownloadManager { }; let manager = Arc::clone(self); - tokio::spawn(async move { + // 用 tauri::async_runtime::spawn 而不是 tokio::spawn —— + // Tauri 同步 command 不在 tokio runtime 上下文里,调 tokio::spawn 会立刻 + // panic("there is no reactor running, must be called from the context of a Tokio 1.x runtime")。 + // tauri::async_runtime 走 Tauri 持有的 runtime handle,不依赖调用方上下文。 + tauri::async_runtime::spawn(async move { let result = run_download(&app, model_id, mirror, &flag).await; manager.cancel_flags.lock().remove(&key); match result { diff --git a/openless-all/app/src-tauri/src/asr/local/local_provider.rs b/openless-all/app/src-tauri/src/asr/local/local_provider.rs index a295e93b..06e381c5 100644 --- a/openless-all/app/src-tauri/src/asr/local/local_provider.rs +++ b/openless-all/app/src-tauri/src/asr/local/local_provider.rs @@ -65,8 +65,11 @@ impl LocalQwenAsr { })); // qwen_transcribe_stream 是阻塞调用;用 spawn_blocking 防止占住 tokio runtime。 + // 用 tauri::async_runtime::spawn_blocking 而非 tokio 的 —— 同 download.rs 注释, + // 走 Tauri 持有的 runtime handle,不依赖调用方上下文(虽然这里目前都在 async 路径上调, + // 但保持一致更稳)。 let engine = Arc::clone(&self.engine); - let text = tokio::task::spawn_blocking(move || engine.transcribe_stream(&samples_f32)) + let text = tauri::async_runtime::spawn_blocking(move || engine.transcribe_stream(&samples_f32)) .await .context("transcribe spawn_blocking join 失败")? .context("qwen_transcribe_stream 失败")?; diff --git a/openless-all/app/src-tauri/src/asr/local/qwen_ffi.rs b/openless-all/app/src-tauri/src/asr/local/qwen_ffi.rs index 81a9ec75..ae9def7d 100644 --- a/openless-all/app/src-tauri/src/asr/local/qwen_ffi.rs +++ b/openless-all/app/src-tauri/src/asr/local/qwen_ffi.rs @@ -14,7 +14,9 @@ pub struct QwenCtx { /// `typedef void (*qwen_token_cb)(const char *piece, void *userdata);` pub type QwenTokenCb = unsafe extern "C" fn(piece: *const c_char, userdata: *mut c_void); -unsafe extern "C" { +// 用经典 `extern "C"` block 而非 `unsafe extern "C"` block — 后者需 Rust +// 1.82+;CLAUDE.md 声明 rust-version = "1.77",避免给二次贡献者制造毛刺。 +extern "C" { pub fn qwen_load(model_dir: *const c_char) -> *mut QwenCtx; pub fn qwen_free(ctx: *mut QwenCtx);