Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

std::cerr redirection hangs on Windows #2921

Open
borrrden opened this issue Oct 16, 2024 · 0 comments
Open

std::cerr redirection hangs on Windows #2921

borrrden opened this issue Oct 16, 2024 · 0 comments

Comments

@borrrden
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant