retool the retry loop

This commit is contained in:
Dustin L. Howett 2025-12-09 15:41:28 -06:00
parent 19433ee514
commit 0037507bb8

View File

@ -12,8 +12,8 @@ using namespace Microsoft::Console::Types;
using PointTree = interval_tree::IntervalTree<til::point, size_t>; using PointTree = interval_tree::IntervalTree<til::point, size_t>;
static constexpr TimerRepr TimerReprMax = std::numeric_limits<TimerRepr>::max(); static constexpr TimerRepr TimerReprMax = std::numeric_limits<TimerRepr>::max();
// We want there to be five retry periods; on the last one, we will mark the render as failed. // We want there to be five retry periods; after the last one, we will mark the render as failed.
static constexpr DWORD maxRetriesForRenderEngine = 6; static constexpr DWORD maxRetriesForRenderEngine = 5;
// The renderer will wait this number of milliseconds * 2^tries before trying again. // The renderer will wait this number of milliseconds * 2^tries before trying again.
static constexpr DWORD renderBackoffBaseTimeMilliseconds = 100; static constexpr DWORD renderBackoffBaseTimeMilliseconds = 100;
@ -327,21 +327,29 @@ DWORD Renderer::_timerToMillis(TimerRepr t) noexcept
// - HRESULT S_OK, GDI error, Safe Math error, or state/argument errors. // - HRESULT S_OK, GDI error, Safe Math error, or state/argument errors.
[[nodiscard]] HRESULT Renderer::PaintFrame() [[nodiscard]] HRESULT Renderer::PaintFrame()
{ {
auto tries = maxRetriesForRenderEngine; HRESULT hr{ S_FALSE };
while (tries > 0) 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 // BODGY: Optimally we would want to retry per engine, but that causes different
// problems (intermittent inconsistent states between text renderer and UIA output, // problems (intermittent inconsistent states between text renderer and UIA output,
// not being able to lock the cursor location, etc.). // not being able to lock the cursor location, etc.).
const auto hr = _PaintFrame(); hr = _PaintFrame();
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
break; break;
} }
LOG_HR_IF(hr, hr != E_PENDING); LOG_HR_IF(hr, hr != E_PENDING);
}
if (--tries == 0) if (FAILED(hr))
{ {
// Stop trying. // Stop trying.
_disablePainting(); _disablePainting();
@ -352,15 +360,10 @@ DWORD Renderer::_timerToMillis(TimerRepr t) noexcept
// If there's no callback, we still don't want to FAIL_FAST: the renderer going black // 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 // isn't near as bad as the entire application aborting. We're a component. We shouldn't
// abort applications that host us. // abort applications that host us.
return S_FALSE; hr = S_FALSE;
} }
// Add a bit of backoff. return hr;
// Sleep 100, 200, 400, 600, 800, 1600ms before failing out and disabling the renderer.
Sleep(renderBackoffBaseTimeMilliseconds * (1 << (maxRetriesForRenderEngine - tries - 1)));
}
return S_OK;
} }
[[nodiscard]] HRESULT Renderer::_PaintFrame() noexcept [[nodiscard]] HRESULT Renderer::_PaintFrame() noexcept