Fix a hang when writing wide glyphs into a 1 column terminal (#15171)

The code changes are mostly self-explanatory: Just skip glyphs
that can never be inserted. I implemented it slightly incorrectly
(a newline will be inserted every time you write such a wide glyph),
but it's a niche issue and I think the simplicity of the fix is
more important than its exact correctness.

It also contains a fix for some severe log spam due to
`_PrepareForDoubleByteSequence` complaining in this situation.
The spam is so bad that it freezes the app for a few seconds
during text buffer reflow.

Closes #7416

## Validation Steps Performed
* Open an extra pane and run `TerminalStress.exe` in there
* Resize to 1 column
* Doesn't hang 
This commit is contained in:
Leonard Hecker
2023-04-14 23:15:08 +02:00
committed by GitHub
parent eb725e9993
commit 1d354d0f5c
2 changed files with 16 additions and 12 deletions

View File

@@ -272,10 +272,6 @@ bool TextBuffer::_AssertValidDoubleByteSequence(const DbcsAttribute dbcsAttribut
// - false otherwise (out of memory)
bool TextBuffer::_PrepareForDoubleByteSequence(const DbcsAttribute dbcsAttribute)
{
// This function corrects most errors. If this is false, we had an uncorrectable one which
// older versions of conhost simply let pass by unflinching.
LOG_HR_IF(E_NOT_VALID_STATE, !(_AssertValidDoubleByteSequence(dbcsAttribute))); // Shouldn't be uncorrectable sequences unless something is very wrong.
auto fSuccess = true;
// Now compensate if we don't have enough space for the upcoming double byte sequence
// We only need to compensate for leading bytes

View File

@@ -139,18 +139,26 @@ void AdaptDispatch::_WriteToBuffer(const std::wstring_view string)
if (isWrapping)
{
// We want to wrap, but we failed to write even a single character into the row.
// ROW::Write() returns the lineWidth and leaves stringIterator untouched. To prevent a
// deadlock, because stringIterator never advances, we need to throw that glyph away.
//
// This can happen under two circumstances:
// * The glyph is wider than the buffer and can never be inserted in
// the first place. There's no good way to detect this, so we check
// whether the begin column is the left margin, which is the column
// at which any legit insertion should work at a minimum.
// * The DECAWM Autowrap mode is disabled ("\x1b[?7l", !wrapAtEOL) and
// we tried writing a wide glyph into the last column which can't work.
if (textPositionBefore == textPositionAfter && (state.columnBegin == 0 || !wrapAtEOL))
{
textBuffer.ConsumeGrapheme(state.text);
}
if (wrapAtEOL)
{
cursor.DelayEOLWrap();
}
else if (textPositionBefore == textPositionAfter)
{
// We want to wrap, but we're not allowed to and we failed to write even a single character into the row.
// This can only mean one thing! The DECAWM Autowrap mode is disabled ("\x1b[?7l") and we tried writing a
// wide glyph into the last column. ROW::Write() returns the lineWidth and leaves stringIterator untouched.
// To prevent a deadlock, because stringIterator never advances, we need to throw that glyph away.
textBuffer.ConsumeGrapheme(state.text);
}
}
}