简体   繁体   English

从DllMain()锁定时,Visual Studio 2012死锁中的C ++ 11 std :: mutex

[英]C++11 std::mutex in Visual Studio 2012 deadlock when locked from DllMain()

I am seeing a deadlock with std::mutex when the mutex is locked from DllMain() Below is a minimal DLL test case that exhibits the problem for me. 当从DllMain()锁定std::mutex时,我看到std::mutex出现死锁。这是一个最小的DLL测试用例,它对我来说是个问题。 My actual code does the mutex locking because it uses member functions that are also usable outside initialization during normal function. 我的实际代码执行互斥锁,因为它使用成员函数,这些成员函数也可以在常规函数初始化期间使用。

I think that the problem is a deadlock between the scheduler as seen in the call stack of main() thread and the other thread (probably) spawned by the scheduler. 我认为问题是在main()线程的调用堆栈和调度程序产生的另一个线程(可能)之间出现了调度程序之间的死锁。 The deadlock seems to happen before main() is actually executed. 死锁似乎发生在main()实际执行之前。

I would appreciate any advice as to how to fix/resolve the deadlock. 对于如何解决/解决死锁问题,我将不胜感激。

Simple DLL: 简单的DLL:

static void testFunc()
{
    std::mutex mtx;
    mtx.lock();

    mtx.unlock();
}


BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        testFunc ();
        break;

    case DLL_THREAD_ATTACH:
        testFunc ();
        break;

    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

At the point of the deadlock there are two threads in the process: 在死锁点,该过程中有两个线程:

Not Flagged >   6408    0   Main Thread Main Thread msvcr110d.dll!Concurrency::details::SchedulerBase::SchedulerBase    Normal
Not Flagged     7600    0   Worker Thread   ntdll.dll!_TppWaiterpThread@4() ntdll.dll!_NtDelayExecution@8   Normal

Here is the call stack of main() thread: 这是main()线程的调用堆栈:

    ntdll.dll!_NtWaitForKeyedEvent@16() Unknown
    ntdll.dll!_TppWaitpSet@16() Unknown
    ntdll.dll!_TppSetWaitInterrupt@12() Unknown
    ntdll.dll!_RtlRegisterWait@24() Unknown
    kernel32.dll!_RegisterWaitForSingleObject@24()  Unknown
>   msvcr110d.dll!Concurrency::details::SchedulerBase::SchedulerBase(const Concurrency::SchedulerPolicy & policy) Line 152  C++
    msvcr110d.dll!Concurrency::details::ThreadScheduler::ThreadScheduler(const Concurrency::SchedulerPolicy & policy) Line 26   C++
    msvcr110d.dll!Concurrency::details::ThreadScheduler::Create(const Concurrency::SchedulerPolicy & policy) Line 34    C++
    msvcr110d.dll!Concurrency::details::SchedulerBase::CreateWithoutInitializing(const Concurrency::SchedulerPolicy & policy) Line 276  C++
    msvcr110d.dll!Concurrency::details::SchedulerBase::GetDefaultScheduler() Line 650   C++
    msvcr110d.dll!Concurrency::details::SchedulerBase::CreateContextFromDefaultScheduler() Line 567 C++
    msvcr110d.dll!Concurrency::details::SchedulerBase::CurrentContext() Line 399    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++
    ConsoleApplicationDll.dll!std::_Mtx_lockX(_Mtx_internal_imp_t * * _Mtx) Line 68 C++
    ConsoleApplicationDll.dll!std::_Mutex_base::lock() Line 43  C++
    ConsoleApplicationDll.dll!testFunc() Line 16    C++
    ConsoleApplicationDll.dll!DllMain(HINSTANCE__ * hModule, unsigned long ul_reason_for_call, void * lpReserved) Line 29   C++
    ConsoleApplicationDll.dll!__DllMainCRTStartup(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 508    C
    ConsoleApplicationDll.dll!_DllMainCRTStartup(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 472 C
    ntdll.dll!_LdrpCallInitRoutine@16() Unknown
    ntdll.dll!_LdrpRunInitializeRoutines@4()    Unknown
    ntdll.dll!_LdrpInitializeProcess@8()    Unknown
    ntdll.dll!__LdrpInitialize@8()  Unknown
    ntdll.dll!_LdrInitializeThunk@8()   Unknown

The second thread's call stack is short: 第二个线程的调用栈很短:

>   ntdll.dll!_NtDelayExecution@8() Unknown
    ntdll.dll!__LdrpInitialize@8()  Unknown
    ntdll.dll!_LdrInitializeThunk@8()   Unknown

EDIT 1: 编辑1:

WinDbg confirms that it is loader lock issue: WinDbg确认这是加载程序锁定问题:

PRIMARY_PROBLEM_CLASS:  APPLICATION_HANG_HungIn_LoaderLock

Check the Best Practices for Creating DLLs document: 查看创建DLL最佳做法文档:

You should never perform the following tasks from within DllMain: 您永远不要在DllMain中执行以下任务:

  • Call LoadLibrary or LoadLibraryEx (either directly or indirectly). 调用LoadLibrary或LoadLibraryEx(直接或间接)。 This can cause a deadlock or a crash. 这可能导致死锁或崩溃。
  • Synchronize with other threads. 与其他线程同步。 This can cause a deadlock. 这可能导致死锁。

It seems that using QueueUserAPC() to queue initialization is always executed before main() but out of the dreaded loader lock. 似乎总是在main()之前执行QueueUserAPC()进行队列初始化,但要在可怕的加载器锁之外执行。 This looks like a solution to my problem. 这看起来像解决我的问题。

EDIT 1 编辑1

After some testing it seems that the APC method works if I queue the APC from DllMain() but it does not work if I queue the APC from a ctor of a static global instance of a class. 经过一些测试后,如果我从DllMain()排队APC,则APC方法似乎可以工作,但是如果我从类的静态全局实例的ctor排队APC,则APC方法不能工作。 IOW, using the APC is not uniformly usable across all possible combinations of compilers and build modes for me. IOW,对于我来说,并不是在所有可能的编译器和构建模式组合中都统一使用APC。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM