Is static local variables safe for a class that creates/uses std::threads?
Because when I use something like this:
logger& logger::get_instance(void)
{
static logger lg;
return lg;
}
And try to exit (force close) the executable, it crashes/exits improperly (somethings the Visual Studio 2012 debugger even crashes).
When I don't do that, the program exits gracefully when i force close.
Here's the stack call when it crashes
ntdll.dll!77c10dbd() Unknown
[Frames below may be incorrect and/or missing, no symbols loaded for ntdll.dll]
ntdll.dll!77b7bfdc() Unknown
kernel32.dll!75b55bab() 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::ExternalContextBase::ExternalContextBase(Concurrency::details::SchedulerBase * pScheduler, bool explicitAttach) Line 52 C++
msvcr110d.dll!Concurrency::details::SchedulerBase::GetExternalContext(bool explicitAttach) Line 1579 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::lock() Line 1017 C++
msvcp110d.dll!mtx_do_lock(_Mtx_internal_imp_t * * mtx, const xtime * target) Line 65 C++
msvcp110d.dll!_Mtx_lock(_Mtx_internal_imp_t * * mtx) Line 144 C++
escobar.exe!std::_Mtx_lockX(_Mtx_internal_imp_t * * _Mtx) Line 68 C++
escobar.exe!std::_Mutex_base::lock() Line 43 C++
escobar.exe!std::unique_lock<std::mutex>::unique_lock<std::mutex>(std::mutex & _Mtx) Line 228 C++
escobar.exe!escobar::utilities::blocking_queue<escobar::logging::log_message *>::interrupt() Line 71 C++
escobar.exe!escobar::logging::log_worker::~log_worker() Line 17 C++
escobar.exe!escobar::logging::log_worker::`scalar deleting destructor'(unsigned int) C++
escobar.exe!escobar::logging::logger::close() Line 72 C++
escobar.exe!escobar::logging::logger::~logger() Line 27 C++
escobar.exe!`escobar::logging::logger::get_instance'::`2'::`dynamic atexit destructor for 'lg''() C++
msvcr110d.dll!doexit(int code, int quick, int retcaller) Line 585 C
msvcr110d.dll!_cexit() Line 410 C
msvcr110d.dll!__CRTDLL_INIT(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 296 C
msvcr110d.dll!_CRTDLL_INIT(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 210 C
ntdll.dll!77bb2846() Unknown
ntdll.dll!77bb2893() Unknown
ntdll.dll!77bc09c8() Unknown
ntdll.dll!77bc08ad() Unknown
KernelBase.dll!75525bbb() Unknown
KernelBase.dll!75525c51() Unknown
kernel32.dll!75b58543() Unknown
ntdll.dll!77bbac69() Unknown
ntdll.dll!77bbac3c() Unknown
Here are a couple of the functoin
log_worker::~log_worker(void)
{
this->queue.interrupt();
service.join();
}
void log_worker::run(void)
{
while (true)
{
log_message* msg;
if (this->queue.dequeue(msg) == false)
break;
this->lg->print_log_message(msg);
delete msg;
}
}
bool dequeue(T& item)
{
std::unique_lock<std::mutex> lock(m);
// handles spurious wakeups
while (!this->data_available && !this->interrupted)
cv.wait(lock);
if (this->interrupted)
return false;
item = std::move(this->front());
this->pop();
if (this->empty())
this->data_available = false;
return true;
}
void interrupt(void)
{
std::unique_lock<std::mutex> lock(m);
this->interrupted = true;
cv.notify_all();
printf("notified threads...\n");
}
Looks like you have a detached thread SchedulerBase(could be any scheduled thread which still uses logger) which is still running while your application is stopped and logger is destroyed which caused the crash.
And log_worker is dynamically allocated in the logger class.
You need to make sure all threads who use logger instance are shutdown properly before logger is destroyed
delete log_worker in logger destructor
I simply had to stop deleting things on shutdown. When you close the console using the 'X' button, it's not a proper shutdown, so It's pointless trying to shutdown threads.
Related
Hello stackoverflow community,
first time asking.
I'm working on a prototype for modeling CAD data in VR with the Unreal Engine.
I have managed to get my Unreal project to build with my CAD System Siemens NX, or rather the API NXOpen.
Overall the API works and I can call NXOpen functions and can create basic objects
But some operations cause crashes and my guess is that it has something to do with memory management.
I cant wrap my head around what exactly is happening.
For example I have code that creates a NX block feature that works fine.
Afterwards I'll need a body object that is stored inside the block feature. The access also works, but when 'bodies' runs out of scope, it crashes.
Similar crashes happen when accessing all kinds of data within NX features. This dummy code triggers the crash quite often:
// 'block' gets created here
if (block) {
std::vector<NXOpen::Body*> bodies = block->GetBodies();
if (bodies.size() > 0) {
NXOpen::Body* body = bodies.front();
}
} // <-- this line crashes
Exception thrown: read access violation.
**flag** was 0xFFFFFFFFFFFFFFFF. occurred
[Inline Frame] UE4Editor-Core.dll!__TBB_machine_cmpswp1(volatile void *) Line 69 C++
> [Inline Frame] UE4Editor-Core.dll!__TBB_TryLockByte(unsigned char &) Line 917 C++
UE4Editor-Core.dll!__TBB_LockByte(unsigned char & flag) Line 924 C++
[Inline Frame] UE4Editor-Core.dll!MallocMutex::scoped_lock::{ctor}(MallocMutex &) Line 66 C++
UE4Editor-Core.dll!rml::internal::Block::freePublicObject(rml::internal::FreeObject * objectToFree) Line 1382 C++
[Inline Frame] UE4Editor-Core.dll!rml::internal::internalPoolFree(rml::internal::MemoryPool * memPool, void *) Line 2571 C++
UE4Editor-Core.dll!rml::internal::internalFree(void * object) Line 2595 C++
UE4Editor-Core.dll!FMemory::Free(void * Original) Line 76 C++
UE4Editor-GestEnUE-Win64-DebugGame.dll!operator delete(void * Ptr, unsigned __int64 Size) Line 6 C++
[External Code]
UE4Editor-GestEnUE-Win64-DebugGame.dll!UNXConnection::AddPointSetsToBody(const FString & name) Line 268 C++
UE4Editor-GestEnUE-Win64-DebugGame.dll!ANXPart::AddCylinder(FVector pos, FVector axis, float height, float diameter, bool WorldSpace) Line 103 C++
UE4Editor-GestEnUE-Win64-DebugGame.dll!ANXPart::execAddCylinder(UObject * Context, FFrame & Stack, void * const Z_Param__Result) Line 18 C++
UE4Editor-CoreUObject.dll!UFunction::Invoke(UObject * Obj, FFrame & Stack, void * const Z_Param__Result) Line 4643 C++
UE4Editor-CoreUObject.dll!UObject::CallFunction(FFrame & Stack, void * const Z_Param__Result, UFunction * Function) Line 904 C++
[Inline Frame] UE4Editor-CoreUObject.dll!FFrame::Step(UObject *) Line 319 C++
UE4Editor-CoreUObject.dll!UObject::ProcessContextOpcode(FFrame & Stack, void * const Z_Param__Result, bool bCanFailSilently) Line 2314 C++
[Inline Frame] UE4Editor-CoreUObject.dll!FFrame::Step(UObject *) Line 319 C++
UE4Editor-CoreUObject.dll!UObject::execLetObj(UObject * Context, FFrame & Stack, void * const Z_Param__Result) Line 2136 C++
[Inline Frame] UE4Editor-CoreUObject.dll!FFrame::Step(UObject *) Line 319 C++
UE4Editor-CoreUObject.dll!ProcessLocalScriptFunction(UObject * Context, FFrame & Stack, void * const Z_Param__Result) Line 974 C++
UE4Editor-CoreUObject.dll!ProcessScriptFunction<void (__cdecl*)(UObject *,FFrame &,void *)>(UObject * Context, UFunction * Function, FFrame & Stack, void * const Z_Param__Result, void(*)(UObject *, FFrame &, void *) ExecFtor) Line 809 C++
UE4Editor-CoreUObject.dll!ProcessLocalFunction(UObject * Context, UFunction * Fn, FFrame & Stack, void * const Z_Param__Result) Line 1033 C++
[Inline Frame] UE4Editor-CoreUObject.dll!FFrame::Step(UObject *) Line 319 C++
UE4Editor-CoreUObject.dll!ProcessLocalScriptFunction(UObject * Context, FFrame & Stack, void * const Z_Param__Result) Line 974 C++
UE4Editor-CoreUObject.dll!ProcessScriptFunction<void (__cdecl*)(UObject *,FFrame &,void *)>(UObject * Context, UFunction * Function, FFrame & Stack, void * const Z_Param__Result, void(*)(UObject *, FFrame &, void *) ExecFtor) Line 809 C++
UE4Editor-CoreUObject.dll!ProcessLocalFunction(UObject * Context, UFunction * Fn, FFrame & Stack, void * const Z_Param__Result) Line 1033 C++
[Inline Frame] UE4Editor-CoreUObject.dll!FFrame::Step(UObject *) Line 319 C++
UE4Editor-CoreUObject.dll!UObject::ProcessContextOpcode(FFrame & Stack, void * const Z_Param__Result, bool bCanFailSilently) Line 2314 C++
[Inline Frame] UE4Editor-CoreUObject.dll!FFrame::Step(UObject *) Line 319 C++
UE4Editor-CoreUObject.dll!ProcessLocalScriptFunction(UObject * Context, FFrame & Stack, void * const Z_Param__Result) Line 974 C++
UE4Editor-CoreUObject.dll!ProcessScriptFunction<void (__cdecl*)(UObject *,FFrame &,void *)>(UObject * Context, UFunction * Function, FFrame & Stack, void * const Z_Param__Result, void(*)(UObject *, FFrame &, void *) ExecFtor) Line 809 C++
UE4Editor-CoreUObject.dll!ProcessLocalFunction(UObject * Context, UFunction * Fn, FFrame & Stack, void * const Z_Param__Result) Line 1033 C++
[Inline Frame] UE4Editor-CoreUObject.dll!FFrame::Step(UObject *) Line 319 C++
UE4Editor-CoreUObject.dll!ProcessLocalScriptFunction(UObject * Context, FFrame & Stack, void * const Z_Param__Result) Line 974 C++
UE4Editor-CoreUObject.dll!UObject::ProcessInternal(UObject * Context, FFrame & Stack, void * const Z_Param__Result) Line 1058 C++
UE4Editor-CoreUObject.dll!UFunction::Invoke(UObject * Obj, FFrame & Stack, void * const Z_Param__Result) Line 4643 C++
UE4Editor-CoreUObject.dll!UObject::ProcessEvent(UFunction * Function, void * Parms) Line 1464 C++
UE4Editor-Engine.dll!AActor::ProcessEvent(UFunction * Function, void * Parameters) Line 769 C++
UE4Editor-GestEnUE-Win64-DebugGame.dll!ABuilderBase::commit() Line 37 C++
UE4Editor-GestEnUE-Win64-DebugGame.dll!AGestEnLeapPawn::Tick(float DeltaTime) Line 138 C++
UE4Editor-Engine.dll!AActor::TickActor(float DeltaSeconds, ELevelTick TickType, FActorTickFunction & ThisTickFunction) Line 970 C++
UE4Editor-Engine.dll!FActorTickFunction::ExecuteTick(float DeltaTime, ELevelTick TickType, ENamedThreads::Type CurrentThread, const TRefCountPtr<FGraphEvent> & MyCompletionGraphEvent) Line 164 C++
[Inline Frame] UE4Editor-Engine.dll!FTickFunctionTask::DoTask(ENamedThreads::Type) Line 285 C++
UE4Editor-Engine.dll!TGraphTask<FTickFunctionTask>::ExecuteTask(TArray<FBaseGraphTask *,FDefaultAllocator> & NewTasks, ENamedThreads::Type CurrentThread) Line 842 C++
[Inline Frame] UE4Editor-Core.dll!FBaseGraphTask::Execute(TArray<FBaseGraphTask *,FDefaultAllocator> & CurrentThread, ENamedThreads::Type) Line 511 C++
UE4Editor-Core.dll!FNamedTaskThread::ProcessTasksNamedThread(int QueueIndex, bool bAllowStall) Line 686 C++
UE4Editor-Core.dll!FNamedTaskThread::ProcessTasksUntilQuit(int QueueIndex) Line 582 C++
[Inline Frame] UE4Editor-Core.dll!FTaskGraphImplementation::ProcessThreadUntilRequestReturn(ENamedThreads::Type) Line 1406 C++
UE4Editor-Core.dll!FTaskGraphImplementation::WaitUntilTasksComplete(const TArray<TRefCountPtr<FGraphEvent>,TInlineAllocator<4,FDefaultAllocator> > & Tasks, ENamedThreads::Type CurrentThreadIfKnown) Line 1457 C++
UE4Editor-Engine.dll!FTickTaskSequencer::ReleaseTickGroup(ETickingGroup WorldTickGroup, bool bBlockTillComplete) Line 557 C++
UE4Editor-Engine.dll!FTickTaskManager::RunTickGroup(ETickingGroup Group, bool bBlockTillComplete) Line 1505 C++
UE4Editor-Engine.dll!UWorld::RunTickGroup(ETickingGroup Group, bool bBlockTillComplete) Line 789 C++
UE4Editor-Engine.dll!UWorld::Tick(ELevelTick TickType, float DeltaSeconds) Line 1568 C++
UE4Editor-UnrealEd.dll!UEditorEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 1618 C++
UE4Editor-UnrealEd.dll!UUnrealEdEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 403 C++
UE4Editor-Win64-DebugGame.exe!FEngineLoop::Tick() Line 3967 C++
[Inline Frame] UE4Editor-Win64-DebugGame.exe!EngineTick() Line 62 C++
UE4Editor-Win64-DebugGame.exe!GuardedMain(const wchar_t * CmdLine, HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, int nCmdShow) Line 168 C++
UE4Editor-Win64-DebugGame.exe!WinMain(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * __formal, int nCmdShow) Line 261 C++
If I understand the callstack correctly the error occurs when the object is deleted. But why can that be the problem?
Could it be that both Unreal and the NX third party lib try to manage the memory?
Has anyone an idea what could cause these crashed and even an idea how to solve it?
I'm happy to give more information if necessary.
Greetings,
LaRanaRica
I found that using the debug runtime library often can cause memory errors when using NXOpen.
You can change this with the linker setting /MD (indstead of /MDd).
I know this is late, but I have a much better solution than to build in Release mode instead of Debug mode, because I've had this issue many times before and solved it using the same simple solution.
I don't know exactly why this occurs with NX Open C++, but it only happens when using std::vector (which some NX Open C++ APIs return) and Debug mode, together. You can fix it by adding this line to the top of your C++ source file, before all the #includes:
#define _ITERATOR_DEBUG_LEVEL 0
I have a slot:
void Foo::func(QString str1, const QString& str2, int width, int height)
{
std::unique_lock<std::mutex> _lock(m_mutex);
#ifdef _DEBUG
MEMORYSTATUSEX statex;
statex.dwLength = sizeof (statex);
if (GlobalMemoryStatusEx(&statex)) {
qDebug() << QString("There are %1 free MB of physical memory.\n").arg(statex.ullAvailPhys / 1024 / 1024);
}
#endif
BaseClass::someFunc(
str1.toStdString(),
str2.toUtf8().constData(),
width,
height
);
}
It seems to be correct and it works. But if program works for a long time (night for example) it crashes in this function at line
str2.toUtf8().constData()
I first thought that this is a thread-based error, but my lock didn't work. All the local variables and class members are ok. The crash error is:
First-chance exception at 0x76E45B68 foo.exe: Microsoft C++ exception: std::bad_alloc at memory location 0x00EFC6EC
But from the code above last MEMORYSTATUSEX show that available memory is
There are 2314 free MB of physical memory
I also thought that str2 is too big, but it works fine with length of 49152, 168441 etc. What is the problem? Did I miss something?
Stack Trace:
KernelBase.dll!_RaiseException#16() Unknown
[External Code]
Qt5Cored.dll!qBadAlloc() Line 2849 C++
Qt5Cored.dll!QByteArray::QByteArray(int size, Qt::Initialization __formal) Line 1409 C++
Qt5Cored.dll!QUtf8::convertFromUnicode(const QChar * uc, int len) Line 151 C++
Qt5Cored.dll!QString::toUtf8_helper(const QString & str) Line 4373 C++
Qt5Cored.dll!QString::toUtf8() Line 56 C++
foo.exe!Foo::func(QString str1, const QString & str2, int width, int height) Line 3600 C++
foo.exe!Foo::qt_static_metacall(QObject * _o, QMetaObject::Call _c, int _id, void * * _a) Line 1126 C++
Qt5Cored.dll!QMetaObject::activate(QObject * sender, int signalOffset, int local_signal_index, void * * argv) Line 3717 C++
Qt5Cored.dll!QMetaObject::activate(QObject * sender, const QMetaObject * m, int local_signal_index, void * * argv) Line 3582 C++
foo.exe!SomeClass::func(QString _t1, const QString & _t2, int _t3, int _t4) Line 137 C++
foo.exe!SomeClass::qt_static_metacall(QObject * _o, QMetaObject::Call _c, int _id, void * * _a) Line 81 C++
Qt5Cored.dll!QMetaCallEvent::placeMetaCall(QObject * object) Line 485 C++
Qt5Cored.dll!QObject::event(QEvent * e) Line 1246 C++
Qt5Widgetsd.dll!QApplicationPrivate::notify_helper(QObject * receiver, QEvent * e) Line 3720 C++
Qt5Widgetsd.dll!QApplication::notify(QObject * receiver, QEvent * e) Line 3164 C++
Qt5Cored.dll!QCoreApplication::notifyInternal(QObject * receiver, QEvent * event) Line 935 C++
Qt5Cored.dll!QCoreApplication::sendEvent(QObject * receiver, QEvent * event) Line 228 C++
Qt5Cored.dll!QCoreApplicationPrivate::sendPostedEvents(QObject * receiver, int event_type, QThreadData * data) Line 1552 C++
Qt5Cored.dll!QCoreApplication::sendPostedEvents(QObject * receiver, int event_type) Line 1410 C++
qwindowsd.dll!QWindowsGuiEventDispatcher::sendPostedEvents() Line 81 C++
Qt5Cored.dll!qt_internal_proc(HWND__ * hwnd, unsigned int message, unsigned int wp, long lp) Line 414 C++
[External Code]
Qt5Cored.dll!QEventDispatcherWin32::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 807 C++
qwindowsd.dll!QWindowsGuiEventDispatcher::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 73 C++
Qt5Cored.dll!QEventLoop::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 129 C++
Qt5Cored.dll!QEventLoop::exec(QFlags<enum QEventLoop::ProcessEventsFlag> flags) Line 204 C++
Qt5Cored.dll!QCoreApplication::exec() Line 1188 C++
Qt5Guid.dll!QGuiApplication::exec() Line 1508 C++
Qt5Widgetsd.dll!QApplication::exec() Line 2957 C++
foo.exe!WinMain(HINSTANCE__ * __formal, HINSTANCE__ * __formal, char * lpCmdLine, int __formal) Line 348 C++
[External Code]
Thanks in advance!
For those who, like me, just stumbled on this thread because they wanted to convert a QString into a char * with a call to str.toUtf8().constData() or str.toUtf8().data(), but that was leading a program to crash or to throw an exception like EXC_BAD_ACCESS on XCode, here's the answer:
in a call like foo = str.toUtf8().constData();, the str.toUtf8() part creates a temp object.
a call to tmp.data() or tmp.constData() return a pointer to an internal buffer managed by the object itself, and whose lifetime is tied to the temp object's lifetime. source.
once that statement is executed, the lifetime of the object whose address was assigned to foo, once returned by data() or constData(), will end.
therefore, as expected, after that statement foo will hold an address to an object whose lifetime ended, thus causing crashes and exceptions.
This happens with the MSVC 2013 compiler. I have a solution with 2 projects: .exe and .dll.
The .exe source code:
extern "C" int test_dll();
int main() {
test_dll();
return 0;
}
The .dll source code:
#include <mutex>
struct AAAA {
AAAA() {
std::mutex mutex;
if (mutex.try_lock()) { // always blocks
mutex.unlock();
}
};
} globalObject;
extern "C" __declspec(dllexport)
int test_dll() {
return true;
}
The try_lock call never returns. Here's the call stack:
msvcr120d.dll!Concurrency::details::ThreadScheduler::Create(const Concurrency::SchedulerPolicy & policy) Line 34
msvcr120d.dll!Concurrency::details::SchedulerBase::CreateWithoutInitializing(const Concurrency::SchedulerPolicy & policy) Line 285
msvcr120d.dll!Concurrency::details::SchedulerBase::GetDefaultScheduler() Line 654
msvcr120d.dll!Concurrency::details::SchedulerBase::CreateContextFromDefaultScheduler() Line 571
msvcr120d.dll!Concurrency::details::SchedulerBase::CurrentContext() Line 399
msvcr120d.dll!Concurrency::details::LockQueueNode::LockQueueNode(unsigned int timeout) Line 619
msvcr120d.dll!Concurrency::critical_section::try_lock() Line 1049
msvcp120d.dll!mtx_do_lock(_Mtx_internal_imp_t * * mtx, const xtime * target) Line 75
msvcp120d.dll!_Mtx_trylock(_Mtx_internal_imp_t * * mtx) Line 162
MutexDll.dll!std::_Mtx_trylockX(_Mtx_internal_imp_t * * _Mtx) Line 73
MutexDll.dll!std::_Mutex_base::try_lock() Line 46
MutexDll.dll!AAAA::AAAA() Line 8
MutexDll.dll!`dynamic initializer for 'AAA''() Line 21
[External Code]
This doesn't happen if the global object is created in the main .exe project. Only when in the DLL. What's going on here?
The hang doesn't occur with the MSVC2015 compiler (v140 toolset).
i try to capture in visual studio 2012 express Invalid address
in very simple code , how can i capture it .
This is what im getting when trying to run the code :
HEAP[match2_new.win32.exe]: Invalid address specified to RtlValidateHeap( 002C0000, 02E43AA8 )
match2_new.win32.exe has triggered a breakpoint.
this is the stack:
msvcr110d.dll!_CrtIsValidHeapPointer(const void * pUserData) Line 2036 C++
msvcr110d.dll!_free_dbg_nolock(void * pUserData, int nBlockUse) Line 1322 C++
msvcr110d.dll!_free_dbg(void * pUserData, int nBlockUse) Line 1265 C++
msvcr110d.dll!operator delete(void * pUserData) Line 54 C++
match2_new.win32.exe!std::allocator<std::_Container_proxy>::deallocate(std::_Container_proxy * _Ptr, unsigned int __formal) Line 586 C++
match2_new.win32.exe!std::_String_alloc<0,std::_String_base_types<char,std::allocator<char> > >::_Free_proxy() Line 683 C++
match2_new.win32.exe!std::_String_alloc<0,std::_String_base_types<char,std::allocator<char> > >::~_String_alloc<0,std::_String_base_types<char,std::allocator<char> > >() Line 656 C++
match2_new.win32.exe!std::basic_string<char,std::char_traits<char>,std::allocator<char> >::~basic_string<char,std::char_traits<char>,std::allocator<char> >() Line 965 C++
> match2_new.win32.exe!GameController::setGemsInBoard(int levlnumber) Line 98 C++
match2_new.win32.exe!GameLayer::startGame() Line 53 C++
match2_new.win32.exe!GameScene::init() Line 43 C++
match2_new.win32.exe!GameScene::create() Line 24 C++
match2_new.win32.exe!GameScene::scene() Line 19 C++
match2_new.win32.exe!AppDelegate::applicationDidFinishLaunching() Line 109 C++
libcocos2d.dll!cocos2d::CCApplication::run() Line 47 C++
match2_new.win32.exe!wWinMain(HINSTANCE__ * hInstance, HINSTANCE__ * hPrevInstance, wchar_t * lpCmdLine, int nCmdShow) Line 34 C++
match2_new.win32.exe!__tmainCRTStartup() Line 528 C
match2_new.win32.exe!wWinMainCRTStartup() Line 377 C
simple code :
void GameController::setGemsInBoard(int levlnumber)
{
switch (levlnumber)
{
case 1:
{
std::string level_1 = "00011000,00111100,01111110,01111110,01111110,01111110,00111100,00011000";
fillBoardArray(level_1);
break;
}
case 2:
{
std::string level_2 = "00011000,00011000,11111111,11111111,11111111,00111100,00011000,00011000";
fillBoardArray(level_2);
break;
}
default:
break;
}
}
update :
void fillBoardArray(std::string lvlstr);
void GameController::fillBoardArray(std::string lvlstr)
{
// CCLOG("fillBoardArray");
int irow=0;
int icol=0;
for(int i=0;i<lvlstr.size();i++)
{
if(lvlstr[i]=='0')
{
m_GemBoardPositionMap[irow][icol] = 0;
icol++;
}
else if(lvlstr[i]=='1')
{
m_GemBoardPositionMap[irow][icol] = 1;
icol++;
}
else if(lvlstr[i]==',')
{
irow++;
icol=0;
}
}
}
I have a function called PreProcessSource, which allocates a boost::wave::context and does some preprocessing; nothing fancy at all.
std::string PreProcessSource(const std::string& instring, const std::string& defines)
{
typedef boost::wave::cpplexer::lex_token<> token_type;
typedef boost::wave::cpplexer::lex_iterator<token_type> lex_iterator_type;
typedef boost::wave::context<std::string::iterator, lex_iterator_type> context_type;
std::string source = instring;
context_type ctx(source.begin(), source.end()); // DEADLOCK here
ctx.set_language(boost::wave::enable_emit_line_directives(ctx.get_language(), true));
if(!defines.empty())
{
std::vector<std::string> tokens;
Split<std::string>(defines, tokens, ",");
std::vector<std::string>::const_iterator cit = tokens.begin();
for (;cit != tokens.end(); ++cit)
ctx.add_macro_definition(*cit);
}
context_type::iterator_type first = ctx.begin();
context_type::iterator_type last = ctx.end();
std::string outstring;
while (first != last)
{
const token_type::string_type& value = (*first).get_value();
std::copy(value.begin(), value.end(), std::back_inserter(outstring));
++first;
}
return outstring;
}
In the past (this project is being modernized so it was broken for a long time) it used to work fine in the same setup:
Library A is a DLL that hosts the PreProcess source function, executable B uses the DLL A and can call PreProcess source, and DLL A can also sometimes call the function itself.
But right now this is no longer the case: whenever DLL A calls the function itself, or DLL A calls another function, which in turn calls back into DLL A PreProcess source, it deadlocks.
Here's an example that works just fine from my unit testing:
BOOST_AUTO_TEST_CASE(Preprocessor)
{
std::stringstream sstream;
sstream << "void main(inout float4 vtxInput : POSITION) { }" << std::endl << std::endl;
std::string source = sstream.str();
std::string defines = "";
try
{
std::string shaderCode = Nitro::PreProcessSource(source, defines);
} catch (boost::wave::preprocess_exception& pe)
{
std::cerr << pe.what() << std::endl;
}
}
And here is the weird bit, if DO_DEADLOCK is defined to 1 in the following piece of code, the deadlock will happen:
class Tutorial00 : public Game
{
public:
// ...
Tutorial00()
: Game()
{
#if !DO_DEADLOCK
std::stringstream sstream;
sstream << "void main(inout float4 vtxInput : POSITION) { }" << std::endl << std::endl;
std::string source = sstream.str();
std::string defines = "";
std::string shaderCode = Nitro::PreProcessSource(source, defines);
#endif
}
void LoadContent()
{
#if DO_DEADLOCK
std::stringstream sstream;
sstream << "void main(inout float4 vtxInput : POSITION) { }" << std::endl << std::endl;
std::string source = sstream.str();
std::string defines = "";
std::string shaderCode = Nitro::PreProcessSource(source, defines);
#endif
// Original code that deadlocks too.
// Calls in the DLL, which will call PreProcessSource.
// effect->Initialize(device, source, "DX11");
}
// ...
};
#if 1
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
#else
int main(int argc, char* argv[])
#endif
{
Tutorial00 app;
app.Run();
return 0;
}
Note that the constructor is called directly from the executable, whereas LoadContent is called from the DLL:
// In the DLL
void Game::Run()
{
try
{
// ...
LoadContent();
// ...
} catch(/* ... */) { }
}
The code is compiled for x64 and in debug mode.
I compile boost myself into a DLL (using bcp to get the files for the libraries I use) with the following options:
Preprocessor:
WIN32
BOOST_ALL_NO_LIB
BOOST_ALL_DYN_LINK
BOOST_THREAD_BUILD_DLL
_DLL
_DEBUG
_WINDOWS
_USRDLL
Code Generation:
C++ Exceptions (/EHsc)
Multi-threaded Debug DLL (/MDd)
Function-level linking (/Gy)
Streaming SIMD Extensions 2 (/arch:SSE2) (/arch:SSE2)
Fast floating point model (/fp:fast)
DLL A uses the same options except for BOOST_ALL_DYN_LINK, BOOST_THREAD_BUILD_DLL and the string pooling option.
The Boost unit test library is build separately as a static library.
Here is the stack trace of the working version:
Nitro.dll!boost::call_once<void (__cdecl*)(void)>(boost::once_flag & flag, void (void)* f) Line 200 C++
Nitro.dll!boost::call_once(void (void)* func, boost::once_flag & flag) Line 28 C++
Nitro.dll!boost::spirit::classic::static_<boost::thread_specific_ptr<boost::weak_ptr<boost::spirit::classic::impl::grammar_helper ... Line 72 + 0x13 bytes C++
Nitro.dll!boost::spirit::classic::impl::get_definition<boost::wave::util::time_conversion::time_conversion_grammar, ... Line 241 + 0x17 bytes C++
Nitro.dll!boost::spirit::classic::impl::grammar_parser_parse<0,boost::wave::util::time_conversion::time_conversion_grammar, ... Line 296 + 0xa bytes C++
Nitro.dll!boost::spirit::classic::grammar<boost::wave::util::time_conversion::time_conversion_grammar, ... Line 55 + 0x3c bytes C++
Nitro.dll!boost::spirit::classic::grammar<boost::wave::util::time_conversion::time_conversion_grammar, ... Line 65 + 0x75 bytes C++
Nitro.dll!boost::spirit::classic::impl::phrase_parser<boost::spirit::classic::space_parser>::parse<char const * __ptr64,boost::wave::util::time_conversion::time_conversion_grammar> ... Line 136 C++
Nitro.dll!boost::spirit::classic::parse<char const * __ptr64,boost::wave::util::time_conversion::time_conversion_grammar, ... Line 155 + 0x3a bytes C++
Nitro.dll!boost::spirit::classic::parse<char,boost::wave::util::time_conversion::time_conversion_grammar, ... Line 173 + 0x23 bytes C++
Nitro.dll!boost::wave::util::time_conversion::time_conversion_helper::time_conversion_helper(const char * act_time) Line 123 C++
Nitro.dll!boost::wave::util::predefined_macros::predefined_macros() Line 196 + 0x38 bytes C++
...
Nitro.dll!Nitro::PreProcessSource(const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & instring, const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & defines) Line 51 + 0xa0 bytes C++
NitroCoreUnitTests.exe!Preprocessor::test_method() Line 16 C++
NitroCoreUnitTests.exe!Preprocessor_invoker() Line 7 + 0x1f bytes C++
...
NitroCoreUnitTests.exe!boost::unit_test::unit_test_main(boost::unit_test::test_suite * (int, char * *)* init_func, int argc, char * * argv) Line 187 C++
NitroCoreUnitTests.exe!main(int argc, char * * argv) Line 238 C++
NitroCoreUnitTests.exe!__tmainCRTStartup() Line 555 + 0x19 bytes C
NitroCoreUnitTests.exe!mainCRTStartup() Line 371 C
kernel32.dll!00000000766a652d()
[Frames below may be incorrect and/or missing, no symbols loaded for kernel32.dll]
ntdll.dll!0000000076d9c521()
And here is the stack trace of the deadlock:
ntdll.dll!0000000076dc135a()
[Frames below may be incorrect and/or missing, no symbols loaded for ntdll.dll]
KernelBase.dll!000007fefd4f10dc()
Nitro.dll!boost::call_once<void (__cdecl*)(void)>(boost::once_flag & flag, void (void)* f) Line 197 + 0x18 bytes C++
Nitro.dll!boost::call_once(void (void)* func, boost::once_flag & flag) Line 28 C++
Nitro.dll!boost::spirit::classic::static_<boost::thread_specific_ptr<boost::weak_ptr<boost::spirit::classic::impl::grammar_helper ... Line 72 + 0x13 bytes C++
Nitro.dll!boost::spirit::classic::impl::get_definition<boost::wave::util::time_conversion::time_conversion_grammar, ... Line 241 + 0x17 bytes C++
Nitro.dll!boost::spirit::classic::impl::grammar_parser_parse<0,boost::wave::util::time_conversion::time_conversion_grammar, ... Line 296 + 0xa bytes C++
Nitro.dll!boost::spirit::classic::grammar<boost::wave::util::time_conversion::time_conversion_grammar, ... Line 55 + 0x3c bytes C++
Nitro.dll!boost::spirit::classic::grammar<boost::wave::util::time_conversion::time_conversion_grammar, ... Line 65 + 0x75 bytes C++
Nitro.dll!boost::spirit::classic::impl::phrase_parser<boost::spirit::classic::space_parser>::parse<char const * __ptr64,boost::wave::util::time_conversion::time_conversion_grammar> ... Line 136 C++
Nitro.dll!boost::spirit::classic::parse<char const * __ptr64,boost::wave::util::time_conversion::time_conversion_grammar, ... Line 155 + 0x3a bytes C++
Nitro.dll!boost::spirit::classic::parse<char,boost::wave::util::time_conversion::time_conversion_grammar, ... Line 173 + 0x23 bytes C++
Nitro.dll!boost::wave::util::time_conversion::time_conversion_helper::time_conversion_helper(const char * act_time) Line 123 C++
Nitro.dll!boost::wave::util::predefined_macros::predefined_macros() Line 196 + 0x38 bytes C++
...
Nitro.dll!Nitro::PreProcessSource(const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & instring, const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & defines) Line 51 + 0xa0 bytes C++
Tutorial01.exe!Tutorial01::LoadContent() Line 70 + 0x33 bytes C++
Nitro.dll!Nitro::Game::Run() Line 40 C++
Tutorial01.exe!wWinMain(HINSTANCE__ * hInstance, HINSTANCE__ * hPrevInstance, wchar_t * lpCmdLine, int nCmdShow) Line 149 C++
Tutorial01.exe!__tmainCRTStartup() Line 547 + 0x42 bytes C
Tutorial01.exe!wWinMainCRTStartup() Line 371 C
kernel32.dll!00000000766a652d()
ntdll.dll!0000000076d9c521()
It seems boost::wave is trying to parse some time stamp, and by doing so instantiates a grammar and that's when things seem to go South.
Thanks in advance for any help :)
I found the following ticket opened on boost's trac: boost::call_once not re-entrant (at least in win32)
It says that call_once is not re-entrant, and shouldn't be called recursively. Thus the code that I am employing is undefined behavior; the ticket was marked as "will not fix".
The solution I've taken was to create a separate DLL that only contains the PreProcessSource function.