Hello,
I have a namespace std::condition_variable object in a DLL, whose destructor crashes sometimes when the DLL is unloaded on process exit:
// one of DLL's cpp files
#include <list> #include <mutex> #include <condition_variable> namespace { std::list<Request> requests_queue; std::mutex queue_mutex; std::condition_variable queue_cv; ... }
The stack trace is attached below; It seems that the destructor of std::condition_variable, in VS2012' implementation, uses some mechanisms from the Concurrency Runtime which introduces some kind of subtle dependency;
The C++ standard requires as the only precondition for this destructor to work as expected that no threads wait on the condition variable: "There are no threads blocked on *this in a call to wait(), wait_for(), or
wait_until()."
I made sure the this precondition is met, but still got the crash. Besides this, by the time destructors of namespace objects in DLLs are called, all threads had already been destroyed by the OS through the ExitProcess() function which first destroys all threads except the main thread and then starts unloading DLLs causing the CRT to call the destructor.
It seems this issue is similar to
this one. However, I'm using only standard C++ classes and no ConcRT features.
Perhaps one could elaborate a bit on how and why std::condition_variable is tied to ConcRT and suggest a workaround for the crash.
The stack trace of the crash:
ntdll.dll!_NtRaiseException@12() Unknown
ntdll.dll!_KiUserExceptionDispatcher@8() Unknown
ntdll.dll!_TpAllocWait@16() Unknown
kernel32.dll!_CreateThreadpoolWait@12() Unknown
msvcr110d.dll!__crtCreateThreadpoolWait(void (_TP_CALLBACK_INSTANCE *, void *, _TP_WAIT *, unsigned long) * pfnwa, void * pv, _TP_CALLBACK_ENVIRON_V1 * pcbe) Line 569 C
msvcr110d.dll!Concurrency::details::RegisterAsyncWaitAndLoadLibrary(void * waitingEvent, void (_TP_CALLBACK_INSTANCE *, void *, _TP_WAIT *, unsigned long) * callback, void * data) Line 675 C++
msvcr110d.dll!Concurrency::details::ExternalContextBase::PrepareForUse(bool explicitAttach) Line 120 C++
msvcr110d.dll!Concurrency::details::SchedulerBase::GetExternalContext(bool explicitAttach) Line 1585 C++
msvcr110d.dll!Concurrency::details::SchedulerBase::AttachExternalContext(bool explicitAttach) Line 1527 C++
msvcr110d.dll!Concurrency::details::SchedulerBase::CreateContextFromDefaultScheduler() Line 569 C++
msvcr110d.dll!Concurrency::details::SchedulerBase::CurrentContext() Line 402 C++
msvcr110d.dll!Concurrency::details::LockQueueNode::LockQueueNode(unsigned int timeout) Line 616 C++
msvcr110d.dll!Concurrency::critical_section::scoped_lock::scoped_lock(Concurrency::critical_section & _Critical_section) Line 1276 C++
msvcr110d.dll!Concurrency::details::_Condition_variable::notify_all() Line 678 C++
msvcr110d.dll!Concurrency::details::_Condition_variable::~_Condition_variable() Line 556 C++
msvcp110d.dll!Concurrency::details::_Condition_variable::`scalar deleting destructor'(unsigned int) C++
msvcp110d.dll!_Cnd_destroy(_Cnd_internal_imp_t * * cond) Line 35 C++
ConnectModelUtil.dll!std::condition_variable::~condition_variable() Line 41 C++
ConnectModelUtil.dll!`anonymous namespace'::`dynamic atexit destructor for 'queue_cv''() C++
ConnectModelUtil.dll!_CRT_INIT(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 416 C
ConnectModelUtil.dll!__DllMainCRTStartup(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 522 C
ConnectModelUtil.dll!_DllMainCRTStartup(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 472 C