diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index bfa107318d..f3c2e1d835 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -1851,6 +1851,13 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation // - N/A winrt::fire_and_forget TermControl::_TerminalCursorPositionChanged() { + bool expectedFalse{ false }; + if (!_coroutineDispatchStateUpdateInProgress.compare_exchange_weak(expectedFalse, true)) + { + // somebody's already in here. + return; + } + if (_closing.load()) { return; @@ -1858,6 +1865,15 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation auto weakThis{ get_weak() }; + // Muffle 2: Muffle Harder + // If we're the lucky coroutine who gets through, we'll still wait 100ms to clog + // the atomic above so we don't service the cursor update too fast. If we get through + // and finish processing the update quickly but similar requests are still beating + // down the door above in the atomic, we may still update the cursor way more than + // is visible to anyone's eye, which is a waste of effort. + static constexpr auto CursorUpdateQuiesceTime{ std::chrono::milliseconds(100) }; + co_await winrt::resume_after(CursorUpdateQuiesceTime); + co_await winrt::resume_foreground(Dispatcher()); if (auto control{ weakThis.get() }) @@ -1866,6 +1882,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation { TSFInputControl().TryRedrawCanvas(); } + _coroutineDispatchStateUpdateInProgress.store(false); } } diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index cb28cd78ab..d308296555 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -231,6 +231,12 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation void _CompositionCompleted(winrt::hstring text); void _CurrentCursorPositionHandler(const IInspectable& sender, const CursorPositionEventArgs& eventArgs); void _FontInfoHandler(const IInspectable& sender, const FontInfoEventArgs& eventArgs); + + // this atomic is to be used as a guard against dispatching billions of coroutines for + // routine state changes that might happen millions of times a second. + // Unbounded main dispatcher use leads to massive memory leaks and intense slowdowns + // on the UI thread. + std::atomic _coroutineDispatchStateUpdateInProgress{ false }; }; }