Catch2 icon indicating copy to clipboard operation
Catch2 copied to clipboard

std::cerr redirection hangs on Windows

Open borrrden opened this issue 1 year ago • 1 comments

Describe the bug With shouldRedirectStdOut enabled, Windows can hang on writing to std::cerr

Expected behavior No hangs should happen

Reproduction steps Steps to reproduce the bug.

This is a really obnoxious one to track down but I was able to create a reproduction that reproduces it....sometimes (catch_repro.zip). You can run CMake on it to generate a visual studio project and then run the resulting exe with the -r quiet argument. You'll know if it hits the actual issue if the program is hung and attaching a debugger reveals that it is blocked with the following stack trace (I had better luck today running from the command prompt and then using visual studio to attach a debugger once I saw it hanging):

 	ucrtbased.dll!_lock_file(_iobuf * stream) Line 141	C++
 	msvcp140d.dll!std::basic_filebuf<char,std::char_traits<char>>::_Lock() Line 376	C++
 	catch_repro.exe!std::basic_ostream<char,std::char_traits<char>>::_Sentry_base::_Sentry_base(std::basic_ostream<char,std::char_traits<char>> & _Ostr) Line 78	C++
 	catch_repro.exe!std::basic_ostream<char,std::char_traits<char>>::sentry::sentry(std::basic_ostream<char,std::char_traits<char>> & _Ostr) Line 96	C++
 	catch_repro.exe!std::_Insert_string<char,std::char_traits<char>,unsigned __int64>(std::basic_ostream<char,std::char_traits<char>> & _Ostr, const char * const _Data, const unsigned __int64 _Size) Line 480	C++
 	catch_repro.exe!std::operator<<<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> & _Ostr, const std::basic_string_view<char,std::char_traits<char>> _Str) Line 1782	C++
 	catch_repro.exe!fleece_Catch::CaseListReporter::sectionStarting(const Catch::SectionInfo & _sectionInfo) Line 466	C++
 	catch_repro.exe!Catch::RunContext::sectionStarted(Catch::StringRef sectionName, const Catch::SourceLineInfo & sectionLineInfo, Catch::Counts & assertions) Line 5917	C++
 	catch_repro.exe!Catch::Section::Section(Catch::SectionInfo && info) Line 6316	C++
 	catch_repro.exe!Catch::NWayMethodTestInvoker<`anonymous namespace'::CATCH2_INTERNAL_TEST_2>::invoke() Line 18	C++
	catch_repro.exe!Catch::TestCaseHandle::invoke() Line 7163	C++
 	catch_repro.exe!Catch::RunContext::invokeActiveTestCase() Line 6161	C++
 	catch_repro.exe!Catch::RunContext::runCurrentTest() Line 6124	C++
 	catch_repro.exe!Catch::RunContext::runTest(const Catch::TestCaseHandle & testCase) Line 5822	C++
 	catch_repro.exe!Catch::`anonymous namespace'::TestGroup::execute() Line 1253	C++
 	catch_repro.exe!Catch::Session::runInternal() Line 1473	C++
 	catch_repro.exe!Catch::Session::run() Line 1405	C++
 	catch_repro.exe!Catch::Session::run<char>(int argc, const char * const * argv) Line 4937	C++

Platform information:

  • OS: Windows 10.0.10945 Pro
  • Compiler+version: MSVC 19.41.34120.0
  • Catch version: v3.7.1

Additional context The underlying issue here, in my opinion, is a weird decision by the Microsoft C++ runtime. Whenever you write to some sort of stream, the underlying rdbuf is locked and then unlocked when the write is done. Seems sane enough except that the way it does it is by asking the stream for its rdbuf in the constructor, locking it, and then asking the stream for its rdbuf again in the destructor and unlocking that. My theory is that since Catch is so wildly swapping the rdbuf back and forth, it might be switching the rdbuf between the constructor and destructor while another thread is writing and thus causing the original rdbuf to be locked forever. I experimented by having catch do the same locking when swapping the rdbuf but that did not solve the issue. In fact the only thing that DOES work is commenting out the stopRedirect logic so that only startRedirect actually does anything. Everything I want to be logged is still logged due to Catch using Catch::cout() instead of std::cout to log so I am willing to patch it that way, but this problem is bothering me quite a bit.

Sorry this isn't quite a minimal project as it uses a custom reporter (slightly altered console reporter) and a custom invoker to allow variations on a test in different "modes" but hopefully it is minimal enough.

borrrden avatar Oct 16 '24 04:10 borrrden

We are currently sticking to 3.5.4 since the issue prevents us from using more recent Catch2 in Windows.

m-dot-jung avatar Jul 04 '25 13:07 m-dot-jung