views:

350

answers:

3

The question is how function-level statics are constructed when the function is called on multiple threads?

Problem Description: Deadlock occurs and my application doesn't get terminated. During initialization of local static variable it tries to acquire MSVCR80!_lock and never gets hold on the lock.

!locks command in WinDbg gives the following output.

CritSec ntdll!LdrpLoaderLock+0 at 7c97e178
LockCount 0

RecursionCount 1

OwningThread 1998

EntryCount d

ContentionCount d

* Locked

CritSec MSVCR80!__app_type+94 at 781c3bc8

LockCount 1

RecursionCount 1

OwningThread 9a8

EntryCount 1

ContentionCount 1

* Locked

Below is the calls stack and you will see that it will never get hold on the lock _mlock

#

**

Call stack Thread 17e8

**

781c3bc8 78132bd9 0777fde4 ntdll!RtlEnterCriticalSection+0x46

00000008 b87d2630 00000000 MSVCR80!_lock+0x2e

0864ae10 08631d7f 0864ae10 EPComUtilities32!_onexit+0x36

0864ae10 b87d2588 00000001 EPComUtilities32!atexit+0x9

0777fea8 0864719f 08630000 EPComUtilities32!XCriticalSectionEx::ThreadTerminated+0x5f

08630000 00000003 00000000 EPComUtilities32!DllMain+0x20

08630000 7c90118a 08630000 EPComUtilities32!__DllMainCRTStartup+0x7a

08630000 00000003 00000000 EPComUtilities32!_DllMainCRTStartup+0x1d #

**

Call Stack thread 1100

**

000000b0 00000000 00000000 ntdll!ZwWaitForSingleObject+0xc

000000b0 ffffffff 00000000 kernel32!WaitForSingleObjectEx+0xa8

000000b0 ffffffff 06ce64e0 kernel32!WaitForSingleObject+0x12

000480ba 000f4240 00000000 CATSysMultiThreading!CATThreads::Join+0xf5

0012fcc8 00000004 00000000 JS0GROUP!CATLM::StopHB+0xf4

d138509f 00416694 00000001 JS0GROUP!CATLM::Unmake+0x6b

00000000 00000000 00000000 MSVCR80!_cinit+0xd6

00000000 0012fd6c 081e68d9 MSVCR80!exit+0xd

00000000 06d404f0 0998fb90 JS0GROUP!CATExit+0x1d

00000000 004ef366 0000000d DNBPLMProvider!DNBEPLMTransactionMgt::OnApplicationExit+0x229

00000000 0012fd9c 004eabfc JS0GROUP!CATCallExits+0x2bc

00000000 0012ff7c 0040cefd JS0GROUP!CATErrorNormalEnd+0x31 00000000 06ce71d0 06ce71d0 JS0GROUP!CATExit+0xc

00000007 06cdb120 059b61d8 DLMMfgContextSolver!main+0x146d

ffffffff ffffffff bffde000 DLMMfgContextSolver!__tmainCRTStartup+0x10f

// Code snippet below 
void main() 
{

    atexit(MyCallBack); 
    exit(0); 

}

void MyCallBack() 
{

// Waitingforsingleobject() // Waits until all threads are terminated

}

The EXE call DllMain with DLL_THREAD_DETACH flag and we have an explicit handling as shown below

BOOL APIENTRY DllMain( HANDLE, DWORD dwReason, LPVOID ) 
{
  if(dwReason == DLL_THREAD_DETACH) 
  { 
    F1();
    F2();
  }
}

F1()
{

    const static CComBSTR bstrMethod = __ FUNCTION __ ;

}

F2()
{

    const static CComBSTR bstrMethod = __ FUNCTION __ ;

}

Is it thread safe to have local static initialization within a function. Also I noticed if static variable is once initialized before the exit() of main application I don't see any problem. Can any one please explain what might be issue?

Note: But when I make static variable as non static the deadlock doesn't occur and problem is solved.

Also let me know any alternate solution which might help in this situation Eagerly waiting for reply.

A: 

Can you set a breakpoint in C:\Program Files\Microsoft Visual Studio 8\VC\crt\src\atonexit.c at line 103 and record which threads acquire _EXIT_LOCK1 in what order (and what the call stack is in each case, and possibly which other locks are already acquired at the time)?

Also, in your sample code Waitingforsingleobject is commented out -- can you please confirm that it is indeed commented out in your code when the deadlock occurs?

vladr
Replay to Answer 2, No Waitingforsingleobject is not commented out, also i have updated the Description with actual call stack as you can see thread 1100 already holds lock on MSVCR80 and is waiting for thread 17e8 to terminated but intern this thread 17e8 tries acquire the _lock(_EXIT_LOCK1); during initialization of static variable but able to acquire and hence deadlock occurs.
IRFAN
A: 

I'll take a stab at it, event though I'm not familiar with the CComBSTR class.

Bascially you have a single object that is being used (in this case constructed) by multiple threads with NO concurrency protection. Doesn't that sound like a Bad Thing?

There are tons of articles on why depending on automatic initialization of static variables is a bad idea. That's what you're doing here, the fact that they are scoped to the function, not the module really doesn't matter.

kfh
+1  A: 

In a sense, this question has been answered by Vlad's comments to your question: Don't do anything (if at all possible) in DllMain.

You're really limited by what's sensible in DllMain, Anything involving delving into another Dll (including using malloc etc) is out. In fact about the only things you can do are: make calls into Kernel32.dll (except for loading/unloading Dlls!) and initialising simple datatypes.

I really think that given your description of the problem, using CComBSTR's is breaking one or more of these rules.

Hope this helps,

p.s.

An extra link from MSDN blogs: http://blogs.msdn.com/oleglv/default.aspx

Moleski