Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix a crash when closing panes #17333

Merged
merged 2 commits into from
May 30, 2024
Merged

Conversation

lhecker
Copy link
Member

@lhecker lhecker commented May 29, 2024

Calling Close() from within WalkPanes is not safe. Simply using _FindPane is enough to fix this.

This PR also fixes another potential source of infinite recursion, and fixes panes being passed by-value into the callbacks.

Closes #17305

Validation Steps Performed

  • Split panes vertically 3 times
  • exit the middle, the bottom and final one, in that order
  • Doesn't crash ✅

// copy `weakThis` onto the stack, because that's the only thing that gets captured in coroutines.
// See: https://devblogs.microsoft.com/oldnewthing/20211103-00/?p=105870
const auto weakThisCopy = weakThis;
co_await wil::resume_foreground(dispatcher);
Copy link
Member

Choose a reason for hiding this comment

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

is it no longer necessary to switch to the UI thread?

Copy link
Member Author

Choose a reason for hiding this comment

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

Never was! TerminalPaneContent raises CloseRequested in its Close method where it also performs UI cleanup, so it can't possibly be on a background thread.

BTW since a tab owns the pane which owns the content, it's a little weird that Close() on the content goes into the tab and then back down into Close() in the pane, which then doesn't call Close() on the content. Seems like it should be the other way around. 🤔

@zadjii-msft zadjii-msft added this pull request to the merge queue May 30, 2024
Merged via the queue into main with commit baba406 May 30, 2024
20 checks passed
@zadjii-msft zadjii-msft deleted the dev/lhecker/17305-close-crash branch May 30, 2024 15:38
@lhecker
Copy link
Member Author

lhecker commented May 31, 2024

I think I introduced a bug in this PR. Closing 1 tab now closes 2.
To work around this, simply use exit instead of clicking on the "x" button. Ctrl+Shif+W also works.

github-merge-queue bot pushed a commit that referenced this pull request Jun 4, 2024
#17333 introduced a regression: While it fixes a recursion *into*
`Pane::Close()` that doesn't fix the recursion outside of it.
In this case, `Close()` raises the `Closed` event which results
in another tab being closed because it's bound to `_RemoveTab`.
The recursion is now obvious, because I made the entire process
synchronous. Previously, it would (hopefully) just be scheduled
after the pane and its content are already gone.

The issue can be fixed by moving the recursion check from
`Pane::Close()` to `TerminalTab::Shutdown()` but I felt like
it would better to fix the issue a bit more thoroughly.

`IPaneContent` can raise a `CloseRequested` event to indicate it wants
to be closed. However, that also contained recursion, because the
content would call its own `Close()` to raise the event, which the
tab catches, calls `Close()` on the `Pane` which calls `Close()` on
the content which raises the event again and so on. That's what was
fixed in #17333 among others. We can do this better by not raising
the event from `IPaneContent::Close()`. Instead, that method will now
be exclusively called by `Pane`. The `CloseRequested` event will now
truly be just a request and nothing more. Furthermore, the ownership
of the event handling was moved from the `TerminalTab` to the `Pane`.

To make all of this a bit simpler and more robust, two new methods
were added to `Pane`: `_takePaneContent` and `_setPaneContent`.
These methods ensure that `Close()` is called on the content,
that the event handlers are always added and revoked
and that the ownership transfers cleanly between panes.

## Validation Steps Performed
* Open 3 tabs, close the middle one ✅
* Open 3 vertical panes, close the middle one ✅
* Drag tabs with multiple panes between windows ✅
DHowett pushed a commit that referenced this pull request Jun 7, 2024
Calling Close() from within WalkPanes is not safe. Simply using
_FindPane is enough to fix this.

This PR also fixes another potential source of infinite recursion, and
fixes panes being passed by-value into the callbacks.

Closes #17305

## Validation Steps Performed
* Split panes vertically 3 times
* `exit` the middle, the bottom and final one, in that order
* Doesn't crash ✅

(cherry picked from commit baba406)
Service-Card-Id: 92643006
Service-Version: 1.21
DHowett pushed a commit that referenced this pull request Jun 7, 2024
#17333 introduced a regression: While it fixes a recursion *into*
`Pane::Close()` that doesn't fix the recursion outside of it.
In this case, `Close()` raises the `Closed` event which results
in another tab being closed because it's bound to `_RemoveTab`.
The recursion is now obvious, because I made the entire process
synchronous. Previously, it would (hopefully) just be scheduled
after the pane and its content are already gone.

The issue can be fixed by moving the recursion check from
`Pane::Close()` to `TerminalTab::Shutdown()` but I felt like
it would better to fix the issue a bit more thoroughly.

`IPaneContent` can raise a `CloseRequested` event to indicate it wants
to be closed. However, that also contained recursion, because the
content would call its own `Close()` to raise the event, which the
tab catches, calls `Close()` on the `Pane` which calls `Close()` on
the content which raises the event again and so on. That's what was
fixed in #17333 among others. We can do this better by not raising
the event from `IPaneContent::Close()`. Instead, that method will now
be exclusively called by `Pane`. The `CloseRequested` event will now
truly be just a request and nothing more. Furthermore, the ownership
of the event handling was moved from the `TerminalTab` to the `Pane`.

To make all of this a bit simpler and more robust, two new methods
were added to `Pane`: `_takePaneContent` and `_setPaneContent`.
These methods ensure that `Close()` is called on the content,
that the event handlers are always added and revoked
and that the ownership transfers cleanly between panes.

## Validation Steps Performed
* Open 3 tabs, close the middle one ✅
* Open 3 vertical panes, close the middle one ✅
* Drag tabs with multiple panes between windows ✅

(cherry picked from commit ce0f8d6)
Service-Card-Id: 92678790
Service-Version: 1.21
@lhecker lhecker added Issue-Bug It either shouldn't be doing this or needs an investigation. Severity-Crash Crashes are real bad news. labels Nov 1, 2024
@microsoft-github-policy-service microsoft-github-policy-service bot added Area-Quality Stability, Performance, Etc. Priority-0 Bugs that we consider release-blocking/recall-class (P0) Product-Terminal The new Windows Terminal. labels Nov 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Quality Stability, Performance, Etc. Issue-Bug It either shouldn't be doing this or needs an investigation. Priority-0 Bugs that we consider release-blocking/recall-class (P0) Product-Terminal The new Windows Terminal. Severity-Crash Crashes are real bad news.
Projects
Status: Rejected
Development

Successfully merging this pull request may close these issues.

Terminal crashes when closing any other pane than the last
4 participants