2

Some time ago I have encountered a problem during application startup that I could not lock mutexes from below DllMain(): C++11 std::mutex in Visual Studio 2012 deadlock when locked from DllMain(). I have worked around as mentioned in the accepted answer.

Now I am having a similar issue but this time caused by process shutdown:

KernelBase.dll!RaiseException() Unknown
msvcr120d.dll!_CxxThrowException(void * pExceptionObject, const _s__ThrowInfo * pThrowInfo) Line 154    C++
msvcr120d.dll!Concurrency::details::SchedulerBase::SchedulerBase(const Concurrency::SchedulerPolicy & policy) Line 155  C++
msvcr120d.dll!Concurrency::details::ThreadScheduler::ThreadScheduler(const Concurrency::SchedulerPolicy & policy) Line 26   C++
msvcr120d.dll!Concurrency::details::ThreadScheduler::Create(const Concurrency::SchedulerPolicy & policy) Line 34    C++
msvcr120d.dll!Concurrency::details::SchedulerBase::CreateWithoutInitializing(const Concurrency::SchedulerPolicy & policy) Line 285  C++
msvcr120d.dll!Concurrency::details::SchedulerBase::GetDefaultScheduler() Line 654   C++
msvcr120d.dll!Concurrency::details::SchedulerBase::CreateContextFromDefaultScheduler() Line 571 C++
msvcr120d.dll!Concurrency::details::SchedulerBase::CurrentContext() Line 399    C++
msvcr120d.dll!Concurrency::details::LockQueueNode::LockQueueNode(unsigned int timeout) Line 619 C++
msvcr120d.dll!Concurrency::critical_section::lock() Line 1026   C++
msvcp120d.dll!mtx_do_lock(_Mtx_internal_imp_t * * mtx, const xtime * target) Line 67    C++
msvcp120d.dll!_Mtx_lock(_Mtx_internal_imp_t * * mtx) Line 154   C++
log4cplusUD.dll!std::_Mtx_lockX(_Mtx_internal_imp_t * * _Mtx) Line 68   C++
log4cplusUD.dll!std::_Mutex_base::lock() Line 42    C++
log4cplusUD.dll!log4cplus::thread::Mutex::lock() Line 80    C++
log4cplusUD.dll!log4cplus::thread::SyncGuard<log4cplus::thread::Mutex>::SyncGuard<log4cplus::thread::Mutex>(const log4cplus::thread::Mutex & m) Line 208    C++
log4cplusUD.dll!log4cplus::spi::ObjectRegistryBase::clear() Line 113    C++
log4cplusUD.dll!log4cplus::spi::FactoryRegistry<log4cplus::spi::LocaleFactory>::~FactoryRegistry<log4cplus::spi::LocaleFactory>() Line 156  C++
log4cplusUD.dll!log4cplus::`anonymous namespace'::DefaultContext::~DefaultContext() C++
log4cplusUD.dll!log4cplus::`anonymous namespace'::DefaultContext::`scalar deleting destructor'(unsigned int)    C++
log4cplusUD.dll!log4cplus::`anonymous namespace'::destroy_default_context() Line 90 C++
log4cplusUD.dll!log4cplus::thread_callback(void * __formal, unsigned long fdwReason, void * __formal) Line 457  C++
log4cplusUD.dll!DllMain(HINSTANCE__ * hinstDLL, unsigned long fdwReason, void * lpReserved) Line 480    C++
log4cplusUD.dll!__DllMainCRTStartup(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 508  C
log4cplusUD.dll!_DllMainCRTStartup(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 473   C
ntdll.dll!LdrShutdownProcess()  Unknown
ntdll.dll!RtlExitUserProcess()  Unknown
msvcr120d.dll!__crtExitProcess(int status) Line 776 C
msvcr120d.dll!doexit(int code, int quick, int retcaller) Line 679   C
msvcr120d.dll!exit(int code) Line 426   C
timeformat_test.exe!__tmainCRTStartup() Line 662    C
timeformat_test.exe!mainCRTStartup() Line 466   C
kernel32.dll!BaseThreadInitThunk()  Unknown
ntdll.dll!RtlUserThreadStart()  Unknown

The exception that it is raising is scheduler_resource_allocation_error. It is caused by failure of RegisterWaitForSingleObject() call.

The previous workaround will not help. I would have to be able to globally disable locking for all mutexes in various classes.

How the hell am I supposed to use C++11 threading and synchronization facilities if the Visual Studio 2013 does not let me clean up at process exit?

Community
  • 1
  • 1
wilx
  • 17,697
  • 6
  • 59
  • 114
  • Can you clean up explicitly before process exit? – Jon Mar 14 '14 at 11:21
  • @Jon: In the end I can do anything but I do not consider it an acceptable option, given that the same code works just fine on non-Windows platforms. Also, this is a DLL that can be used by other DLLs. Going your suggest way would force everybody transitively to expose some cleanup function. – wilx Mar 14 '14 at 11:37
  • Shutting down a multi-threaded program orderly is just plain hard. The reason that std::quick_exit() was added in C++11. – Hans Passant Mar 14 '14 at 14:14
  • @HansPassant: But there are no other threads than the main thread running anywhere at the shutdown time. Only the cleanup code is using member function that do lock mutexes because the same member functions need to work during normal operation under multiple threads. – wilx Mar 14 '14 at 14:30
  • http://sourceforge.net/p/log4cplus/wiki/BugReportingInstructions/ – Hans Passant Mar 14 '14 at 14:37
  • 2
    @HansPassant: Haha, this is funny because I am the sole developer of log4cplus and I have written those instructions. :) – wilx Mar 14 '14 at 22:55
  • Aaaand this is still an issue 3 years later. – wilx Jul 11 '17 at 14:45

4 Answers4

2

It seems it's a bug of VC2013. Happens in my app too. I've created a report here: https://connect.microsoft.com/VisualStudio/feedback/details/1282596

Alexander Dyagilev
  • 1,139
  • 1
  • 15
  • 43
2

Try to create a dummy std::mutex and call lock_guard with it, and the problem hopefully disappear:-)

We had a similar problem recently, though we are not calling mutex functions from DllMain, and we got problem when process exit, cleaning global variables.

It seems that std::mutex in VS is implemented with Concurrency Runtime, which internally has delay initialization mechanism. And if no lock_guard like function is called, CR is not initialized yet. And such initialization not working correctly when process is exiting.

So, create & lock a dummy mutex to force CR to be initialized. Hopefully problem disappear.

Btw, as @Alexander Dyagilev mentioned, in the post at Connect, VS2015 is implementing std mutex with Windows API, hopefully will not be a problem for it.

sali98
  • 31
  • 2
1

You shouldn't clean up resources in DllMain if the process is exiting. Just flush your files and return.

http://msdn.microsoft.com/en-US/library/windows/desktop/ms682583%28v=vs.85%29.aspx

If the process is terminating (the lpvReserved parameter is non-NULL), all threads in the process except the current thread either have exited already or have been explicitly terminated by a call to the ExitProcess function, which might leave some process resources such as heaps in an inconsistent state. In this case, it is not safe for the DLL to clean up the resources. Instead, the DLL should allow the operating system to reclaim the memory.

http://blogs.msdn.com/b/oldnewthing/archive/2012/01/05/10253268.aspx

When the Dll­Main function receives a reason code of DLL_PROCESS_DETACH, the increasingly-inaccurately-named lpReserved parameter is used to indicate whether the process is exiting. And if the process is exiting, then you should just return without doing anything.

Jon
  • 5,275
  • 5
  • 39
  • 51
0

The error can be reproduced by a simple console application using log4cplus lib with following main:

int _tmain(int argc, _TCHAR* argv[])
{
    log4cplus::initialize(); 
    return 0;
}

I solved the Problem with following two options:

a) Change in win32.h following line:

#  if _MSC_VER >= 1700

to

#  if _MSC_VER >= 1900 

--> win32 critical sections (syncprims-impl.h) will be used during destruction of factory_registry objects in destroy_default_context_ object.

NOTE that i didn't check if this works with VStudio2015 but might be. see: https://connect.microsoft.com/VisualStudio/feedback/details/1282596

if not, check solution b).

b) the Problem occurs because during PROCESS_DETACH the std::recursive_mutex cannot be allocated during application Exit.

Solution:

Change Constructor of

ObjectRegistryBase::ObjectRegistryBase()
{    
}

to:

ObjectRegistryBase::ObjectRegistryBase()
{
    thread::MutexGuard guard(mutex);
}
H.J.Lücke
  • 21
  • 3
  • Why should/does adding `thread::MutexGuard guard(mutex);` help here? – wilx Dec 07 '16 at 08:45
  • The mutex lock in this case is used during the default initialization by creating the global object (static destroy_default_context_) . Then it is not necessary to allocate mutex resource during PROCESS_DETACH. – H.J.Lücke Dec 07 '16 at 09:52