Fix the static UTF8OutPipeReader & tests (#1998)
This commit addresses some lingering issues in UTF8OutPipeReader and cleans up its termination logic. It also fixes some issues exposed in the test. Fixes #1997.
This commit is contained in:
parent
de1de4425e
commit
988fe0ba60
|
@ -190,14 +190,14 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||||
|
|
||||||
DWORD ConhostConnection::_OutputThread()
|
DWORD ConhostConnection::_OutputThread()
|
||||||
{
|
{
|
||||||
static UTF8OutPipeReader pipeReader{ _outPipe };
|
UTF8OutPipeReader pipeReader{ _outPipe.get() };
|
||||||
std::string_view strView{};
|
std::string_view strView{};
|
||||||
|
|
||||||
// process the data of the output pipe in a loop
|
// process the data of the output pipe in a loop
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
HRESULT result = pipeReader.Read(strView);
|
HRESULT result = pipeReader.Read(strView);
|
||||||
if (FAILED(result))
|
if (FAILED(result) || result == S_FALSE)
|
||||||
{
|
{
|
||||||
if (_closing.load())
|
if (_closing.load())
|
||||||
{
|
{
|
||||||
|
@ -208,7 +208,8 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
|
||||||
_disconnectHandlers();
|
_disconnectHandlers();
|
||||||
return (DWORD)-1;
|
return (DWORD)-1;
|
||||||
}
|
}
|
||||||
else if (strView.empty())
|
|
||||||
|
if (strView.empty())
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,24 @@
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
UTF8OutPipeReader::UTF8OutPipeReader(wil::unique_hfile& outPipe) :
|
UTF8OutPipeReader::UTF8OutPipeReader(HANDLE outPipe) :
|
||||||
_outPipe{ outPipe }
|
_outPipe{ outPipe }
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Method Description:
|
||||||
|
// Populates a string_view with *complete* UTF-8 codepoints read from the pipe.
|
||||||
|
// If it receives an incomplete codepoint, it will cache it until it can be completed.
|
||||||
|
// Note: This method trusts that the other end will, in fact, send complete codepoints.
|
||||||
|
// Arguments:
|
||||||
|
// - strView: on return, populated with successfully-read codepoints.
|
||||||
|
// Return Value:
|
||||||
|
// An HRESULT indicating whether the read was successful. For the purposes of this
|
||||||
|
// method, a closed pipe is considered a successful (but false!) read. All other errors
|
||||||
|
// are translated into an appropriate status code.
|
||||||
|
// S_OK for a successful read
|
||||||
|
// S_FALSE for a read on a closed pipe
|
||||||
|
// E_* (anything) for a failed read
|
||||||
[[nodiscard]] HRESULT UTF8OutPipeReader::Read(_Out_ std::string_view& strView)
|
[[nodiscard]] HRESULT UTF8OutPipeReader::Read(_Out_ std::string_view& strView)
|
||||||
{
|
{
|
||||||
DWORD dwRead{};
|
DWORD dwRead{};
|
||||||
|
@ -18,7 +31,7 @@ UTF8OutPipeReader::UTF8OutPipeReader(wil::unique_hfile& outPipe) :
|
||||||
|
|
||||||
// in case of early escaping
|
// in case of early escaping
|
||||||
*_buffer = 0;
|
*_buffer = 0;
|
||||||
strView = reinterpret_cast<char*>(_buffer);
|
strView = std::string_view{ reinterpret_cast<char*>(_buffer), 0 };
|
||||||
|
|
||||||
// copy UTF-8 code units that were remaining from the previously read chunk (if any)
|
// copy UTF-8 code units that were remaining from the previously read chunk (if any)
|
||||||
if (_dwPartialsLen != 0)
|
if (_dwPartialsLen != 0)
|
||||||
|
@ -27,19 +40,30 @@ UTF8OutPipeReader::UTF8OutPipeReader(wil::unique_hfile& outPipe) :
|
||||||
}
|
}
|
||||||
|
|
||||||
// try to read data
|
// try to read data
|
||||||
fSuccess = !!ReadFile(_outPipe.get(), &_buffer[_dwPartialsLen], std::extent<decltype(_buffer)>::value - _dwPartialsLen, &dwRead, nullptr);
|
fSuccess = !!ReadFile(_outPipe, &_buffer[_dwPartialsLen], std::extent<decltype(_buffer)>::value - _dwPartialsLen, &dwRead, nullptr);
|
||||||
|
|
||||||
dwRead += _dwPartialsLen;
|
dwRead += _dwPartialsLen;
|
||||||
_dwPartialsLen = 0;
|
_dwPartialsLen = 0;
|
||||||
|
|
||||||
|
if (!fSuccess) // reading failed (we must check this first, because dwRead will also be 0.)
|
||||||
|
{
|
||||||
|
auto lastError = GetLastError();
|
||||||
|
if (lastError == ERROR_BROKEN_PIPE)
|
||||||
|
{
|
||||||
|
// This is a successful, but detectable, exit.
|
||||||
|
// There is a chance that we put some partials into the buffer. Since
|
||||||
|
// the pipe has closed, they're just invalid now. They're not worth
|
||||||
|
// reporting.
|
||||||
|
return S_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return HRESULT_FROM_WIN32(lastError);
|
||||||
|
}
|
||||||
|
|
||||||
if (dwRead == 0) // quit if no data has been read and no cached data was left over
|
if (dwRead == 0) // quit if no data has been read and no cached data was left over
|
||||||
{
|
{
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
else if (!fSuccess) // reading failed
|
|
||||||
{
|
|
||||||
return E_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
const BYTE* const endPtr{ _buffer + dwRead };
|
const BYTE* const endPtr{ _buffer + dwRead };
|
||||||
const BYTE* backIter{ endPtr - 1 };
|
const BYTE* backIter{ endPtr - 1 };
|
||||||
|
|
|
@ -27,12 +27,10 @@ Author(s):
|
||||||
class UTF8OutPipeReader final
|
class UTF8OutPipeReader final
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
UTF8OutPipeReader(wil::unique_hfile& outPipe);
|
UTF8OutPipeReader(HANDLE outPipe);
|
||||||
[[nodiscard]] HRESULT Read(_Out_ std::string_view& strView);
|
[[nodiscard]] HRESULT Read(_Out_ std::string_view& strView);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
wil::unique_hfile& _outPipe;
|
|
||||||
|
|
||||||
enum _Utf8BitMasks : BYTE
|
enum _Utf8BitMasks : BYTE
|
||||||
{
|
{
|
||||||
IsAsciiByte = 0b0'0000000, // Any byte representing an ASCII character has the MSB set to 0
|
IsAsciiByte = 0b0'0000000, // Any byte representing an ASCII character has the MSB set to 0
|
||||||
|
@ -63,6 +61,7 @@ private:
|
||||||
_Utf8BitMasks::IsLeadByteThreeByteSequence,
|
_Utf8BitMasks::IsLeadByteThreeByteSequence,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
HANDLE _outPipe; // non-owning reference to a pipe.
|
||||||
BYTE _buffer[4096]{ 0 }; // buffer for the chunk read
|
BYTE _buffer[4096]{ 0 }; // buffer for the chunk read
|
||||||
BYTE _utf8Partials[4]{ 0 }; // buffer for code units of a partial UTF-8 code point that have to be cached
|
BYTE _utf8Partials[4]{ 0 }; // buffer for code units of a partial UTF-8 code point that have to be cached
|
||||||
DWORD _dwPartialsLen{}; // number of cached UTF-8 code units
|
DWORD _dwPartialsLen{}; // number of cached UTF-8 code units
|
||||||
|
|
|
@ -109,19 +109,17 @@ class UTF8OutPipeReaderTests
|
||||||
// Performs the sub-tests.
|
// Performs the sub-tests.
|
||||||
HRESULT RunTest(std::string& utf8TestString)
|
HRESULT RunTest(std::string& utf8TestString)
|
||||||
{
|
{
|
||||||
HANDLE readFrom{ INVALID_HANDLE_VALUE }, writeTo{ INVALID_HANDLE_VALUE }; // pipe handles
|
|
||||||
SECURITY_ATTRIBUTES sa{ sizeof(SECURITY_ATTRIBUTES) };
|
|
||||||
|
|
||||||
std::string_view strView{}; // contains the chunk that we get from UTF8OutPipeReader::Read
|
std::string_view strView{}; // contains the chunk that we get from UTF8OutPipeReader::Read
|
||||||
const winrt::hstring utf16Expected{ winrt::to_hstring(utf8TestString) }; // contains the whole string converted to UTF-16
|
const winrt::hstring utf16Expected{ winrt::to_hstring(utf8TestString) }; // contains the whole string converted to UTF-16
|
||||||
winrt::hstring utf16Actual{}; // will be concatenated from the converted chunks
|
winrt::hstring utf16Actual{}; // will be concatenated from the converted chunks
|
||||||
|
|
||||||
CreatePipe(&readFrom, &writeTo, &sa, 0); // create the pipe handles
|
wil::unique_hfile outPipe{};
|
||||||
|
wil::unique_hfile inPipe{};
|
||||||
|
|
||||||
wil::unique_hfile outPipe{ readFrom };
|
SECURITY_ATTRIBUTES sa{ sizeof(SECURITY_ATTRIBUTES) };
|
||||||
wil::unique_hfile inPipe{ writeTo };
|
CreatePipe(&outPipe, &inPipe, &sa, 0); // create the pipe handles
|
||||||
|
|
||||||
static UTF8OutPipeReader reader{ outPipe }; // declare a static instance of UTF8OutPipeReader
|
UTF8OutPipeReader reader{ outPipe.get() };
|
||||||
|
|
||||||
ThreadData data{ inPipe, utf8TestString };
|
ThreadData data{ inPipe, utf8TestString };
|
||||||
|
|
||||||
|
@ -132,7 +130,7 @@ class UTF8OutPipeReaderTests
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
// get a chunk of UTF-8 data
|
// get a chunk of UTF-8 data
|
||||||
RETURN_IF_FAILED(reader.Read(strView));
|
THROW_IF_FAILED(reader.Read(strView));
|
||||||
|
|
||||||
if (strView.empty())
|
if (strView.empty())
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue