问题
I have an application that spawns some 20 threads using _beginthreadex
. All of the threads are waiting on a queue to be filled which is a wrapper over the std::queue
: class MyQueue
.The queue
is created as a global variable something like MyQueue processQueue
The front
function of MyQueue
looks something like,
std::unique_lock<std::mutex> mlock(mutex_);
while (queue_.empty())
{
cond_.wait(mlock);
}
auto item = queue_.front();
queue_.pop();
return item;
And push looks like:
std::unique_lock<std::mutex> mlock(mutex_);
queue_.push(item);
mlock.unlock();
cond_.notify_one();
cond_,queue_ and mutex_
are member variable of MyQueue
.
So initially, all the threads are in waiting state. When the queue
has an item, one of the thread reads it and process it.
The problem occurs when the application is closed, gracefully or abruptly. There is a crash in the msvcr120!Concurrency::details::_Condition_variable::notify_all
.
Entire stack from the crash dump
ntdll!TppRaiseInvalidParameter+0x48
ntdll!TpAllocWait+0x6725f
kernel32!CreateThreadpoolWaitStub+0x1a
msvcr120!Concurrency::details::ExternalContextBase::PrepareForUse+0xa1
msvcr120!Concurrency::details::ExternalContextBase::ExternalContextBase+0xa2
msvcr120!Concurrency::details::SchedulerBase::AttachExternalContext+0xcf
Concurrency::details::SchedulerBase::CreateContextFromDefaultScheduler+0xfe
msvcr120!Concurrency::details::SchedulerBase::CurrentContext+0x26
msvcr120!Concurrency::critical_section::scoped_lock::scoped_lock+0x47
msvcr120!Concurrency::details::_Condition_variable::notify_all+0x23
msvcp120!_Cnd_destroy+0x1b
myfunction+0x36c501
msvcr120!doexit+0x145
msvcr120!__CRTDLL_INIT+0xce
ntdll!LdrpCallInitRoutine+0x41
ntdll!LdrShutdownProcess+0x142
ntdll!RtlExitUserProcess+0x78
KERNELBASE!DefaultHandler+0xf
KERNELBASE!CtrlRoutine+0x9b
kernel32!BaseThreadInitThunk+0xd
ntdll!RtlUserThreadStart+0x1d`
Things i tried,
- use
std::unique_ptr
for the condition variable but it didn't fix the issue. - call _endthreadex(0) in the
dtor
of the queue. This fixed the crash on exit, but i dont think this is the correct way as i am not sure which of thread is getting closed.
Any help would be highly appreciated.
回答1:
this is crt bug. crash because std::condition_variable
destructor called from DLL_PROCESS_DETACH
.
MyQueue queue
is created as a global variable andcond_
, are member variable ofMyQueue
because cond_
is global object in DLL - it destructor called on DLL_PROCESS_DETACH
from stack trace clear visible that all begin from _Cnd_destroy
(called from destructor of conditional variable). crt internally call CreateThreadpoolWait. which call TpAllocWait
. at very begin of this api exist next line of code:
if (RtlGetCurrentPeb()->Ldr->ShutdownInProgress) TppRaiseInvalidParameter();
because CreateThreadpoolWait called after ExitProcess
called (look in stack trace ntdll!RtlExitUserProcess
) (in DLL_PROCESS_DETACH
handler) - the ShutdownInProgress
already true
and as result TppRaiseInvalidParameter
called - again clear visible from your stack trace.
here - std::condition_variable destructor crashes on VS2012 another example of this crash. - if you look to stack trace of the crash - you can view that it absolute the same (highlight main points in stack)
ntdll.dll!_NtRaiseException@12
ntdll.dll!_KiUserExceptionDispatcher@8
ntdll.dll!_TpAllocWait@16
kernel32.dll!_CreateThreadpoolWait@12
msvcp110d.dll!_Cnd_destroy(_Cnd_internal_imp_t * * cond) Line 35 C++
ConnectModelUtil.dll!std::condition_variable::~condition_variable() Line 41 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
so only solution here - not use global condition_variable
object in dll - otherwise you got crash on DLL_PROCESS_DETACH
also from std::condition_variable::~condition_variable
The programmer must ensure that no threads attempt to wait on *this once the destructor has been started
but this is not true in your case. your threads terminated during wait on conditional variable cond_.wait(mlock);
. so from conditional variable view - threads still waiting - it data (pointers to it blocks allocated from it stack) linked to conditional variable. you need or somehow unwait all threads before ExitProcess
call - but you can not do this from DLL_PROCESS_DETACH
which is called after this - for this exe must call some dll function before. or not use wait on dll global conditional variable
回答2:
You'll have to wake up the wait for shutdown. cond_.notify_all()
in destructor of your class fix your problem?
回答3:
Compiled code using vc140 using visual studio 2015 and there was no more crash. Looks like visual studio 2013 bug after all.
来源:https://stackoverflow.com/questions/50524156/application-crash-due-to-invalid-parameter-after-call-in-stdcondition-variable