Skip to content

fix: 修复退出软件时的一些问题#334

Merged
J3n5en merged 2 commits intoJ3n5en:mainfrom
RebornQ:fix/exit-anr
Mar 5, 2026
Merged

fix: 修复退出软件时的一些问题#334
J3n5en merged 2 commits intoJ3n5en:mainfrom
RebornQ:fix/exit-anr

Conversation

@RebornQ
Copy link
Contributor

@RebornQ RebornQ commented Mar 2, 2026

  1. 修复退出软件时ANR
  2. 关闭请求只由自定义退出确认弹窗裁决;取消/关闭弹窗会立刻通知主进程“取消关闭”,主进程不再弹 fallback 原生退出确认窗。

@github-actions
Copy link
Contributor

github-actions bot commented Mar 2, 2026

Claude finished @RebornQ's task in 3m 38s —— View job


🔍 代码审查报告

👋 欢迎查看审查结果! 本次审查已完成,以下是详细分析和建议。
💡 使用提示:点击 📁 文件链接可直接跳转到代码位置,建议按优先级处理问题。

📊 审查概览 · 一目了然

📋 审查项目 🔢 发现数量 📈 占比 🎯 处理状态
📁 审查文件 10 100% 已完成
🚨 严重问题 0 0%
⚠️ 中等问题 2 50% 🟡 本版本修复
轻微问题 2 50% 🔵 后续优化

🏆 代码质量评分

📊 综合评分: 85/100  🌟🌟🌟🌟⭐
📋 本次等级: 良好

📋 问题分布

🚨 严重: ░░░░░░░░░░ 0%
⚠️ 中等: ████░░░░░░ 40%
⚡ 轻微: ████░░░░░░ 40%
✅ 优秀: █████████░ 90%
总体评分: ⭐⭐⭐⭐☆ (4/5)

🎯 关键发现

🏷️ 类别 🔍 发现数量 🎯 优先级 📈 趋势
🔒 安全性 0 个 ✅ 无 ✅ 安全
⚡ 性能 0 个 ✅ 无 ✅ 改善
🎨 代码质量 2 个 🟡 中 📊 平稳
🛠️ 健壮性 2 个 🟡 中 📊 平稳

📝 变更摘要

本 PR 针对退出软件时的 ANR(Application Not Responding)问题进行了系统性修复,主要涵盖以下几个方面:

  1. 关闭流程重构MainWindow.ts):引入 closeFlowInProgress 防重入锁;将 waitFor 拆分为 waitForNoTimeout(等待渲染端退出确认)和 waitForWithTimeout(等待保存操作);渲染端的退出确认响应新增 confirmed 字段,取消/关闭弹窗可直接通知主进程中止关闭流程。
  2. PTY 生命周期管理shell.tsPtyManager.ts):新增 LiveExecPty 追踪系统,确保 execInPty 创建的 PTY 进程在退出时可被可靠清理;PtySession 新增 dataDisposable/exitDisposable 存储,确保原生线程资源及时释放。
  3. 进程终止改进processUtils.ts):killProcessTreekillProcessTreeAsync 现优先使用 target.kill() 而非 process.kill(pid),使 node-pty 可运行自身清理逻辑。
  4. 退出清理管线ipc/index.tsindex.ts):新增 cleanupExecInPtys 为清理流程第一步;will-quit 事件加入 isQuittingCleanupRunning 防重入锁和 8 秒强制退出保底定时器;将 app.quit() 替换为 app.exit(0) 避免 will-quit 重入。
  5. 渲染端配合App.tsxuseAppLifecycle.tspreload/index.ts):新增 cancelCloseAndRespond 回调,弹窗取消/关闭时立刻通知主进程中止关闭流程。

整体架构设计合理,修复方案全面覆盖了 PTY 资源泄漏、关闭流程重入、退出清理超时等多个痛点。代码质量良好,注释清晰。


🎯 问题详细分析 · 📊 点击展开查看

📊 **👆 点击此处展开详细统计和图表分析**

📈 问题趋势分析

本次审查: 🟡🟡🔵🔵 (发现 4 个问题)
代码质量: █████████░ 85%
安全评分: ██████████ 100% ✅
性能评分: █████████░ 90%  ✅
🏆 质量指标 📊 当前分数 🎯 目标分数 📈 改进方向
代码质量 85/100 90+ ⬆️ 小幅改进
安全等级 100/100 100 ✅ 优秀
性能优化 90/100 90+ ✅ 达标

🐛 问题清单与修复指南 · 📋 点击展开查看

📋 **👆 点击此处展开详细问题列表 (共 4 个问题) · 一键复制追踪 📌**

🚀 快速开始


⚠️ 中等级别 (Major) 建议修复

🟡 #1 src/main/windows/MainWindow.ts:173-186

🏷️ 类型: 健壮性缺陷 | ⏰ 优先级: P1 - 本版本修复 | 🎯 影响范围: 窗口关闭流程

  • 🔍 问题描述waitForNoTimeout 没有任何超时机制。如果渲染进程在收到 APP_CLOSE_REQUEST 后崩溃或卡死,未能发送 APP_CLOSE_RESPONSE,则该 Promise 永远不会 resolve。此时 closeFlowInProgress 保持 true,用户无法再通过关闭按钮退出应用。

    虽然通过 Cmd+Q / Alt+F4 触发的 will-quit 事件有 8 秒强制退出保底,但「点击窗口关闭按钮」这条路径在渲染端挂起时将完全失效。

    // src/main/windows/MainWindow.ts:173-186
    const waitForNoTimeout = <T>(
      channel: string,
      predicate: (event: Electron.IpcMainEvent, ...args: any[]) => T | null
    ) =>
      new Promise<T>((resolve) => {
        const handler = (event: Electron.IpcMainEvent, ...args: any[]) => {
          const match = predicate(event, ...args);
          if (match === null) return;
          ipcMain.removeListener(channel, handler);
          resolve(match);
        };
    
        ipcMain.on(channel, handler);
      });
  • 🛠️ 解决方案:建议为 waitForNoTimeout 增加一个较长的超时(例如 30-60 秒),超时后自动视为 confirmed: false 并重置 closeFlowInProgress。或者监听 webContentsdestroyed / crashed 事件来自动清理:

    const waitForNoTimeout = <T>(channel: string, predicate: ...) =>
      new Promise<T>((resolve, reject) => {
        const handler = (event, ...args) => { /* ... */ };
        ipcMain.on(channel, handler);
        
        // Safety: if webContents crashes or is destroyed, reject
        const onCrash = () => {
          ipcMain.removeListener(channel, handler);
          reject(new Error('webContents crashed'));
        };
        win.webContents.once('destroyed', onCrash);
        win.webContents.once('render-process-gone', onCrash);
      });
  • ⚠️ 潜在影响:在极端情况下(渲染进程崩溃),用户无法通过窗口关闭按钮退出应用

Fix this →


🟡 #2 src/renderer/App.tsx:1225-1228 & 1240-1241

🏷️ 类型: 代码质量 | ⏰ 优先级: P1 - 本版本修复 | 🎯 影响范围: 退出确认弹窗

  • 🔍 问题描述:点击「取消」按钮时,cancelCloseAndRespond() 会被调用两次:一次在 onClick 中直接调用,一次在 setCloseDialogOpen(false) 触发 onOpenChange(false) 时调用。虽然第二次调用由于 requestId 已被清除而成为空操作不会产生 bug,但这种重复调用降低了代码可读性,且未来维护者可能不理解为何需要两处调用。

    // src/renderer/App.tsx:1225-1228
    onOpenChange={(open) => {
      setCloseDialogOpen(open);
      if (!open) {
        cancelCloseAndRespond(); // 调用 1
      }
    }}
    // src/renderer/App.tsx:1238-1241
    onClick={() => {
      setCloseDialogOpen(false); // 触发 onOpenChange → 调用 1
      cancelCloseAndRespond();   // 调用 2(重复)
    }}
  • 🛠️ 解决方案:由于 onOpenChange(false) 已经统一处理了取消逻辑,取消按钮的 onClick 只需调用 setCloseDialogOpen(false) 即可,无需重复调用 cancelCloseAndRespond()

    onClick={() => {
      setCloseDialogOpen(false);
      // cancelCloseAndRespond() 已在 onOpenChange 中统一处理
    }}
  • ⚠️ 潜在影响:不影响功能,仅影响代码可读性和维护性

Fix this →


⚡ 轻微级别 (Minor) 优化建议

🔵 #3 src/main/index.ts:60

🏷️ 类型: 代码规范 | ⏰ 优先级: P2 - 后续版本 | 🎯 影响范围: 局部

  • 🔍 问题描述isQuittingCleanupRunning 变量名略长且不够直观。在 will-quit 事件处理中,这个标志位的作用是防止 cleanupAllResources 被重复调用。名称 isQuittingCleanupRunning 含义准确但稍显冗余。

  • 🛠️ 解决方案:可考虑简化为 isCleaningUpcleanupStarted,不过这纯属风格偏好,当前命名也可以接受。


🔵 #4 .codex/environments/environment.toml

🏷️ 类型: PR 范围 | ⏰ 优先级: P2 - 后续版本 | 🎯 影响范围: 构建配置

  • 🔍 问题描述.codex/environments/environment.toml 是新增的 Codex 环境配置文件,与本 PR 的退出 ANR 修复主题无关。建议将此文件变更拆分到独立的提交或 PR 中,以保持 PR 的聚焦性。

  • 🛠️ 解决方案:如果这是开发辅助工具配置,可以保留在本 PR 中,但建议在 PR 描述中说明此项变更。


📊 修复优先级矩阵

🎯 优先级 🚨 严重 ⚠️ 中等 ⚡ 轻微 📈 总计
🔴 P0 0 个 - - 0 个
🟡 P1 - 2 个 - 2 个
🔵 P2 - - 2 个 2 个
📊 合计 0 2 2 4 个

✨ 亮点

本次 PR 有几处设计值得肯定:

  1. closeFlowInProgress 防重入锁 + .finally() 重置 — 简洁有效地防止了连续点击关闭按钮导致的重复流程问题
  2. LiveExecPty 追踪机制 — 通过追踪 execInPty 创建的 PTY 进程并提供 cleanupExecInPtys / cleanupExecInPtysSync,从根源上解决了 macOS 上 node-pty 原生插件清理死锁问题
  3. target.kill() 优先策略 — 让 node-pty 等库能运行自身清理逻辑,体现了良好的封装意识
  4. app.exit(0) 替代 app.removeAllListeners('will-quit') + app.quit() — 避免了 will-quit 事件重入的潜在问题,更加安全可靠
  5. 8 秒强制退出定时器 — 作为最后的安全网,即使所有清理步骤超时也能保证进程最终退出

🎯 合并决策建议

📋 合并评估结果

📊 评估维度 ⭐ 得分 🎯 状态 💭 说明
🔐 安全性 10/10 ✅ 通过 无新增安全风险
📊 代码质量 8/10 ✅ 良好 结构清晰,少量冗余代码
⚡ 性能影响 9/10 ✅ 改善 资源释放更及时,退出速度提升
🛠️ 功能完整性 8/10 ⚠️ 轻微 waitForNoTimeout 缺少极端情况保护
🧪 测试覆盖 6/10 ⚠️ 基本 无自动化测试,依赖手动验证

📈 综合评分: 41/50 分 · 等级: 良好


🚦 最终建议

⚠️ 条件合并

📝 详细理由

🎯 具体行动建议

  1. 立即可执行的改进

  2. 后续版本改进

    • 考虑添加关闭流程的集成测试或手动测试用例文档
    • .codex/environments/environment.toml 变更拆分到独立 commit

@J3n5en J3n5en merged commit 84a8c14 into J3n5en:main Mar 5, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants