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

Persist inbox conhost; delegate control activities to it via a pipe #10415

Merged
20 commits merged into from
Jun 16, 2021

Conversation

miniksa
Copy link
Member

@miniksa miniksa commented Jun 11, 2021

Persist inbox conhost; delegate control activities to it via a pipe

PR Checklist

Detailed Description of the Pull Request / Additional comments

It turns out that there's a bit of ownership that goes on with the original inbox conhost.exe and the operating system/driver. The PID of that original conhost.exe is stowed when the initial connection is established and it is verified for several activities. This means that the plan of letting it go completely away and having the OpenConsole.exe take over all of its activities must be slightly revised.

I have tested the following two alternatives to keeping conhost.exe around and they do not work:

  1. Replacing the original owner conhost.exe with OpenConsole.exe - A.) The driver does not allow this. Once the owner is registered, it cannot be replaced. B.) There's no way of updating this information inside the client process space and it is kept there too in the kernelbase/conclnt data from its initial connection.
  2. Attempting to pick up the first packet (to determine headed/headless and other initial connection information that we use to determine whether handoff is appropriate or not) prior to registering any owner at all. - The driver doesn't allow this either. The owner must be registered prior to a packet coming through.

Put this mental model in your head:
CMD --> Conhost (inbox) --> OpenConsole (WT Package) --> Terminal (WT Package)

So since the conhost.exe needs to stick around, here's what I'm doing in this PR:

  • conhost.exe in the OS will receive back the OpenConsole.exe process handle on a successful handoff and is expected to remain alive until the OpenConsole.exe exits. It's now waiting on that before it terminates itself.
  • conhost.exe in the OS will establish a signal channel pipe and listen for control commands from OpenConsole.exe in a very similar fashion to how the ConPTY signal pipe operates between the Terminal and the PTY (provided by OpenConsole.exe in this particular example.) When OpenConsole.exe needs to do something that would be verified by the OS and rejected... it will instead signal the original conhost.exe to do that thing and it will go through.
  • conhost.exe will give its own handle through to OpenConsole.exe so it can monitor its lifetime and cleanup. If the owner is gone, the session should end.
  • Assorted handle cleanup that was leading to improper exits. I was confused between .reset() and .release() for some of the wil::unique_any<T> handling and it lead to leaked handles. The leaked handles meant that threads weren't aware of the other sides collapsing and wouldn't cleanup/terminate appropriately.

How does this fix things?

  • For the WSL cases... WSL was specifically looking up the owner PID of the console session from the driver. That was the conhost.exe PID. If it exits, that PID isn't valid and is recycled. Thus the parameter is incorrect or other inappropriate WSL setup behaviors.
  • Ctrl+C not passed... this is a signal the operating system rejects from a PID that is not the owner. This is now relayed through the original owner and it works.
  • Leftover processes... I believe I explained this was both not-enough-monitoring of each others' process lifetimes coupled with mishandling of release/resetting handles and leaking them.
  • Powershell sometimes fails to attach... my theory on this one is that it's a race that became upset when the conhost.exe disappeared while something about Powershell/.NET was still starting, much like the WSL one. I believe now that it is sticking around, it will be fine.

Also, this WILL require an OS update to complete improvement of functionality and I have revised the interface ID. This is considered an acceptable breaking change with no mitigation because we said this feature was an alpha preview.

Validation Steps Performed

  • Launched WSL with defapp set, it works
  • Launched WSL with defapp set and the debug tap on, it works and opens in two tabs
  • Launched CMD, ran ping, did Ctrl+C, it now receives it
  • Launched Win+X powershell a ton of times. It seems fine now
  • Launched cmd, powershell, wsl, etc. Killed assorted processes in the chain (client/conhost/openconsole/windowsterminal) and observed in Process Explorer (with a long delta timer so I could see it) that they all successfully tear down now without leftovers.

@ghost ghost added Area-DefApp Issue-Bug It either shouldn't be doing this or needs an investigation. Priority-1 A description (P1) Product-Terminal The new Windows Terminal. labels Jun 11, 2021
@miniksa miniksa marked this pull request as draft June 11, 2021 22:56
@miniksa
Copy link
Member Author

miniksa commented Jun 11, 2021

I just realized I likely need to send a few more parameters over the IConsoleHandoff interface while I'm here for #9458. So setting to Draft until I figure that out so I don't have to rev the IID again.

@miniksa
Copy link
Member Author

miniksa commented Jun 11, 2021

I just realized I likely need to send a few more parameters over the IConsoleHandoff interface while I'm here for #9458. So setting to Draft until I figure that out so I don't have to rev the IID again.

ConsoleInitializeConnectInfo looks like it can pick up any of this information in the OpenConsole.exe so it doesn't need to be passed. So nevermind on the #9458 business. That's between OpenConsole.exe and WindowsTerminal.exe... not the OS copy.

@miniksa miniksa marked this pull request as ready for review June 11, 2021 23:13
@o-sdn-o
Copy link

o-sdn-o commented Jun 14, 2021

Could this be related to #10400?

@DHowett
Copy link
Member

DHowett commented Jun 14, 2021

Could this be related to #10400?

Unlikely; this code is only used when conhost (inside windows) spawns a secondary conhost, not when you call CreatePseudoConsole.

src/interactivity/base/ServiceLocator.cpp Outdated Show resolved Hide resolved
@@ -364,6 +365,8 @@ HRESULT ConsoleCreateIoThread(_In_ HANDLE Server,
// from the driver... or an S_OK success.
[[nodiscard]] HRESULT ConsoleEstablishHandoff([[maybe_unused]] _In_ HANDLE Server,
[[maybe_unused]] HANDLE driverInputEvent,
[[maybe_unused]] HANDLE hostSignalPipe,
[[maybe_unused]] HANDLE hostProcessHandle,
[[maybe_unused]] PCONSOLE_API_MSG connectMessage)
try
{
Copy link
Member

Choose a reason for hiding this comment

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

you'll probably merge conflict near here because of til::feature

src/inc/HostSignals.hpp Outdated Show resolved Hide resolved
src/inc/HostSignals.hpp Outdated Show resolved Hide resolved
src/interactivity/base/HostSignalInputThread.cpp Outdated Show resolved Hide resolved
src/interactivity/base/HostSignalInputThread.cpp Outdated Show resolved Hide resolved
src/interactivity/base/HostSignalInputThread.cpp Outdated Show resolved Hide resolved
src/interactivity/base/HostSignalInputThread.cpp Outdated Show resolved Hide resolved
src/interactivity/base/RemoteConsoleControl.cpp Outdated Show resolved Hide resolved
src/interactivity/base/RemoteConsoleControl.cpp Outdated Show resolved Hide resolved
@ghost ghost added Needs-Author-Feedback The original author of the issue/PR needs to come back and respond to something and removed Needs-Author-Feedback The original author of the issue/PR needs to come back and respond to something labels Jun 14, 2021
Copy link
Member

@lhecker lhecker left a comment

Choose a reason for hiding this comment

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

Lotta comments... But no actual mistakes.

src/host/exe/CConsoleHandoff.cpp Outdated Show resolved Hide resolved
src/host/srvinit.cpp Show resolved Hide resolved
src/host/srvinit.cpp Show resolved Hide resolved
// - Creates the PTY Signal Input Thread.
// Arguments:
// - hPipe - a handle to the file representing the read end of the Host Signal pipe.
HostSignalInputThread::HostSignalInputThread(_In_ wil::unique_hfile&& hPipe) :
Copy link
Member

Choose a reason for hiding this comment

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

We don't need _In_ in modern C++ anymore, do we?
Also I think I need to change my opinion: I previously agreed with Dustin that && makes the intent of ownership transfer clear, but now that I read the code I feel like a version without any references at all is cleaner. It properly works even with prvalues, like when you want to write new HostSignalInputThread(wil::make_unique<>(...)) (if that existed), etc.
I'm not sure if it can be argued in this particular case that the && variant helps confer any additional meaning to future readers, what a handle called "unique" doesn't already.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is between you and @DHowett then. && already confuses me and you two understand it better than me. I just want whatever works.

Copy link
Member Author

Choose a reason for hiding this comment

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

(I did drop SAL tho...)

src/interactivity/base/HostSignalInputThread.hpp Outdated Show resolved Hide resolved
src/interactivity/base/HostSignalInputThread.cpp Outdated Show resolved Hide resolved
src/interactivity/base/RemoteConsoleControl.cpp Outdated Show resolved Hide resolved
src/interactivity/base/RemoteConsoleControl.cpp Outdated Show resolved Hide resolved
src/interactivity/base/ServiceLocator.cpp Outdated Show resolved Hide resolved
src/server/IoDispatchers.cpp Outdated Show resolved Hide resolved
@miniksa
Copy link
Member Author

miniksa commented Jun 15, 2021

Don't merge yet. I need to do a final e2e test after all the style feedback to make sure I didn't bungle a byte buffer.

@miniksa
Copy link
Member Author

miniksa commented Jun 16, 2021

Built against OS and fixed. Patched OS and tried it and confirmed the issues are still resolved. Setting automerge.

@miniksa miniksa added the AutoMerge Marked for automatic merge by the bot when requirements are met label Jun 16, 2021
@ghost
Copy link

ghost commented Jun 16, 2021

Hello @miniksa!

Because this pull request has the AutoMerge label, I will be glad to assist with helping to merge this pull request once all check-in policies pass.

p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (@msftbot) and give me an instruction to get started! Learn more here.

@ghost ghost merged commit 2bd5791 into main Jun 16, 2021
@ghost ghost deleted the dev/miniksa/def_wsl branch June 16, 2021 19:23
Comment on lines 23 to 24
THROW_HR_IF(E_HANDLE, _hFile.get() == INVALID_HANDLE_VALUE);
THROW_HR_IF(E_HANDLE, _hFile.get() == nullptr);
Copy link
Member

Choose a reason for hiding this comment

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

huh, i thought that these were the same

moreover, however: !_hFile is the valid way to check for presence in a wil handle

{
return NTSTATUS_FROM_WIN32(::GetLastError());
NT_RETURN_NTSTATUS(static_cast<NTSTATUS>(NTSTATUS_FROM_WIN32(::GetLastError())));
Copy link
Member

Choose a reason for hiding this comment

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

i... thought we moved to wil because there was a macro that made this less ugly, not a macro that made it more ugly 😉

Copy link
Member

Choose a reason for hiding this comment

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

For the glory of error tracing! 🥲
It's really weird that the static_cast is necessary...

Copy link
Member Author

Choose a reason for hiding this comment

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

the only plus side to this mess is that it is more debuggable with the wil tracelog. You're right... wil was supposed to solve this but then on further inspection... someone implemented like 4 of the functions and not the ones I needed (NT_RETURN_NTSTATUS_IF and NT_RETURN_NTSTATUS_IF_WIN32_BOOL_FALSE etc.)

@ghost
Copy link

ghost commented Jul 14, 2021

🎉Windows Terminal Preview v1.10.1933.0 has been released which incorporates this pull request.:tada:

Handy links:

This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-DefApp AutoMerge Marked for automatic merge by the bot when requirements are met Issue-Bug It either shouldn't be doing this or needs an investigation. Priority-1 A description (P1) Product-Terminal The new Windows Terminal.
Projects
None yet
4 participants