Fix orphaned modal sheet deadlock when parent window is destroyed - #699
Fix orphaned modal sheet deadlock when parent window is destroyed#699Ranxi wants to merge 1 commit into
Conversation
-[NSAlert(iTerm) runSheetModalForWindow:] uses beginSheetModalForWindow: with a synchronous runModalForWindow:. If the parent window is destroyed before the user dismisses the sheet, the completion handler never fires so stopModalWithCode: is never called, leaving the main thread permanently stuck in the modal run loop. This can happen when a temporary window (e.g. Touch Bar popup) is closed while a confirm-close sheet is showing: the window auto- dismisses, the sheet is destroyed with it, but the modal loop continues running. The entire app becomes unresponsive — menu bar, mouse input, keyboard, new windows all blocked. The fix adds an NSWindowWillCloseNotification observer that calls [NSApp abortModal] if the parent window closes before the sheet is dismissed. A stopped flag ensures the observer and completion handler do not race. Added a deterministic test that closes the window from a background thread while the modal is running, verifying the call returns instead of blocking.
|
Thank you, this is a real fix for a nasty deadlock. I verified it end to end: reproduced the orphaned-modal hang (dead menu bar, Force-Quit only) with a timer-driven window close during a sheet, and confirmed your observer approach cleanly aborts the modal instead of wedging. Merged (squashed) as 1b0ab6d, with you as the author. A couple of adjustments I made while reviewing:
One follow-up worth noting: the same orphaned-modal deadlock exists in several other hand-rolled beginSheet: + runModalForWindow: sites that do not go through this method (coprocess panel, paste-special, session parameter panel, password manager, Add Account panel, split panel). Those are actually more dangerous, since a custom sheet has no always-clickable default button to recover with. I plan to generalize this guard into a shared helper those call sites can use. Thanks again for the fix and the clear write-up. |
|
您好,您发的邮件已收到,谢谢!
|
|
Follow-up: I landed the generalization mentioned above as 9158a7c. It factors this parent-window-close guard out of the alert category into a shared iTermRunModalForWindowAbortingIfParentCloses() and routes the five other hand-rolled beginSheet: + runModalForWindow: sheets through it (paste-special, coprocess, session parameter, split, and add-account panels), so the same deadlock is closed across all of them, not just alerts. While generalizing I also hardened the guard beyond the original alert version:
|
Environment
Steps to Reproduce
Actual Behavior
The entire iTerm2 application becomes unresponsive:
The app process does not crash — it runs indefinitely with the main thread stuck in a nested
runModalForWindow:event loop. Only force-quitting via Cmd+Option+Esc recovers, losing all session state including remote tmux/SSH connections.Root Cause
-[NSAlert(iTerm) runSheetModalForWindow:]usesbeginSheetModalForWindow:completionHandler:paired with a synchronousrunModalForWindow:. When the parent window is destroyed (the Touch Bar temporary window auto-dismisses), the sheet is torn down with it, but the completion handler never fires — sostopModalWithCode:is never called. The main thread remains permanently blocked in the modal run loop.Fix
Added an
NSWindowWillCloseNotificationobserver inrunSheetModalForWindow:. If the parent window closes before the user dismisses the sheet, the observer calls[NSApp abortModal], which forces the modal loop to unwind. A__block BOOL stoppedflag prevents the observer and the normal completion handler from racing.Changes in
sources/Categories/NSAlert+iTerm.monly (+14 lines).Test Plan
NSAlert_iTermTests/test_modalReturnsWhenParentWindowClosescloses the window from a background thread while the modal is running, verifying the call returns instead of blockingrunSheetModalForWindow:benefit from this fix (single method-level change)