11
This commit is contained in:
@@ -26,6 +26,7 @@ use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
use tauri::{AppHandle, Emitter, Window};
|
||||
use tokio::sync::{mpsc, watch, Mutex};
|
||||
use tokio::time::{timeout, Duration};
|
||||
|
||||
use crate::app_config::AppConfig;
|
||||
use crate::js_runtime::PipelineEvent;
|
||||
@@ -102,6 +103,14 @@ pub(crate) fn spawn_event_pump(
|
||||
})
|
||||
}
|
||||
|
||||
async fn finish_event_pump(mut pump: tokio::task::JoinHandle<()>) {
|
||||
// 正常情况下 sender drop 后 pump 会自行退出;超时则强制结束,避免前端 invoke 卡住。
|
||||
if timeout(Duration::from_millis(300), &mut pump).await.is_err() {
|
||||
pump.abort();
|
||||
let _ = pump.await;
|
||||
}
|
||||
}
|
||||
|
||||
/// 按脚本名跑:去后端拉 `scriptName`,包装 runner,spawn 子进程,回送结果。
|
||||
#[tauri::command]
|
||||
pub async fn run_nodejs_script(
|
||||
@@ -136,7 +145,7 @@ pub async fn run_nodejs_script(
|
||||
|
||||
let result = result?;
|
||||
|
||||
pump.await.ok();
|
||||
finish_event_pump(pump).await;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@@ -175,7 +184,7 @@ pub async fn run_nodejs_script_source(
|
||||
|
||||
let result = result?;
|
||||
|
||||
pump.await.ok();
|
||||
finish_event_pump(pump).await;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
||||
use tokio::process::Command;
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
use tokio::sync::watch;
|
||||
use tokio::time::{timeout, Duration};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::js_runtime::PipelineEvent;
|
||||
@@ -431,7 +432,7 @@ async fn run_script_bundle(
|
||||
// stderr → 解析 __EVT__<json> → PipelineEvent
|
||||
let stderr = child.stderr.take().expect("stderr piped");
|
||||
let events_tx = events.clone();
|
||||
let stderr_task = tokio::spawn(async move {
|
||||
let mut stderr_task = tokio::spawn(async move {
|
||||
let mut reader = BufReader::new(stderr).lines();
|
||||
let mut tail = String::new();
|
||||
while let Ok(Some(line)) = reader.next_line().await {
|
||||
@@ -488,12 +489,11 @@ async fn run_script_bundle(
|
||||
// stdout → 找 __RESULT__ 那一行
|
||||
let stdout = child.stdout.take().expect("stdout piped");
|
||||
let events_tx2 = events.clone();
|
||||
let stdout_task = tokio::spawn(async move {
|
||||
let mut stdout_task = tokio::spawn(async move {
|
||||
let mut reader = BufReader::new(stdout).lines();
|
||||
let mut result: Option<String> = None;
|
||||
while let Ok(Some(line)) = reader.next_line().await {
|
||||
if let Some(rest) = line.strip_prefix("__RESULT__") {
|
||||
result = Some(rest.to_string());
|
||||
return Some(rest.to_string());
|
||||
} else if !line.is_empty() {
|
||||
// 用户 console.log 的内容也透传给前端
|
||||
let _ = events_tx2.send(PipelineEvent::Log {
|
||||
@@ -503,22 +503,48 @@ async fn run_script_bundle(
|
||||
});
|
||||
}
|
||||
}
|
||||
result
|
||||
None
|
||||
});
|
||||
|
||||
// 与取消信号竞争等待;取消时立即杀掉子进程
|
||||
let (status, cancelled) = tokio::select! {
|
||||
status = child.wait() => (status?, false),
|
||||
// 与取消信号、__RESULT__ 竞争等待:
|
||||
// - 收到 __RESULT__ 后不再等待脚本自然退出,主动结束子进程,避免 invoke 卡住。
|
||||
let (status, cancelled, result_line, stdout_done) = tokio::select! {
|
||||
result = &mut stdout_task => {
|
||||
let result_line = result.unwrap_or(None);
|
||||
let _ = child.start_kill();
|
||||
let status = child.wait().await?;
|
||||
(status, false, result_line, true)
|
||||
}
|
||||
status = child.wait() => (status?, false, None, false),
|
||||
_ = wait_for_cancel(cancel_rx) => {
|
||||
let _ = child.start_kill();
|
||||
let status = child.wait().await?;
|
||||
(status, true)
|
||||
(status, true, None, false)
|
||||
}
|
||||
};
|
||||
|
||||
let (stderr_tail, result_line) = tokio::join!(stderr_task, stdout_task);
|
||||
let stderr_tail = stderr_tail.unwrap_or_default();
|
||||
let result_line = result_line.unwrap_or(None);
|
||||
let stderr_tail = match timeout(Duration::from_millis(500), &mut stderr_task).await {
|
||||
Ok(joined) => joined.unwrap_or_default(),
|
||||
Err(_) => {
|
||||
stderr_task.abort();
|
||||
let _ = stderr_task.await;
|
||||
String::new()
|
||||
}
|
||||
};
|
||||
|
||||
let result_line_fallback = if stdout_done {
|
||||
None
|
||||
} else {
|
||||
match timeout(Duration::from_millis(500), &mut stdout_task).await {
|
||||
Ok(joined) => joined.unwrap_or(None),
|
||||
Err(_) => {
|
||||
stdout_task.abort();
|
||||
let _ = stdout_task.await;
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
let result_line = result_line.or(result_line_fallback);
|
||||
|
||||
let _ = tokio::fs::remove_dir_all(&bundle_dir).await;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user