-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
Try to fix crash on close with saving enabled #11440
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -86,7 +86,7 @@ AppHost::AppHost() noexcept : | |
_window->SetAlwaysOnTop(_logic.GetInitialAlwaysOnTop()); | ||
_window->MakeWindow(); | ||
|
||
_windowManager.GetWindowLayoutRequested([this](auto&&, const winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs& args) { | ||
_GetWindowLayoutRequestedToken = _windowManager.GetWindowLayoutRequested([this](auto&&, const winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs& args) { | ||
// The peasants are running on separate threads, so they'll need to | ||
// swap what context they are in to the ui thread to get the actual layout. | ||
args.WindowLayoutJsonAsync(_GetWindowLayoutAsync()); | ||
|
@@ -401,23 +401,42 @@ void AppHost::LastTabClosed(const winrt::Windows::Foundation::IInspectable& /*se | |
_HideNotificationIconRequested(); | ||
} | ||
|
||
// We don't want to try to save layouts if we are about to close | ||
_getWindowLayoutThrottler.reset(); | ||
_windowManager.GetWindowLayoutRequested(_GetWindowLayoutRequestedToken); | ||
Comment on lines
+404
to
+406
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not use a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The issue is that the event handler can get called during the destructor for the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The use of a std::weak_ptr<T> weakSelf{ shared_from_this() };
...
if (const auto self = weakSelf.lock()) {
... // <-- This path will not be reached if the destructor is running.
} Except of course you meant the issue were our WinUI/XAML classes are being There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤷 I'm not sure what to do. Currently There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah I'm sorry I mixed up We can wait for input from other terminal devs. 🙂 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
// Remove ourself from the list of peasants so that we aren't included in | ||
// any future requests. This will also mean we block until any existing | ||
// event handler finishes. | ||
_windowManager.SignalClose(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added this which makes many of the other changes potentially redundant (but it doesn't hurt to have more checks). Assuming this call doesn't fail somehow, we will try to acquire the lock in the monarch which means we block until any outstanding event handlers finish, and then afterwards we are removed from the list of peasants so we shouldn't be called again. Edit: I remembered that I made this function do nothing if we are in the middle of a |
||
|
||
_window->Close(); | ||
} | ||
|
||
LaunchPosition AppHost::_GetWindowLaunchPosition() | ||
{ | ||
// Get the position of the current window. This includes the | ||
// non-client already. | ||
const auto window = _window->GetWindowRect(); | ||
LaunchPosition pos{}; | ||
// If we started saving before closing, but didn't resume the event handler | ||
// until after _window might be a nullptr. | ||
if (!_window) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function is exclusively called on the main thread right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is called in a couple of event handlers that may or may not be on the main thread, so I don't know if I can guarantee it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't this a race condition with whatever sets this field to null then? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Its why there is still the try/catch, yes. I admittedly don't have a good sense of the full lifetime of the application, especially as things are closing. I believe in the error that @zadjii-msft found it would be on the main thread (assuming the ui thread is also the one that handles destruction) since it went through There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To be more clear, I believe, but have not verified that this is the order of operations in the crash
I have attempted to solve the problem in the following ways
I think that this is sufficiently resilient to avoiding crashes but it is possible that there is something I'm missing still in my understanding of how the runtime manages the lifetime of |
||
{ | ||
return pos; | ||
} | ||
|
||
const auto dpi = _window->GetCurrentDpi(); | ||
const auto nonClientArea = _window->GetNonClientFrame(dpi); | ||
try | ||
{ | ||
// Get the position of the current window. This includes the | ||
// non-client already. | ||
const auto window = _window->GetWindowRect(); | ||
|
||
// The nonClientArea adjustment is negative, so subtract that out. | ||
// This way we save the user-visible location of the terminal. | ||
LaunchPosition pos{}; | ||
pos.X = window.left - nonClientArea.left; | ||
pos.Y = window.top; | ||
const auto dpi = _window->GetCurrentDpi(); | ||
const auto nonClientArea = _window->GetNonClientFrame(dpi); | ||
|
||
// The nonClientArea adjustment is negative, so subtract that out. | ||
// This way we save the user-visible location of the terminal. | ||
pos.X = window.left - nonClientArea.left; | ||
pos.Y = window.top; | ||
} | ||
CATCH_LOG(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This try/catch seems to be redundant with the one on line 746. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It felt safer to also just put the try/catch here since this function is called in the |
||
|
||
return pos; | ||
} | ||
|
@@ -723,10 +742,15 @@ winrt::Windows::Foundation::IAsyncOperation<winrt::hstring> AppHost::_GetWindowL | |
{ | ||
winrt::apartment_context peasant_thread; | ||
|
||
winrt::hstring layoutJson = L""; | ||
// Use the main thread since we are accessing controls. | ||
co_await winrt::resume_foreground(_logic.GetRoot().Dispatcher()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this line fail? should that also go in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This cannot fail. At least not in a way that can be caught by try/catch (for instance if |
||
const auto pos = _GetWindowLaunchPosition(); | ||
const auto layoutJson = _logic.GetWindowLayoutJson(pos); | ||
try | ||
{ | ||
const auto pos = _GetWindowLaunchPosition(); | ||
layoutJson = _logic.GetWindowLayoutJson(pos); | ||
} | ||
CATCH_LOG() | ||
|
||
// go back to give the result to the peasant. | ||
co_await peasant_thread; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh jeez, this introduced a new one. I'll file it