mirror of
https://github.com/microsoft/terminal.git
synced 2025-12-10 00:48:23 -06:00
Fix session persistence when the session ends (#17714)
Once all applications that have received a `WM_ENDSESSION` message have returned from processing said message, windows will terminate all processes. This forces us to process the message synchronously. This meant that this issue was timing dependent. If Windows Terminal was quick at persisting buffers and you had some other application that was slow to shut down (e.g. Steam), you would never see this issue. Closes #17179 Closes #17250 ## Validation Steps Performed * Set up a lean Hyper-V VM for fast reboots * `Set-VMComPort <vm> 1 \\.pipe\\<pipe>` * Hook up WIL to write to COM1 * Add a ton of debug prints all over the place * Read COM output with Putty for hours * RTFM, and notice that the `WM_ENDSESSION` documentation states "the session can end any time after all applications have returned from processing this message" * Be very very sad ✅ * Fix it * Rebooting now shows on COM1 that persistence runs ✅ * Windows get restored after reboot ✅ (cherry picked from commit b439925acc1f6c51180b2fa33775395b595d1c60) Service-Card-Id: PVTI_lADOAF3p4s4AmhmszgSGCik Service-Version: 1.21
This commit is contained in:
parent
1742dc03ff
commit
8c2c88e44f
@ -96,7 +96,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
|
||||
peasant.ShowNotificationIconRequested([this](auto&&, auto&&) { ShowNotificationIconRequested.raise(*this, nullptr); });
|
||||
peasant.HideNotificationIconRequested([this](auto&&, auto&&) { HideNotificationIconRequested.raise(*this, nullptr); });
|
||||
peasant.QuitAllRequested({ this, &Monarch::_handleQuitAll });
|
||||
|
||||
{
|
||||
std::unique_lock lock{ _peasantsMutex };
|
||||
@ -134,7 +133,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
// - <none> used
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void Monarch::_handleQuitAll(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::Foundation::IInspectable& /*args*/)
|
||||
void Monarch::QuitAll()
|
||||
{
|
||||
if (_quitting.exchange(true, std::memory_order_relaxed))
|
||||
{
|
||||
@ -166,12 +165,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
{
|
||||
peasantSearch->second.Quit();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Somehow we don't have our own peasant, this should never happen.
|
||||
// We are trying to quit anyways so just fail here.
|
||||
assert(peasantSearch != _peasants.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -86,6 +86,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
|
||||
uint64_t AddPeasant(winrt::Microsoft::Terminal::Remoting::IPeasant peasant);
|
||||
void SignalClose(const uint64_t peasantId);
|
||||
void QuitAll();
|
||||
|
||||
uint64_t GetNumberOfPeasants();
|
||||
|
||||
|
||||
@ -63,6 +63,7 @@ namespace Microsoft.Terminal.Remoting
|
||||
void HandleActivatePeasant(WindowActivatedArgs args);
|
||||
void SummonWindow(SummonWindowSelectionArgs args);
|
||||
void SignalClose(UInt64 peasantId);
|
||||
void QuitAll();
|
||||
|
||||
void SummonAllWindows();
|
||||
Boolean DoesQuakeWindowExist();
|
||||
|
||||
@ -260,22 +260,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
|
||||
void Peasant::RequestQuitAll()
|
||||
{
|
||||
try
|
||||
{
|
||||
QuitAllRequested.raise(*this, nullptr);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
}
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Peasant_RequestQuit",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
|
||||
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
|
||||
}
|
||||
|
||||
void Peasant::AttachContentToWindow(Remoting::AttachRequest request)
|
||||
{
|
||||
try
|
||||
|
||||
@ -56,7 +56,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
void RequestRename(const winrt::Microsoft::Terminal::Remoting::RenameRequestArgs& args);
|
||||
void RequestShowNotificationIcon();
|
||||
void RequestHideNotificationIcon();
|
||||
void RequestQuitAll();
|
||||
void Quit();
|
||||
|
||||
void AttachContentToWindow(Remoting::AttachRequest request);
|
||||
@ -76,7 +75,6 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
|
||||
til::typed_event<> ShowNotificationIconRequested;
|
||||
til::typed_event<> HideNotificationIconRequested;
|
||||
til::typed_event<> QuitAllRequested;
|
||||
til::typed_event<> QuitRequested;
|
||||
|
||||
til::typed_event<winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::AttachRequest> AttachRequested;
|
||||
|
||||
@ -80,7 +80,6 @@ namespace Microsoft.Terminal.Remoting
|
||||
|
||||
void RequestShowNotificationIcon();
|
||||
void RequestHideNotificationIcon();
|
||||
void RequestQuitAll();
|
||||
void Quit();
|
||||
|
||||
void AttachContentToWindow(AttachRequest request);
|
||||
@ -95,7 +94,6 @@ namespace Microsoft.Terminal.Remoting
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ShowNotificationIconRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> HideNotificationIconRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> QuitAllRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> QuitRequested;
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<Object, AttachRequest> AttachRequested;
|
||||
|
||||
@ -375,6 +375,18 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
WindowClosed.raise(s, e);
|
||||
}
|
||||
|
||||
void WindowManager::QuitAll()
|
||||
{
|
||||
if (_monarch)
|
||||
{
|
||||
try
|
||||
{
|
||||
_monarch.QuitAll();
|
||||
}
|
||||
CATCH_LOG()
|
||||
}
|
||||
}
|
||||
|
||||
void WindowManager::SignalClose(const Remoting::Peasant& peasant)
|
||||
{
|
||||
if (_monarch)
|
||||
|
||||
@ -32,6 +32,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
Remoting::Peasant CreatePeasant(const Remoting::WindowRequestedArgs& args);
|
||||
|
||||
void SignalClose(const Remoting::Peasant& peasant);
|
||||
void QuitAll();
|
||||
void SummonWindow(const Remoting::SummonWindowSelectionArgs& args);
|
||||
void SummonAllWindows();
|
||||
Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo> GetPeasantInfos();
|
||||
|
||||
@ -12,6 +12,7 @@ namespace Microsoft.Terminal.Remoting
|
||||
Peasant CreatePeasant(WindowRequestedArgs args);
|
||||
|
||||
void SignalClose(Peasant p);
|
||||
void QuitAll();
|
||||
|
||||
void UpdateActiveTabTitle(String title, Peasant p);
|
||||
|
||||
|
||||
@ -82,7 +82,6 @@ namespace RemotingUnitTests
|
||||
void Summon(const Remoting::SummonWindowBehavior& /*args*/) DIE;
|
||||
void RequestShowNotificationIcon() DIE;
|
||||
void RequestHideNotificationIcon() DIE;
|
||||
void RequestQuitAll() DIE;
|
||||
void Quit() DIE;
|
||||
void AttachContentToWindow(Remoting::AttachRequest) DIE;
|
||||
void SendContent(winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs) DIE;
|
||||
@ -94,7 +93,6 @@ namespace RemotingUnitTests
|
||||
til::typed_event<winrt::Windows::Foundation::IInspectable, Remoting::SummonWindowBehavior> SummonRequested;
|
||||
til::typed_event<> ShowNotificationIconRequested;
|
||||
til::typed_event<> HideNotificationIconRequested;
|
||||
til::typed_event<> QuitAllRequested;
|
||||
til::typed_event<> QuitRequested;
|
||||
til::typed_event<winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::AttachRequest> AttachRequested;
|
||||
til::typed_event<winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::RequestReceiveContentArgs> SendContentRequested;
|
||||
@ -111,6 +109,7 @@ namespace RemotingUnitTests
|
||||
void HandleActivatePeasant(Remoting::WindowActivatedArgs /*args*/) DIE;
|
||||
void SummonWindow(Remoting::SummonWindowSelectionArgs /*args*/) DIE;
|
||||
void SignalClose(uint64_t /*peasantId*/) DIE;
|
||||
void QuitAll() DIE;
|
||||
|
||||
void SummonAllWindows() DIE;
|
||||
bool DoesQuakeWindowExist() DIE;
|
||||
|
||||
@ -13,6 +13,8 @@
|
||||
|
||||
#include <TerminalThemeHelpers.h>
|
||||
|
||||
#include <til/latch.h>
|
||||
|
||||
using namespace winrt::Windows::UI;
|
||||
using namespace winrt::Windows::UI::Composition;
|
||||
using namespace winrt::Windows::UI::Xaml;
|
||||
@ -348,9 +350,29 @@ void AppHost::Initialize()
|
||||
});
|
||||
|
||||
_windowCallbacks.AutomaticShutdownRequested = _window->AutomaticShutdownRequested([this]() {
|
||||
// Raised when the OS is beginning an update of the app. We will quit,
|
||||
// to save our state, before the OS manually kills us.
|
||||
_quit();
|
||||
// This is the WM_ENDSESSION handler.
|
||||
// The event is raised when the user is logged out, because the system is rebooting, etc.
|
||||
// Due to the design of WM_ENDSESSION, returning from the message indicates to the OS that it's fine to
|
||||
// terminate us at any time. Luckily Windows has never heavily relied on message passing or asynchronous
|
||||
// eventing in any of its UI frameworks. It also was clearly impossible to use WaitForMultipleObjects with
|
||||
// bWaitAll=TRUE and a timeout to wait for all applications to exit cleanly.
|
||||
// As such we attempt to synchronously shut down the app here. Otherwise, it could just call _quit().
|
||||
|
||||
const auto state = ApplicationState::SharedInstance();
|
||||
|
||||
state.PersistedWindowLayouts(nullptr);
|
||||
|
||||
// A duplicate of AppHost::_QuitRequested().
|
||||
if (_appLogic && _windowLogic && _appLogic.ShouldUsePersistedLayout())
|
||||
{
|
||||
_windowLogic.PersistState();
|
||||
}
|
||||
|
||||
_windowManager.SignalClose(_peasant);
|
||||
_windowManager.QuitAll();
|
||||
|
||||
// Ensure to write the state.json before we get TerminateProcess()d by the OS (Thanks!).
|
||||
state.Flush();
|
||||
});
|
||||
|
||||
// Load bearing: make sure the PropertyChanged handler is added before we
|
||||
@ -440,7 +462,7 @@ winrt::fire_and_forget AppHost::_quit()
|
||||
co_await winrt::resume_background();
|
||||
|
||||
ApplicationState::SharedInstance().PersistedWindowLayouts(nullptr);
|
||||
peasant.RequestQuitAll();
|
||||
_windowManager.QuitAll();
|
||||
}
|
||||
|
||||
void AppHost::_revokeWindowCallbacks()
|
||||
@ -1176,25 +1198,32 @@ void AppHost::_IsQuakeWindowChanged(const winrt::Windows::Foundation::IInspectab
|
||||
}
|
||||
|
||||
// Raised from our Peasant. We handle by propagating the call to our terminal window.
|
||||
winrt::fire_and_forget AppHost::_QuitRequested(const winrt::Windows::Foundation::IInspectable&,
|
||||
const winrt::Windows::Foundation::IInspectable&)
|
||||
void AppHost::_QuitRequested(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&)
|
||||
{
|
||||
auto weakThis{ weak_from_this() };
|
||||
// Need to be on the main thread to close out all of the tabs.
|
||||
co_await wil::resume_foreground(_windowLogic.GetRoot().Dispatcher());
|
||||
// We process the shutdown synchronously here, because otherwise the
|
||||
// AutomaticShutdownRequested() logic wouldn't run synchronously either.
|
||||
til::latch latch{ 1 };
|
||||
|
||||
const auto strongThis = weakThis.lock();
|
||||
if (!strongThis)
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
_windowLogic.GetRoot().Dispatcher().RunAsync(winrt::Windows::UI::Core::CoreDispatcherPriority::Normal, [&latch, weakThis = weak_from_this()]() {
|
||||
const auto countDownOnExit = wil::scope_exit([&latch] {
|
||||
latch.count_down();
|
||||
});
|
||||
|
||||
if (_appLogic && _windowLogic && _appLogic.ShouldUsePersistedLayout())
|
||||
{
|
||||
_windowLogic.PersistState();
|
||||
}
|
||||
const auto self = weakThis.lock();
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PostQuitMessage(0);
|
||||
if (self->_appLogic && self->_windowLogic && self->_appLogic.ShouldUsePersistedLayout())
|
||||
{
|
||||
self->_windowLogic.PersistState();
|
||||
}
|
||||
|
||||
PostQuitMessage(0);
|
||||
});
|
||||
|
||||
latch.wait();
|
||||
}
|
||||
|
||||
// Raised from TerminalWindow. We handle by bubbling the request to the window manager.
|
||||
|
||||
@ -123,8 +123,8 @@ private:
|
||||
void _SystemMenuChangeRequested(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::TerminalApp::SystemMenuChangeArgs& args);
|
||||
|
||||
winrt::fire_and_forget _QuitRequested(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Windows::Foundation::IInspectable& args);
|
||||
void _QuitRequested(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Windows::Foundation::IInspectable& args);
|
||||
|
||||
void _RequestQuitAll(const winrt::Windows::Foundation::IInspectable& sender,
|
||||
const winrt::Windows::Foundation::IInspectable& args);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user