So, this is what I\'m talking about: std is complex.
In VS2013 this simple program will cause a deadlock.
#include
#include
The specification for std::thread
contains the following requirement (N4527 §30.3.1.2[thread.thread.constr]/6):
Synchronization: The completion of the invocation of the constructor synchronizes with the beginning of the invocation of the copy of
f
.
(where f
is the callable entity which is to be executed on the newly created thread.)
The constructor for the std::thread
cannot return until the new thread starts executing the thread procedure. When a new thread is created, before the thread procedure is invoked, the entry point of each loaded DLL is invoked for DLL_THREAD_ATTACH
. To do this, the new thread must acquire the loader lock. Unfortunately, your existing thread already holds the loader lock.
Thus, you deadlock: the existing thread cannot release the loader lock until the new thread starts executing the thread procedure but the new thread cannot execute the thread procedure until it can acquire the loader lock, which is held by the existing thread.
Note that the documentation expressly recommends against creation of threads from the DLL entry point:
You should never perform the following tasks from within
DllMain
: [...] CallCreateThread
. Creating a thread can work if you do not synchronize with other threads, but it is risky.
(That page has a long list of things that should not be done from a DLL entry point; this is just one of them.)
Use detach()
member function to fix the crash. Example:
void Hook_Init();
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
{
std::thread hookthread(Hook_Init);
hookthread.detach();
break;
}
}
return TRUE;
}
void Hook_Init()
{
// Code
}
You're mixing the platform level with std
level. Calling the raw winapi function CreateThread
can work in DllMain
. But there's no guarantee about how std::thread
will interact with the platform. It's well known that it's extremely dangerous to be doing things like this in DllMain, so I don't recommend it at all. If you insist on trying, then you're going to need to tip-toe around and call the winapi directly, avoiding consequences of the std
implementation.
As for "why", it shouldn't really matter, but after a very quick look in the debugger, it seems that the MSVC implementation has a handshake with the new thread for exchanging arguments and resources. So it requires synchronization to know when the resources have been handed off. Seems reasonable.
std::thread
creates a C++ thread. It means you can rely on the C++ library in that thread. This means certain shared data structures must be set up, which force synchronization (you might create multiple threads in parallel). The stack trace clearly shows this: std::_Cnd_waitX
is clearly a part of the Standard Library, and is clearly synchronizing. Synchronizing is blacklisted in the document you mentioned, so this crash isn't a big surprise.
Further up the stack we see Concurrency::
. This is specific to Visual Studio versions up to VS2015. This means you may luck out in VS2015. Doing thread synchronization in DllMain
is not a guaranteed crash. Just quite possible.