To be clear, this is only an experiment, no one should ever write their code like this, but consider this code:
class Base
{
protected:
std::thread m_thread;
std::mutex m_mutex;
std::condition_variable m_condv;
virtual void DerivedThread() = 0;
public:
Base()
{
m_thread = std::thread(&Base::DerivedThread, this);
std::unique_lock<std::mutex> lck(m_mutex);
m_condv.wait(lck);
}
};
class Derived : Base
{
protected:
void DerivedThread() override
{
m_condv.notify_one();
}
public:
Derived() : Base() {}
~Derived()
{
m_thread.join();
}
};
int main()
{
Derived foo;
return 0;
}
The Base
class constructor will start a thread at the pure virtual method DerivedThread
. It then blocks execution and waits for the conditional variable signal from that thread.
The code above will crash somewhere in the type_traits
library, I haven't succeeded to debug to see what exactly cause the crash, and the stack trace looks weird, this was compiled with MSVC x86_64 in debug mode, /std:c++20 /Od
:
ucrtbased.dll() Unknown
ucrtbased.dll() Unknown
ucrtbased.dll() Unknown
vcruntime140d.dll() Unknown
> TestMain.exe!std::invoke<void (__cdecl Base::*)(void),Base *>(void(Base::*)() && _Obj, Base * && _Arg1) Line 1573 C++
TestMain.exe!std::thread::_Invoke<std::tuple<void (__cdecl Base::*)(void),Base *>,0,1>(void * _RawVals) Line 56 C++
ucrtbased.dll() Unknown
kernel32.dll() Unknown
ntdll.dll() Unknown
Replacing the conditional variable with a simple sleep will also cause this behavior:
std::this_thread::sleep_for(std::chrono::milliseconds(100));
It seems like this was caused by std::thread
when calling _Cnd_do_broadcast_at_thread_exit
(line 6 in the stack trace), but the thread didn't even start yet. Perhaps for some reason, the thread failed to start?
Here is the Godbolt link.