From 0037507bb84f23f05a6502b3c053dc3894bb6afa Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Tue, 9 Dec 2025 15:41:28 -0600 Subject: [PATCH] retool the retry loop --- src/renderer/base/renderer.cpp | 51 ++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp index db2a5100d8..ce7c5fd240 100644 --- a/src/renderer/base/renderer.cpp +++ b/src/renderer/base/renderer.cpp @@ -12,8 +12,8 @@ using namespace Microsoft::Console::Types; using PointTree = interval_tree::IntervalTree; static constexpr TimerRepr TimerReprMax = std::numeric_limits::max(); -// We want there to be five retry periods; on the last one, we will mark the render as failed. -static constexpr DWORD maxRetriesForRenderEngine = 6; +// We want there to be five retry periods; after the last one, we will mark the render as failed. +static constexpr DWORD maxRetriesForRenderEngine = 5; // The renderer will wait this number of milliseconds * 2^tries before trying again. static constexpr DWORD renderBackoffBaseTimeMilliseconds = 100; @@ -327,40 +327,43 @@ DWORD Renderer::_timerToMillis(TimerRepr t) noexcept // - HRESULT S_OK, GDI error, Safe Math error, or state/argument errors. [[nodiscard]] HRESULT Renderer::PaintFrame() { - auto tries = maxRetriesForRenderEngine; - while (tries > 0) + HRESULT hr{ S_FALSE }; + for (auto attempt = 0; attempt < maxRetriesForRenderEngine; ++attempt) { + if (attempt > 0) [[unlikely]] + { + // Add a bit of backoff. + // Sleep 100, 200, 400, 600, 800ms before failing out and disabling the renderer. + Sleep(renderBackoffBaseTimeMilliseconds * (1 << (attempt - 1))); + } + // BODGY: Optimally we would want to retry per engine, but that causes different // problems (intermittent inconsistent states between text renderer and UIA output, // not being able to lock the cursor location, etc.). - const auto hr = _PaintFrame(); + hr = _PaintFrame(); if (SUCCEEDED(hr)) { break; } LOG_HR_IF(hr, hr != E_PENDING); - - if (--tries == 0) - { - // Stop trying. - _disablePainting(); - if (_pfnRendererEnteredErrorState) - { - _pfnRendererEnteredErrorState(); - } - // If there's no callback, we still don't want to FAIL_FAST: the renderer going black - // isn't near as bad as the entire application aborting. We're a component. We shouldn't - // abort applications that host us. - return S_FALSE; - } - - // Add a bit of backoff. - // Sleep 100, 200, 400, 600, 800, 1600ms before failing out and disabling the renderer. - Sleep(renderBackoffBaseTimeMilliseconds * (1 << (maxRetriesForRenderEngine - tries - 1))); } - return S_OK; + if (FAILED(hr)) + { + // Stop trying. + _disablePainting(); + if (_pfnRendererEnteredErrorState) + { + _pfnRendererEnteredErrorState(); + } + // If there's no callback, we still don't want to FAIL_FAST: the renderer going black + // isn't near as bad as the entire application aborting. We're a component. We shouldn't + // abort applications that host us. + hr = S_FALSE; + } + + return hr; } [[nodiscard]] HRESULT Renderer::_PaintFrame() noexcept