Repository: microsoft/terminal
File: src/cascadia/TerminalConnection/ConptyConnection.cpp (C++)

Code before:
        {
            _initialVisibility = show;
        }
    }

    void ConptyConnection::ReparentWindow(const uint64_t newParent)
    {
        // If we haven't started connecting at all, stash this HWND to use once we have started.
        if (!_isStateAtOrBeyond(ConnectionState::Connecting))
        {
            _initialParentHwnd = newParent;
        }
        // Otherwise, just inform the conpty of the new owner window handle.
        // This shouldn't be hittable until GH#5000 / GH#1256, when it's
        // possible to reparent terminals to different windows.
        else if (_isConnected())
        {
            THROW_IF_FAILED(ConptyReparentPseudoConsole(_hPC.get(), reinterpret_cast<HWND>(newParent)));
        }
    }

    void ConptyConnection::Close() noexcept
    try
    {
        // .reset()ing either of these two will signal ConPTY to send out a CTRL_CLOSE_EVENT to all attached clients.
        // FYI: The other members of this class are concurrently read by the _hOutputThread
        // thread running in the background and so they're not safe to be .reset().
        _hPC.reset();
        _inPipe.reset();

        _transitionToState(ConnectionState::Closing);

        // CloseHandle() on pipes blocks until any current WriteFile()/ReadFile() has returned.
        // CancelSynchronousIo prevents us from deadlocking ourselves.
        // At this point in Close(), _inPipe won't be used anymore since the UI parts are torn down.
        // _outPipe is probably still stuck in ReadFile() and might currently be written to.
        CancelSynchronousIo(_hOutputThread.get());
        // Waiting for the output thread to exit ensures that all pending _TerminalOutputHandlers()
        // calls have returned and won't notify our caller (ControlCore) anymore. This ensures that
        // we don't call a destroyed event handler asynchronously from a background thread (GH#13880).
        LOG_LAST_ERROR_IF(WAIT_FAILED == WaitForSingleObject(_hOutputThread.get(), INFINITE));

        // Now that the background thread is done, we can safely clean up the other system objects, without
        // race conditions, or fear of deadlocking ourselves (e.g. by calling CloseHandle() on _outPipe).
        _outPipe.reset();
        _hOutputThread.reset();
        _piClient.reset();

        _transitionToState(ConnectionState::Closed);
    }
    CATCH_LOG()

Review comment (suggestion):
Ah so this does a `ConptyClosePseudoConsoleTimeout(0)`, which calls close on the pseudoconsole without waiting for the conpty process to exit. 



Code after:

    void ConptyConnection::ReparentWindow(const uint64_t newParent)
    {
        // If we haven't started connecting at all, stash this HWND to use once we have started.
        if (!_isStateAtOrBeyond(ConnectionState::Connecting))
        {
            _initialParentHwnd = newParent;
        }
        // Otherwise, just inform the conpty of the new owner window handle.
        // This shouldn't be hittable until GH#5000 / GH#1256, when it's
        // possible to reparent terminals to different windows.
        else if (_isConnected())
        {
            THROW_IF_FAILED(ConptyReparentPseudoConsole(_hPC.get(), reinterpret_cast<HWND>(newParent)));
        }
    }

    void ConptyConnection::Close() noexcept
    try
    {
        _transitionToState(ConnectionState::Closing);

        // .reset()ing either of these two will signal ConPTY to send out a CTRL_CLOSE_EVENT to all attached clients.
        // FYI: The other members of this class are concurrently read by the _hOutputThread
        // thread running in the background and so they're not safe to be .reset().
        _hPC.reset();
        _inPipe.reset();

        if (_hOutputThread)
        {
            // Loop around `CancelSynchronousIo()` just in case the signal to shut down was missed.
            // This may happen if we called `CancelSynchronousIo()` while not being stuck
            // in `ReadFile()` and if OpenConsole refuses to exit in a timely manner.
            for (;;)
            {
                // ConptyConnection::Close() blocks the UI thread, because `_TerminalOutputHandlers` might indirectly
                // reference UI objects like `ControlCore`. CancelSynchronousIo() allows us to have the background
                // thread exit as fast as possible by aborting any ongoing writes coming from OpenConsole.
                CancelSynchronousIo(_hOutputThread.get());

                // Waiting for the output thread to exit ensures that all pending _TerminalOutputHandlers()
                // calls have returned and won't notify our caller (ControlCore) anymore. This ensures that
                // we don't call a destroyed event handler asynchronously from a background thread (GH#13880).
                const auto result = WaitForSingleObject(_hOutputThread.get(), 1000);
                if (result == WAIT_OBJECT_0)
                {
                    break;
                }

                LOG_LAST_ERROR();
            }