perf(terminal): optimistic close — instant pane removal, busy check off-thread#145
Merged
Conversation
…ndo off-thread
Closing a terminal had a few-hundred-ms lag on macOS. The grace-period
soft-close busy check ran synchronously on the GPUI thread *before* the pane
was removed from the layout, and on macOS that check forks subprocesses:
`get_foreground_shell_pid` spawns `tmux list-panes` / `lsof` (dtach) and
`has_child_processes` spawns `pgrep -P`. On Linux these are sub-ms `/proc`
reads, so the lag was macOS-only.
Make every interactive close optimistic: eject the pane from the layout
immediately (PTY stays alive), then run the busy probe off the GPUI thread via
`smol::unblock`. Once it returns, idle terminals are killed right away and busy
ones get the undo toast + grace timer — same end state as before, but the UI
updates instantly regardless of session backend.
- okena-workspace: add `Workspace::decide_pending_close` returning
`PendingDecision` {Raced, Finalized, KeepForUndo} (pure, unit-tested) so the
kill-vs-undo decision is testable and the UI layer only handles the toast.
- okena-app: `soft_close::try_begin` → `begin` (optimistic); extract
`build_soft_close_toast`. Reserve a stable toast id up front so the toast can
be posted later only if the terminal turns out busy.
- okena-state: add `Toast::with_id`.
Race handling (shell exits during the probe / grace window) is unchanged — the
existing exit-path `cancel_pending_close` / `reap_restored_close` cover it, and
`decide_pending_close` no-ops when the pending record is already gone.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_019JZRfqVneyJnKjdrtSp3ro
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Closing a terminal had a perceptible few-hundred-ms lag on macOS (not reproducible on Linux).
The grace-period soft-close busy check ran synchronously on the GPUI thread, before the pane was removed from the layout. On macOS that check forks subprocesses:
get_foreground_shell_pid→ spawnstmux list-panes(tmux backend) orlsof×2 (dtach backend)has_child_processes→ spawnspgrep -POn Linux these are sub-millisecond
/procreads, so the lag was macOS-only. Eachfork+execon macOS is expensive (dyld, AMFI/codesign), and they sat directly on the close keypath.Fix
Make every interactive close optimistic:
smol::unblock.The end state is identical to before — only the ordering changed — and the UI now updates instantly regardless of session backend (tmux/dtach/plain). Applies to both single (
CloseTerminal) and multi (CloseTerminals) close.Changes
Workspace::decide_pending_close→PendingDecision { Raced, Finalized, KeepForUndo }. Pure decision logic, unit-tested; the UI layer only handles the toast.soft_close::try_begin→begin(optimistic); extractedbuild_soft_close_toast. A stable toast id is reserved up front so the toast is posted later only if the terminal turns out busy.Toast::with_id.Races
Unchanged. If the shell exits during the probe or grace window, the existing exit-path
cancel_pending_close/reap_restored_closehandle it, anddecide_pending_closeno-ops when the pending record is already gone (Raced).Tests
decide_keeps_busy_terminal_for_undo,decide_finalizes_idle_terminal,decide_is_noop_when_pending_already_gone(okena-workspace).🤖 Generated with Claude Code