Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src-tauri/src/commands/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -801,7 +801,13 @@ pub struct MaaState {
impl MaaState {
/// 清理所有实例的 agent 子进程
pub fn cleanup_all_agent_children(&self) {
if let Ok(mut instances) = self.instances.lock() {
let lock_start = std::time::Instant::now();
let instances = self.instances.lock();
log::info!(
"[exit] Acquired instances lock for agent cleanup in {}ms",
lock_start.elapsed().as_millis()
);
Comment on lines +806 to +809

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 中等 · Agent 进程在关闭后仍然存活

当另一个线程持有 instances 时发生 panic,cleanup_all_agent_children 会在检查锁结果前记录已获取锁;随后 if let Ok 会因互斥锁已中毒而跳过清理。这样一来,即使 agent 子进程仍在运行,窗口处理程序也可能报告清理已完成。

仅在成功获取锁后记录日志,并报告或处理因锁中毒导致的清理失败,而不是悄悄跳过。

面向 AI agent 的提示
在 `src-tauri/src/commands/types.rs` 第 806-809 行:

**Agent 进程在关闭后仍然存活**

当另一个线程持有 `instances` 时发生 panic,`cleanup_all_agent_children` 会在检查锁结果前记录已获取锁;随后 `if let Ok` 会因互斥锁已中毒而跳过清理。这样一来,即使 agent 子进程仍在运行,窗口处理程序也可能报告清理已完成。

仅在成功获取锁后记录日志,并报告或处理因锁中毒导致的清理失败,而不是悄悄跳过。
Original comment in English

🟡 Medium · Agent processes survive shutdown

When another thread panicked while holding instances, cleanup_all_agent_children logs that it acquired the lock before checking the result, then if let Ok skips cleanup for the poisoned mutex; the window handler can report cleanup finished while agent children remain running.

Log acquisition only after a successful lock, and report or handle the poisoned-lock cleanup failure instead of silently skipping it.

Prompt for AI agents
In `src-tauri/src/commands/types.rs` at lines 806-809:

**Agent processes survive shutdown**

When another thread panicked while holding `instances`, `cleanup_all_agent_children` logs that it acquired the lock before checking the result, then `if let Ok` skips cleanup for the poisoned mutex; the window handler can report cleanup finished while agent children remain running.

Log acquisition only after a successful lock, and report or handle the poisoned-lock cleanup failure instead of silently skipping it.

if let Ok(mut instances) = instances {
for (id, instance) in instances.iter_mut() {
for mut child in instance.agent_children.drain(..) {
log::info!("Killing agent child process for instance: {}", id);
Expand Down
29 changes: 26 additions & 3 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,15 +374,31 @@ pub fn run() {
}
// 窗口关闭请求:检查是否最小化到托盘
tauri::WindowEvent::CloseRequested { api, .. } => {
if tray::handle_close_requested(window.app_handle()) {
let minimize_to_tray = tray::handle_close_requested(window.app_handle());
log::info!(
"[exit] Window '{}' close requested, minimize_to_tray={}",
window.label(),
minimize_to_tray
);
Comment on lines +378 to +382

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 中等 · 日志存储缓慢会延迟关闭

当配置的日志目录或其文件系统在关闭事件期间发生卡顿时,目录日志目标会在事件线程上同步写入。因此,log::info! 会在调用 api.prevent_close() 之前阻塞关闭处理,或使 RunEvent::Exit 清理延迟到日志写入完成;因此,日志文件系统卡顿会导致窗口或应用程序退出受阻。

在关闭回调中使用非阻塞日志路径,或以其他方式确保日志写入卡顿不会阻塞事件处理。

另见 src-tauri/src/lib.rs:389-392、src-tauri/src/lib.rs:423。

面向 AI agent 的提示
在 `src-tauri/src/lib.rs` 第 378-382 行:

**日志存储缓慢会延迟关闭**

当配置的日志目录或其文件系统在关闭事件期间发生卡顿时,目录日志目标会在事件线程上同步写入。因此,`log::info!` 会在调用 `api.prevent_close()` 之前阻塞关闭处理,或使 `RunEvent::Exit` 清理延迟到日志写入完成;因此,日志文件系统卡顿会导致窗口或应用程序退出受阻。

在关闭回调中使用非阻塞日志路径,或以其他方式确保日志写入卡顿不会阻塞事件处理。

另见 `src-tauri/src/lib.rs:389-392`、`src-tauri/src/lib.rs:423`。
Original comment in English

🟡 Medium · Slow log storage delays shutdown

When the configured log folder or its filesystem stalls during a shutdown event, the folder log target writes synchronously on the event thread, so log::info! blocks close handling before api.prevent_close() or delays RunEvent::Exit cleanup until the log write returns; a stalled log filesystem therefore stalls the window or application exit.

Use a nonblocking log path for shutdown callbacks, or otherwise ensure a stalled log write cannot block event handling.

Also at src-tauri/src/lib.rs:389-392, src-tauri/src/lib.rs:423.

Prompt for AI agents
In `src-tauri/src/lib.rs` at lines 378-382:

**Slow log storage delays shutdown**

When the configured log folder or its filesystem stalls during a shutdown event, the folder log target writes synchronously on the event thread, so `log::info!` blocks close handling before `api.prevent_close()` or delays `RunEvent::Exit` cleanup until the log write returns; a stalled log filesystem therefore stalls the window or application exit.

Use a nonblocking log path for shutdown callbacks, or otherwise ensure a stalled log write cannot block event handling.

Also at `src-tauri/src/lib.rs:389-392`, `src-tauri/src/lib.rs:423`.

if minimize_to_tray {
api.prevent_close();
}
}
// 窗口销毁时清理所有 agent 子进程
tauri::WindowEvent::Destroyed => {
log::info!(
"[exit] Window '{}' destroyed, cleaning up agent children",
window.label()
);
let start = std::time::Instant::now();
if let Some(state) = window.try_state::<Arc<MaaState>>() {
state.cleanup_all_agent_children();
}
log::info!(
"[exit] Window '{}' cleanup finished in {}ms",
window.label(),
start.elapsed().as_millis()
);
}
_ => {}
}
Expand All @@ -396,13 +412,15 @@ pub fn run() {
let final_exit_code = requested_exit_code.clone();

let on_event = move |_app_handle: &tauri::AppHandle, event: tauri::RunEvent| {
#[cfg(windows)]
if let tauri::RunEvent::ExitRequested { code, .. } = &event {
log::info!("[exit] Exit requested, code={:?}", code);
#[cfg(windows)]
requested_exit_code.set(*code);
}

// 退出前收尾遥测(结束 Session 与悬挂 Transaction 并 flush)
if matches!(event, tauri::RunEvent::Exit) {
log::info!("[exit] Event loop exiting, running final cleanup");
commands::focus::cancel_all();
#[cfg(target_os = "macos")]
commands::notification::shutdown_macos_notifications();
Expand All @@ -418,7 +436,12 @@ pub fn run() {
// tauri-runtime-wry 2.9.3 的 ControlFlow::Exit 丢失请求的退出码。
// 先让事件循环完成退出回调和框架清理,再向操作系统报告该退出码。
let runtime_exit_code = app.run_return(on_event);
std::process::exit(final_exit_code.get().unwrap_or(runtime_exit_code));
let exit_code = final_exit_code.get().unwrap_or(runtime_exit_code);
log::info!(
"[exit] Event loop returned, exiting process with code {}",
exit_code
);
std::process::exit(exit_code);
}
#[cfg(not(windows))]
app.run(on_event);
Expand Down
1 change: 1 addition & 0 deletions src/services/taskStopService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export async function stopInstanceTasksAndExitApp(instanceId: string): Promise<b
return true;
}

log.info(`[task-stop#${instanceId}] 任务已停止,请求退出应用`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Await the pre-exit log write

In the Tauri exit path this call does not wait for the marker to reach disk: the logger method factory invokes the asynchronous writeLogToFile at src/utils/logger.ts:152-160 without returning its promise, and this function immediately proceeds to exit(0). On a fast exit or concurrent backend command dispatch, the WebView/process can be torn down before the plugin-fs append completes, so the marker intended to prove execution reached the exit request can be absent; use an awaited write/flush or a synchronous backend log command before requesting exit.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 中等 · 退出标记未写入日志

当应用程序退出时,如果异步文件写入仍在等待中,log.info 会启动文件写入但不会等待其完成,而 exit(0) 可能会在写入完成前终止进程,因此退出前的标记不会出现在持久化日志中。

在调用 exit(0) 之前等待日志写入完成。

面向 AI agent 的提示
在 `src/services/taskStopService.ts` 第 98 行:

**退出标记未写入日志**

当应用程序退出时,如果异步文件写入仍在等待中,`log.info` 会启动文件写入但不会等待其完成,而 `exit(0)` 可能会在写入完成前终止进程,因此退出前的标记不会出现在持久化日志中。

在调用 `exit(0)` 之前等待日志写入完成。
Original comment in English

🟡 Medium · Exit marker disappears from logs

When the app exits while the asynchronous file write is pending, log.info starts the file write without awaiting it, and exit(0) can terminate the process before it completes, so the pre-exit marker is absent from persisted logs.

Await completion of the log write before calling exit(0).

Prompt for AI agents
In `src/services/taskStopService.ts` at line 98:

**Exit marker disappears from logs**

When the app exits while the asynchronous file write is pending, `log.info` starts the file write without awaiting it, and `exit(0)` can terminate the process before it completes, so the pre-exit marker is absent from persisted logs.

Await completion of the log write before calling `exit(0)`.

const { exit } = await import('@tauri-apps/plugin-process');
await exit(0);
return true;
Expand Down
Loading