Opening a hyperlink can cause the Terminal to hang #11100

Closed
opened 2026-01-31 02:38:34 +00:00 by claunia · 6 comments
Owner

Originally created by @zadjii-msft on GitHub (Oct 21, 2020).

Originally assigned to: @miniksa on GitHub.

I originally posted this in #7691, but it turns out the hang wasn't even introduced there - it was already in main before that.


Blocking based off the hang discussion we're currently having on teams. Turns out the main thread is grabbing the write lock twice:

Once in

[0x4f] TerminalControl!winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl::_PointerPressedHandler + 0x593

then again in

[0x6]   TerminalControl!winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl::_TrySendKeyEvent + 0x116   

because WinUI somehow routes a KeyDownEvent to TermControl during the PointerPressed???


Suggested fix:

if (point.Properties().IsLeftButtonPressed())
{
    const auto cursorPosition = point.Position();
    const auto terminalPosition = _GetTerminalPosition(cursorPosition);
    {
        // handle ALT key
        auto lock = _terminal->LockForWriting();
        _terminal->SetBlockSelection(altEnabled);
        // Release the write lock
    }
    // Re-grab the read lock
    auto lock = _terminal->LockForReading();
    ...
}
Originally created by @zadjii-msft on GitHub (Oct 21, 2020). Originally assigned to: @miniksa on GitHub. I originally posted this in #7691, but it turns out the hang wasn't even introduced there - it was already in `main` before that. <hr> Blocking based off the hang discussion we're currently having on teams. Turns out the main thread is grabbing the write lock twice: Once in ``` [0x4f] TerminalControl!winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl::_PointerPressedHandler + 0x593 ``` then again in ``` [0x6] TerminalControl!winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl::_TrySendKeyEvent + 0x116 ``` because WinUI somehow routes a `KeyDownEvent` to TermControl _during the `PointerPressed`???_ <hr> Suggested fix: ```c++ if (point.Properties().IsLeftButtonPressed()) { const auto cursorPosition = point.Position(); const auto terminalPosition = _GetTerminalPosition(cursorPosition); { // handle ALT key auto lock = _terminal->LockForWriting(); _terminal->SetBlockSelection(altEnabled); // Release the write lock } // Re-grab the read lock auto lock = _terminal->LockForReading(); ... } ```
Author
Owner

@DHowett commented on GitHub (Oct 28, 2020):

So here's a thought.

We're holding the write lock while calling _HyperlinkHandler.

We should never call a user callback under our lock. That could give the user the ability to hold us up indefinitely.

We should get the hyperlink out of the buffer ASAP, release the lock, and then call the handler.

Problem is, this is tangled up with the selection code.

@DHowett commented on GitHub (Oct 28, 2020): So here's a thought. We're holding the write lock while calling `_HyperlinkHandler`. We should _never_ call a user callback under our lock. That could give the user the ability to hold us up indefinitely. We should get the hyperlink out of the buffer ASAP, release the lock, and then call the handler. Problem is, this is tangled up with the selection code.
Author
Owner

@DHowett commented on GitHub (Oct 28, 2020):

Oh you clearly figured that out ;P

@DHowett commented on GitHub (Oct 28, 2020): Oh you clearly figured that out ;P
Author
Owner

@miniksa commented on GitHub (Oct 28, 2020):

I just had this here on my machine and the issue is that ShellExecute is pumping the message queue during its operation which is then servicing the TermControl::_CursorTimerTick as it runs around the queue looking for whatever it's looking for.

TermControl::_CursorTimerTick attempts to take the lock for writing because it's going to adjust the cursor blink state. But the ShellExecute call it is being serviced off of is a result of TermControl::_HyperlinkHandler which is called from TermControl::_PointerPressedHandler which is already holding a write lock.

So somehow the connection needs to be broken because _HyperlinkHandler first only really needs a read lock to fill its parameter. It doesn't actually need to have anything locked at all after it starts running as it already has all the state it needs.

@DHowett says that really we should probably condense the selection stuff down a layer since a lot of this is duplicated between WPF and XAML.... but that's bigger than this fix.

I think we just need to either:

  1. Edit _HyperlinkHandler to coroutine off somewhere so it can return into _PointerPressedHandler and unlock the write lock allowing the ShellExecute call to potentially pump messages with write locks in them like the _CursorTimerTick one.
  2. Change the top of _PointerPressedHandler to not take a write lock for everything. I bet that was relevant in the past, but now with the hyperlink handler, it first off really only needs a read lock and second could give that up after retrieving the URI data and before calling down further to _HyperlinkHandler, resolving the situation.
@miniksa commented on GitHub (Oct 28, 2020): I just had this here on my machine and the issue is that `ShellExecute` is pumping the message queue during its operation which is then servicing the `TermControl::_CursorTimerTick` as it runs around the queue looking for whatever it's looking for. `TermControl::_CursorTimerTick` attempts to take the lock for writing because it's going to adjust the cursor blink state. But the `ShellExecute` call it is being serviced off of is a result of `TermControl::_HyperlinkHandler` which is called from `TermControl::_PointerPressedHandler` which is already holding a write lock. So somehow the connection needs to be broken because `_HyperlinkHandler` first only really needs a read lock to fill its parameter. It doesn't actually need to have anything locked at all after it starts running as it already has all the state it needs. @DHowett says that really we should probably condense the selection stuff down a layer since a lot of this is duplicated between WPF and XAML.... but that's bigger than this fix. I think we just need to either: 1. Edit `_HyperlinkHandler` to coroutine off somewhere so it can return into `_PointerPressedHandler` and unlock the write lock allowing the `ShellExecute` call to potentially pump messages with write locks in them like the `_CursorTimerTick` one. 2. Change the top of `_PointerPressedHandler` to not take a write lock for everything. I bet that was relevant in the past, but now with the hyperlink handler, it first off really only needs a read lock and second could give that up after retrieving the URI data and before calling down further to `_HyperlinkHandler`, resolving the situation.
Author
Owner

@DHowett commented on GitHub (Oct 28, 2020):

Yep. Do number 1. It's easy.

@DHowett commented on GitHub (Oct 28, 2020): Yep. Do number 1. It's easy.
Author
Owner

@ghost commented on GitHub (Nov 11, 2020):

:tada:This issue was addressed in #8087, which has now been successfully released as Windows Terminal v1.4.3141.0.🎉

Handy links:

@ghost commented on GitHub (Nov 11, 2020): :tada:This issue was addressed in #8087, which has now been successfully released as `Windows Terminal v1.4.3141.0`.:tada: Handy links: * [Release Notes](https://github.com/microsoft/terminal/releases/tag/v1.4.3141.0) * [Store Download](https://www.microsoft.com/store/apps/9n8g5rfz9xk3?cid=storebadge&ocid=badge)
Author
Owner

@ghost commented on GitHub (Nov 11, 2020):

:tada:This issue was addressed in #8087, which has now been successfully released as Windows Terminal Preview v1.5.3142.0.🎉

Handy links:

@ghost commented on GitHub (Nov 11, 2020): :tada:This issue was addressed in #8087, which has now been successfully released as `Windows Terminal Preview v1.5.3142.0`.:tada: Handy links: * [Release Notes](https://github.com/microsoft/terminal/releases/tag/v1.5.3142.0) * [Store Download](https://www.microsoft.com/store/apps/9n8g5rfz9xk3?cid=storebadge&ocid=badge)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#11100