Skip to content

Commit f232413

Browse files
committed
feat (core): 支持停止运行代码
1 parent 2249220 commit f232413

3 files changed

Lines changed: 190 additions & 42 deletions

File tree

src-tauri/src/main.rs

Lines changed: 136 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ use crate::utils::logger::{
1414
clear_logs, get_log_directory, get_log_files, reset_log_directory, set_log_directory,
1515
};
1616
use config::{get_app_config, get_config_path, init_config, update_app_config};
17+
use std::collections::HashMap;
1718

18-
use log::{debug, error, info};
19+
use log::{debug, error, info, warn};
1920
use plugins::{CodeExecutionRequest, ExecutionResult, LanguageInfo, PluginManager};
2021
use std::fs;
2122
use std::process::{Command, Stdio};
@@ -25,12 +26,62 @@ use tokio::sync::Mutex;
2526
use uuid::Uuid;
2627

2728
use std::io::{BufRead, BufReader};
28-
use std::sync::mpsc;
29+
use std::sync::OnceLock;
30+
use std::sync::{Arc, mpsc};
2931
use std::thread;
3032

33+
// 执行任务结构
34+
#[derive(Debug)]
35+
struct ExecutionTask {
36+
#[allow(dead_code)]
37+
pub language: String,
38+
#[allow(dead_code)]
39+
pub process_id: u32,
40+
pub stop_flag: Arc<tokio::sync::Mutex<bool>>,
41+
}
42+
3143
type ExecutionHistory = Mutex<Vec<ExecutionResult>>;
3244
type PluginManagerState = Mutex<PluginManager>;
3345

46+
// 全局任务管理器
47+
type TaskManager = Arc<tokio::sync::Mutex<HashMap<String, ExecutionTask>>>;
48+
static TASK_MANAGER: OnceLock<TaskManager> = OnceLock::new();
49+
50+
// 初始化任务管理器
51+
fn init_task_manager() -> TaskManager {
52+
TASK_MANAGER
53+
.get_or_init(|| Arc::new(tokio::sync::Mutex::new(HashMap::new())))
54+
.clone()
55+
}
56+
57+
// 停止执行命令
58+
#[tauri::command]
59+
async fn stop_execution(language: String) -> Result<bool, String> {
60+
let task_manager = init_task_manager();
61+
let mut guard = task_manager.lock().await;
62+
63+
if let Some(task) = guard.remove(&language) {
64+
// 设置停止标志
65+
{
66+
let mut stop_flag = task.stop_flag.lock().await;
67+
*stop_flag = true;
68+
}
69+
info!("停止执行 -> 成功设置停止标志给语言 [ {} ]", language);
70+
Ok(true)
71+
} else {
72+
warn!("停止执行 -> 语言 [ {} ] 没有正在运行的任务", language);
73+
Ok(false)
74+
}
75+
}
76+
77+
// 检查是否有正在运行的任务
78+
#[tauri::command]
79+
async fn is_execution_running(language: String) -> Result<bool, String> {
80+
let task_manager = init_task_manager();
81+
let guard = task_manager.lock().await;
82+
Ok(guard.contains_key(&language))
83+
}
84+
3485
// 通用的代码执行函数
3586
#[tauri::command]
3687
async fn execute_code(
@@ -40,6 +91,10 @@ async fn execute_code(
4091
app: AppHandle,
4192
) -> Result<ExecutionResult, String> {
4293
info!("执行代码 -> 调用插件 [ {} ] 开始", request.language);
94+
95+
// 先停止之前可能正在运行的任务
96+
let _ = stop_execution(request.language.clone()).await;
97+
4398
let manager = plugin_manager.lock().await;
4499
let plugin = manager
45100
.get_plugin(&request.language)
@@ -100,10 +155,7 @@ async fn execute_code(
100155
.unwrap()
101156
.as_secs();
102157

103-
// 清理临时文件
104158
let _ = fs::remove_file(&file_path);
105-
106-
// 发送执行完成事件
107159
let _ = app.emit(
108160
"code-execution-complete",
109161
serde_json::json!({
@@ -127,66 +179,110 @@ async fn execute_code(
127179
}
128180
};
129181

182+
// 创建停止标志
183+
let stop_flag = Arc::new(tokio::sync::Mutex::new(false));
184+
185+
// 将任务添加到管理器
186+
let task_manager = init_task_manager();
187+
{
188+
let mut guard = task_manager.lock().await;
189+
guard.insert(
190+
request.language.clone(),
191+
ExecutionTask {
192+
language: request.language.clone(),
193+
process_id: child.id(),
194+
stop_flag: stop_flag.clone(),
195+
},
196+
);
197+
}
198+
130199
let stdout = child.stdout.take().unwrap();
131200
let stderr = child.stderr.take().unwrap();
132201

133-
let (stdout_tx, stdout_rx) = mpsc::channel();
134-
let (stderr_tx, stderr_rx) = mpsc::channel();
202+
let (stdout_tx, stdout_rx) = mpsc::channel::<String>();
203+
let (stderr_tx, stderr_rx) = mpsc::channel::<String>();
135204

136205
// 读取 stdout
137206
thread::spawn(move || {
138207
let reader = BufReader::new(stdout);
139-
for line in reader.lines() {
140-
if let Ok(line) = line {
141-
if stdout_tx.send(line).is_err() {
142-
break;
143-
}
208+
for line in reader.lines().map_while(Result::ok) {
209+
if stdout_tx.send(line).is_err() {
210+
break;
144211
}
145212
}
146213
});
147214

148215
// 读取 stderr
149216
thread::spawn(move || {
150217
let reader = BufReader::new(stderr);
151-
for line in reader.lines() {
152-
if let Ok(line) = line {
153-
if stderr_tx.send(line).is_err() {
154-
break;
155-
}
218+
for line in reader.lines().map_while(Result::ok) {
219+
if stderr_tx.send(line).is_err() {
220+
break;
156221
}
157222
}
158223
});
159224

160225
let mut stdout_lines = Vec::new();
161226
let mut stderr_lines = Vec::new();
227+
let timeout = std::time::Duration::from_secs(30);
162228

163-
// 设置超时时间
164-
let timeout = std::time::Duration::from_secs(3000);
165-
229+
// 主执行循环
166230
loop {
231+
// 检查停止标志
232+
{
233+
let stop_guard = stop_flag.lock().await;
234+
if *stop_guard {
235+
info!(
236+
"执行代码 -> 收到停止信号,终止语言 [ {} ] 的执行",
237+
request.language
238+
);
239+
let _ = child.kill();
240+
let _ = child.wait();
241+
let _ = fs::remove_file(&file_path);
242+
243+
// 从任务管理器中移除
244+
{
245+
let mut guard = task_manager.lock().await;
246+
guard.remove(&request.language);
247+
}
248+
249+
let _ = app.emit(
250+
"code-execution-stopped",
251+
serde_json::json!({
252+
"language": request.language
253+
}),
254+
);
255+
256+
return Err("代码执行被用户停止".to_string());
257+
}
258+
}
259+
167260
// 检查超时
168261
if start_time.elapsed() > timeout {
169262
let _ = child.kill();
170-
let _ = child.wait(); // 等待进程清理
171-
172-
// 清理临时文件
263+
let _ = child.wait();
173264
let _ = fs::remove_file(&file_path);
174265

175-
// 发送超时事件
266+
// 从任务管理器中移除
267+
{
268+
let mut guard = task_manager.lock().await;
269+
guard.remove(&request.language);
270+
}
271+
176272
let _ = app.emit(
177273
"code-execution-timeout",
178274
serde_json::json!({
179275
"language": request.language
180276
}),
181277
);
182278

279+
error!("执行代码 -> 超时,终止语言 [ {} ] 的执行", request.language);
183280
return Err("代码执行超时(30秒)".to_string());
184281
}
185282

186283
// 读取并发送 stdout
187284
while let Ok(line) = stdout_rx.try_recv() {
188285
stdout_lines.push(line.clone());
189-
// 发送实时输出事件
190286
let _ = app.emit(
191287
"code-output",
192288
serde_json::json!({
@@ -200,7 +296,6 @@ async fn execute_code(
200296
// 读取并发送 stderr
201297
while let Ok(line) = stderr_rx.try_recv() {
202298
stderr_lines.push(line.clone());
203-
// 发送实时错误事件
204299
let _ = app.emit(
205300
"code-output",
206301
serde_json::json!({
@@ -244,9 +339,14 @@ async fn execute_code(
244339
.unwrap()
245340
.as_secs();
246341

247-
// 清理临时文件
248342
let _ = fs::remove_file(&file_path);
249343

344+
// 从任务管理器中移除
345+
{
346+
let mut guard = task_manager.lock().await;
347+
guard.remove(&request.language);
348+
}
349+
250350
let mut result = ExecutionResult {
251351
success: status.success(),
252352
stdout: stdout_lines.join("\n"),
@@ -256,10 +356,8 @@ async fn execute_code(
256356
language: request.language.clone(),
257357
};
258358

259-
// 后处理
260359
let _ = plugin.post_execute_hook(&mut result);
261360

262-
// 发送执行完成事件
263361
let _ = app.emit(
264362
"code-execution-complete",
265363
serde_json::json!({
@@ -269,12 +367,10 @@ async fn execute_code(
269367
}),
270368
);
271369

272-
// 添加到执行历史
273-
drop(manager); // 释放插件管理器锁
370+
drop(manager);
274371
let mut history_guard = history.lock().await;
275372
history_guard.push(result.clone());
276373

277-
// 保持历史记录不超过100条
278374
if history_guard.len() > 100 {
279375
history_guard.remove(0);
280376
}
@@ -283,17 +379,19 @@ async fn execute_code(
283379
return Ok(result);
284380
}
285381
Ok(None) => {
286-
// 进程仍在运行,短暂休眠
287382
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
288383
}
289384
Err(e) => {
290385
let _ = child.kill();
291386
let _ = child.wait();
292-
293-
// 清理临时文件
294387
let _ = fs::remove_file(&file_path);
295388

296-
// 发送执行错误事件
389+
// 从任务管理器中移除
390+
{
391+
let mut guard = task_manager.lock().await;
392+
guard.remove(&request.language);
393+
}
394+
297395
let _ = app.emit(
298396
"code-execution-error",
299397
serde_json::json!({
@@ -429,6 +527,8 @@ fn main() {
429527
get_supported_languages,
430528
get_execution_history,
431529
clear_execution_history,
530+
stop_execution,
531+
is_execution_running,
432532
get_app_info,
433533
// 日志相关命令
434534
get_log_directory,

src/App.vue

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
:supported-languages="supportedLanguages"
66
:current-language="currentLanguage"
77
@run-code="runCode"
8+
@stop-code="stopCode"
89
@clear-output="clearOutput"
910
@language-change="handleLanguageChange"
1011
@show-settings="showSettings = true">
@@ -178,6 +179,7 @@ let unlistenSettingsFn: UnlistenFn | null = null
178179
let unlistenOutputFn: UnlistenFn | null = null
179180
let unlistenExecutionStartFn: UnlistenFn | null = null
180181
let unlistenExecutionCompleteFn: UnlistenFn | null = null
182+
let unlistenExecutionStoppedFn: UnlistenFn | null = null
181183
let unlistenExecutionTimeoutFn: UnlistenFn | null = null
182184
let unlistenExecutionErrorFn: UnlistenFn | null = null
183185
@@ -303,6 +305,29 @@ const runCode = async () => {
303305
}
304306
}
305307
308+
const stopCode = async () => {
309+
if (!isRunning.value) {
310+
return
311+
}
312+
313+
try {
314+
const result = await invoke<boolean>('stop_execution', {
315+
language: currentLanguage.value
316+
})
317+
318+
if (result) {
319+
toast.info('正在停止代码执行...')
320+
}
321+
else {
322+
toast.warning('没有找到正在运行的任务')
323+
}
324+
}
325+
catch (error) {
326+
console.error('Error stopping execution:', error)
327+
toast.error('停止执行失败')
328+
}
329+
}
330+
306331
const clearOutput = () => {
307332
output.value = ''
308333
realTimeOutput.value = ''
@@ -361,6 +386,16 @@ const handleExecutionComplete = (event: any) => {
361386
}
362387
}
363388
389+
const handleExecutionStopped = (event: any) => {
390+
const data = event.payload
391+
if (data.language === currentLanguage.value) {
392+
isRunning.value = false
393+
output.value += '\n\n🛑 代码执行已被用户停止'
394+
toast.warning('代码执行已停止')
395+
console.log('代码执行已停止')
396+
}
397+
}
398+
364399
const handleExecutionTimeout = (event: any) => {
365400
const data = event.payload
366401
if (data.language === currentLanguage.value) {
@@ -410,6 +445,7 @@ onMounted(async () => {
410445
// 监听执行状态事件
411446
unlistenExecutionStartFn = await listen('code-execution-start', handleExecutionStart)
412447
unlistenExecutionCompleteFn = await listen('code-execution-complete', handleExecutionComplete)
448+
unlistenExecutionStoppedFn = await listen('code-execution-stopped', handleExecutionStopped)
413449
unlistenExecutionTimeoutFn = await listen('code-execution-timeout', handleExecutionTimeout)
414450
unlistenExecutionErrorFn = await listen('code-execution-error', handleExecutionError)
415451
})
@@ -422,6 +458,7 @@ onUnmounted(() => {
422458
unlistenOutputFn,
423459
unlistenExecutionStartFn,
424460
unlistenExecutionCompleteFn,
461+
unlistenExecutionStoppedFn,
425462
unlistenExecutionTimeoutFn,
426463
unlistenExecutionErrorFn
427464
]

0 commit comments

Comments
 (0)