This works to kill the content and have the app live

This commit is contained in:
Mike Griese 2021-08-12 12:55:03 -05:00
parent 56f1223dc5
commit b541179333
7 changed files with 172 additions and 2 deletions

View file

@ -199,6 +199,16 @@ namespace winrt::SampleApp::implementation
// Create the XAML control that will be attached to the content process.
// We're not passing in a connection, because the contentGuid will be used instead.
Control::TermControl control{ contentGuid, settings, nullptr };
control.RaiseNotice([this](auto&&, auto& args) {
_writeToLog(L"Content process died, probably.");
_writeToLog(args.Message());
OutOfProcContent().Children().Clear();
GuidInput().Text(L"");
if (piContentProcess.hProcess)
{
piContentProcess.reset();
}
});
Log().Children().Clear();
OutOfProcContent().Children().Append(control);

View file

@ -3,7 +3,7 @@
namespace SampleApp
{
[default_interface] runtimeclass MyPage : Windows.UI.Xaml.Controls.Page, Windows.UI.Xaml.Data.INotifyPropertyChanged
[default_interface] runtimeclass MyPage : Windows.UI.Xaml.Controls.Page
{
MyPage();
}

View file

@ -7,7 +7,8 @@
namespace winrt::Microsoft::Terminal::Control::implementation
{
ContentProcess::ContentProcess() {}
ContentProcess::ContentProcess() :
_ourPID{ GetCurrentProcessId() } {}
bool ContentProcess::Initialize(Control::IControlSettings settings,
TerminalConnection::ConnectionInformation connectionInfo)
@ -31,6 +32,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return _interactivity;
}
uint64_t ContentProcess::GetPID()
{
return _ourPID;
}
uint64_t ContentProcess::RequestSwapChainHandle(const uint64_t pid)
{
auto ourPid = GetCurrentProcessId();

View file

@ -17,12 +17,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
TerminalConnection::ConnectionInformation connectionInfo);
Control::ControlInteractivity GetInteractivity();
uint64_t GetPID();
uint64_t RequestSwapChainHandle(const uint64_t pid);
WINRT_CALLBACK(Destructed, Control::DestructedArgs);
private:
Control::ControlInteractivity _interactivity{ nullptr };
uint64_t _ourPID;
};
}

View file

@ -16,6 +16,8 @@ namespace Microsoft.Terminal.Control
ControlInteractivity GetInteractivity();
UInt64 GetPID();
UInt64 RequestSwapChainHandle(UInt64 pid);
event DestructedArgs Destructed;

View file

@ -75,6 +75,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
if (_contentProc != nullptr)
{
_interactivity = _contentProc.GetInteractivity();
_contentWaitInterrupt.create();
_createContentWaitThread();
}
}
@ -167,6 +169,148 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_ApplyUISettings(_settings);
}
bool s_waitOnContentProcess(uint64_t contentPid, HANDLE contentWaitInterrupt)
{
// This is the array of HANDLEs that we're going to wait on in
// WaitForMultipleObjects below.
// * waits[0] will be the handle to the monarch process. It gets
// signalled when the process exits / dies.
// * waits[1] is the handle to our _contentWaitInterrupt event. Another
// thread can use that to manually break this loop. We'll do that when
// we're getting torn down.
HANDLE waits[2];
waits[1] = contentWaitInterrupt;
bool displayError = true;
// bool exitThreadRequested = false;
// while (!exitThreadRequested)
// {
// At any point in all this, the current monarch might die. If it
// does, we'll go straight to a new election, in the "jail"
// try/catch below. Worst case, eventually, we'll become the new
// monarch.
try
{
// This might fail to even ask the monarch for it's PID.
wil::unique_handle hContent{ OpenProcess(PROCESS_ALL_ACCESS,
FALSE,
static_cast<DWORD>(contentPid)) };
// If we fail to open the content, then they don't exist
// anymore! Go straight to an election.
if (hContent.get() == nullptr)
{
const auto gle = GetLastError();
TraceLoggingWrite(g_hTerminalControlProvider,
"TermControl_FailedToOpenContent",
// TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"),
TraceLoggingUInt64(gle, "lastError", "The result of GetLastError"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
// exitThreadRequested = true;
// continue;
}
waits[0] = hContent.get();
switch (WaitForMultipleObjects(2, waits, FALSE, INFINITE))
{
case WAIT_OBJECT_0 + 0: // waits[0] was signaled, the handle to the monarch process
TraceLoggingWrite(g_hTerminalControlProvider,
"TermControl_ContentDied",
// TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
// Connect to the new monarch, which might be us!
// If we become the monarch, then we'll return true and exit this thread.
// exitThreadRequested = true;
break;
case WAIT_OBJECT_0 + 1: // waits[1] was signaled, our manual interrupt
TraceLoggingWrite(g_hTerminalControlProvider,
"TermControl_ContentWaitInterrupted",
// TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
// exitThreadRequested = true;
displayError = false;
break;
case WAIT_TIMEOUT:
// This should be impossible.
TraceLoggingWrite(g_hTerminalControlProvider,
"TermControl_ContentWaitTimeout",
// TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
// exitThreadRequested = true;
break;
default:
{
// TODO!
// Returning any other value is invalid. Just die.
const auto gle = GetLastError();
TraceLoggingWrite(g_hTerminalControlProvider,
"TermControl_WaitFailed",
// TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"),
TraceLoggingUInt64(gle, "lastError", "The result of GetLastError"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
// exitThreadRequested = true;
// ExitProcess(0);
}
}
}
catch (...)
{
// Theoretically, if window[1] dies when we're trying to get
// it's PID we'll get here. We can probably just exit here.
TraceLoggingWrite(g_hTerminalControlProvider,
"TermControl_ExceptionInWaitThread",
// TraceLoggingUInt64(peasantID, "peasantID", "Our peasant ID"),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
// exitThreadRequested = true;
}
// }
// if (displayError)
// {
// }
return displayError;
}
void TermControl::_createContentWaitThread()
{
_contentWaitThread = std::thread([weakThis = get_weak(), contentPid = _contentProc.GetPID(), contentWaitInterrupt=_contentWaitInterrupt.get()] {
if (s_waitOnContentProcess(contentPid, contentWaitInterrupt))
{
if (auto control{ weakThis.get() })
{
control->_contentWaitThread.detach();
control->_raiseContentDied();
}
}
});
}
winrt::fire_and_forget TermControl::_raiseContentDied()
{
auto weakThis{ get_weak() };
co_await winrt::resume_foreground(Dispatcher());
if (auto control{ weakThis.get() })
{
// dialog the thing
auto noticeArgs = winrt::make<NoticeEventArgs>(NoticeLevel::Error, L"The content of this terminal died unexpectedly.");
control->_RaiseNoticeHandlers(*control, noticeArgs);
}
}
// Method Description:
// - Loads the search box from the xaml UI and focuses it.
void TermControl::CreateSearchBoxControl()

View file

@ -185,6 +185,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
winrt::Windows::UI::Xaml::Controls::SwapChainPanel::LayoutUpdated_revoker _layoutUpdatedRevoker;
wil::unique_event _contentWaitInterrupt;
std::thread _contentWaitThread;
void _createContentWaitThread();
// void _waitOnContentProcess();
inline bool _IsClosing() const noexcept
{
// _closing isn't atomic and may only be accessed from the main thread.
@ -267,6 +272,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
winrt::fire_and_forget _coreTransparencyChanged(IInspectable sender, Control::TransparencyChangedEventArgs args);
void _coreRaisedNotice(const IInspectable& s, const Control::NoticeEventArgs& args);
void _coreWarningBell(const IInspectable& sender, const IInspectable& args);
winrt::fire_and_forget _raiseContentDied();
};
}