본문 바로가기

프로그래밍/디버깅

스레드에서 CoInitialize() 호출 시 주의 사항


스레드에서 CoInitialize(NULL); 를 호출 할 경우 CoUnInitialize()를 명시적으로 호출하지 않을 경우 메인 스레드가 블럭(로더 락)되는 경우가 발생할 수 있다.

블럭된 프로세스의 덤프를 가지고 WinDBG에서 스레드 콜 스택을 확인한 경우이다.

* 메인 스레드
.  0  Id: d6c.c14 Suspend: 1 Teb: 7ffdf000 Unfrozen
 # ChildEBP RetAddr  Args to Child             
00 0013e4bc 7c93df3c 7c94b22b 00000098 00000000 ntdll!KiFastSystemCallRet (FPO: [0,0,0])
01 0013e4c0 7c94b22b 00000098 00000000 00000000 ntdll!NtWaitForSingleObject+0xc (FPO: [3,0,0])
02 0013e548 7c931046 019ab178 7c942cae 7c9ab178 ntdll!RtlpWaitForCriticalSection+0x132 (FPO: [Non-Fpo])
03 0013e550 7c942cae 7c9ab178 c0150008 00000001 ntdll!RtlEnterCriticalSection+0x46 (FPO: [1,0,0])
04 0013e58c 7c946471 00000001 00000000 0013e5ec ntdll!LdrLockLoaderLock+0xea (FPO: [Non-Fpo])
05 0013e828 7c801bbd 0025c020 0013e874 0013e854 ntdll!LdrLoadDll+0xd6 (FPO: [Non-Fpo])
06 0013e890 7c801d72 7ffdfc00 00000000 00000000 kernel32!LoadLibraryExW+0x18e (FPO: [Non-Fpo])
07 0013e8a4 769a7ade 769a7ae8 00000000 00000000 kernel32!LoadLibraryExA+0x1f (FPO: [Non-Fpo])
08 0013e8b4 769a79b1 0013ed70 76a96dd8 769a710b ole32!GetXPSP2ResModuleHandle+0x16 (FPO: [0,0,0])
09 0013e8c0 769a710b 00000000 00000000 00000000 ole32!LoadResourceStrings+0x9 (FPO: [0,0,0])
0a 0013ed74 769a6be4 00000000 76a961c0 00000000 ole32!CRpcResolver::GetConnection+0x2b0 (FPO: [Non-Fpo])
0b 0013edac 769a638e 00000000 ffffffff 00000000 ole32!CoInitializeSecurity+0x4c (FPO: [Non-Fpo])
0c 0013eddc 76a918e4 0016fa98 00000000 0013eec0 ole32!InitializeSecurity+0x2c (FPO: [0,0,0])
0d 0013edf8 769a5d45 0016fa98 00000000 43895432 ole32!ChannelProcessInitialize+0x13a (FPO: [Non-Fpo])
0e 0013ee2c 769a5c98 003d8c10 00000000 76a900ae ole32!CComApartment::InitRemoting+0xaa (FPO: [Non-Fpo])
0f 0013ee38 76a900ae 0013ee6c 0016fa98 0013ee54 ole32!CComApartment::StartServer+0x13 (FPO: [0,0,0])
10 0013ee48 769bf62c 003d8e18 0013ee70 75bc0cc3 ole32!InitChannelIfNecessary+0x1e (FPO: [Non-Fpo])
11 0013ee54 75bc0cc3 75bbedec 0013ee6c 002519d0 ole32!CoGetObjectContext+0x11 (FPO: [Non-Fpo])
12 0013ee70 75bc1106 75bc1426 003d4258 00000000 jscript!GcContext::GetCurrentAllocator+0x19 (FPO: [Non-Fpo])
13 0013ee74 75bc1426 003d4258 00000000 003d0087 jscript!PvarAlloc+0x5 (FPO: [Non-Fpo])

* 다른 스레드
  12  Id: d6c.8e4 Suspend: 1 Teb: 7ff9f000 Unfrozen
 # ChildEBP RetAddr  Args to Child             
00 03a2fb7c 7c93df3c 7c94b22b 0000049c 00000000 ntdll!KiFastSystemCallRet (FPO: [0,0,0])
01 03a2fb80 7c94b22b 0000049c 00000000 00000000 ntdll!NtWaitForSingleObject+0xc (FPO: [3,0,0])
02 03a2fc08 7c931046 00a961c8 7698d222 76a961c8 ntdll!RtlpWaitForCriticalSection+0x132 (FPO: [Non-Fpo])
03 03a2fc10 7698d222 76a961c8 001c0270 76a961c0 ntdll!RtlEnterCriticalSection+0x46 (FPO: [1,0,0])
04 03a2fc20 7698fb04 001c0270 00000080 00000001 ole32!COleStaticMutexSem::Request+0x59 (FPO: [0,0,0])
05 03a2fdcc 7698fd56 001c0270 00000080 00000001 ole32!COIDTable::ThreadCleanup+0x32 (FPO: [Non-Fpo])
06 03a2fdf8 7698fca9 00000000 03a2fe44 76a967e8 ole32!FinishShutdown+0x69 (FPO: [Non-Fpo])
07 03a2fe14 7698f231 00000000 00000000 001c0270 ole32!ApartmentUninitialize+0x7e (FPO: [Non-Fpo])
08 03a2fe2c 7698ee98 03a2fe44 00000000 00000001 ole32!wCoUninitialize+0x41 (FPO: [Non-Fpo])
09 03a2fe48 769bc269 00000001 76970000 7698d1ba ole32!CoUninitialize+0x5b (FPO: [Non-Fpo])
0a 03a2fe54 7698d1ba 03a2fe7c 7698d159 76970000 ole32!DoThreadSpecificCleanup+0x4f (FPO: [0,0,0])
0b 03a2fe5c 7698d159 76970000 00000003 00000000 ole32!ThreadNotification+0x37 (FPO: [Non-Fpo])
0c 03a2fe7c 7698d101 76970000 00000003 00000000 ole32!DllMain+0x147 (FPO: [Non-Fpo])
0d 03a2fe9c 7c93118a 76970000 00000003 00000000 ole32!_DllMainCRTStartup+0x52 (FPO: [Non-Fpo])
0e 03a2febc 7c943a23 7698d0b9 76970000 00000003 ntdll!LdrpCallInitRoutine+0x14
0f 03a2ff34 7c80c126 01215b50 0013f064 01217110 ntdll!LdrShutdownThread+0xd7 (FPO: [Non-Fpo])
10 03a2ff6c 01190f3e 00000000 01190f60 00000000 kernel32!ExitThread+0x3e (FPO: [Non-Fpo])
11 03a2ff74 01190f5f 00000000 7b2f1c2f 01215b50 □□□□□!_endthreadex+0x38 (FPO: [Non-Fpo]) (CONV: cdecl) [f:\sp\vctools\crt_bld\self_x86\crt\src\threadex.c @ 412]
12 03a2ffac 01190fff 0013f064 7c80b713 01217110 □□□□!_callthreadstartex+0x20 (FPO: [Non-Fpo]) (CONV: cdecl) [f:\sp\vctools\crt_bld\self_x86\crt\src\threadex.c @ 348]
13 03a2ffb4 7c80b713 01217110 01215b50 0013f064 □□□□!_threadstartex+0x7f (FPO: [Non-Fpo]) (CONV: stdcall) [f:\sp\vctools\crt_bld\self_x86\crt\src\threadex.c @ 326]


스레드에서 명시적으로 CoUninitialize();를 호출하지 않을 경우 커널은 LdrShutdownThread 함수에서 ole32.dll을 사용하여 CoUninitialize();을 호출하기 위한 동작을 하게 된다.
MSDN에 CoUninitialize 에 이런내용이 나와 있다.

Because there is no way to control the order in which in-process servers are loaded or unloaded, do not call CoInitialize, CoInitializeEx, or CoUninitialize from the DllMain function.

즉 DllMain()에서 호출하지 말라는 말이다. 헌데 스레드에서 명시적으로 호출을 하지 않았으니 이렇게 동작하게 된 것 같다.

이제 메인 스레드를 보자
jscript.dll에서 정확히는 모르지만 COM 관련 초기화를 진행하면서 LoadLibraryEx를 호출하고 있다.
해당 LoadLibraryEx에서 LdrLockLoaderLock 을 호출하고 로더락을 대기하고 있다.

전체적으로 보면 스레드에서 CoUninitialize()를 명시적으로 호출하지 않았고 그로 인해 커널에서 이를 호출하기 위한 DLL들을 로딩하고 거기서 CoUninitialize()를 호출하면서 락을 걸고 진행하는 동안 메인 스레드에서 COM 초기화를 진행하면서 LoadLibraryEx가 호출되고 그로 인한 로더락이 발생하여 두 스레드간의 블럭이 발생되었다.

해당 스레드에서 명시적으로 CoUninitialize() 를 호출 할 경우 이러한 블럭 현상을 발생되지 않았다.

로더락에 대한 더 자세한 정보는 다음 링크에 더 자세히 나와 있다. http://www.jiniya.net/tt/788