diff --git a/src/cascadia/TerminalConnection/ConptyConnection.cpp b/src/cascadia/TerminalConnection/ConptyConnection.cpp index ab1c9ab5c55..676d1f905fc 100644 --- a/src/cascadia/TerminalConnection/ConptyConnection.cpp +++ b/src/cascadia/TerminalConnection/ConptyConnection.cpp @@ -455,4 +455,23 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation return 0; } + // Function Description: + // - This function will be called (by C++/WinRT) after the final outstanding reference to + // any given connection instance is released. + // When a client application exits, its termination will wait for the output thread to + // run down. However, because our teardown is somewhat complex, our last reference may + // be owned by the very output thread that the client wait threadpool is blocked on. + // During destruction, we'll try to release any outstanding handles--including the one + // we have to the threadpool wait. As you might imagine, this takes us right to deadlock + // city. + // Deferring the final destruction of the connection to a background thread that can't + // be awaiting our destruction breaks the deadlock. + // Arguments: + // - connection: the final living reference to an outgoing connection + winrt::fire_and_forget ConptyConnection::final_release(std::unique_ptr connection) + { + co_await winrt::resume_background(); // move to background + connection.reset(); // explicitly destruct + } + } diff --git a/src/cascadia/TerminalConnection/ConptyConnection.h b/src/cascadia/TerminalConnection/ConptyConnection.h index b3ca0827aa8..557fb4969e0 100644 --- a/src/cascadia/TerminalConnection/ConptyConnection.h +++ b/src/cascadia/TerminalConnection/ConptyConnection.h @@ -27,6 +27,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation const uint32_t rows, const uint32_t cols, const guid& guid); + static winrt::fire_and_forget final_release(std::unique_ptr connection); void Start(); void WriteInput(hstring const& data);