Friday, January 7, 2011

CreateThread Usage Pattern

I don’t really use the old thread pool (Windows 2000 & XP) in favor of the new awesome thread pool introduced in Vista, but sometimes you might inherit old code that does. I just finished fixing a bug related to the old CreateThread. I have some time before my team’s Friday afternoon party, so I thought I would share. I found the error thanks to App Verifier; +1 to App Verifier’s awesomeness.

Basically the usage pattern is:
1. get the module handle (GetModuleHandleEx)
2. create the thread (CreateThread)
3. If COM, CoInitialize a new apartment for the thread
4. Do work
5. If COM, CoUninitialize
6. FreeLibraryAndExitThread with the handle gotten in #1
7. Return

Steps 1-2, The thread creator would do something like this:

if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCWSTR)&Class::ThreadProc , &s_hModule))
hr = HRESULT_FROM_WIN32( GetLastError() );

if (S_OK == hr) {
if (s_hThread)
CloseHandle(s_hThread);
s_hThread = CreateThread(NULL, 0, ThreadProc, this, 0, &s_dwThreadID);
if (!s_hThread) {
hr = HRESULT_FROM_WIN32(::GetLastError());
FreeLibrary( s_hModule );
}
}


In my case, the bug was in the GetModuleHandleEx. Take heed that if you are using the GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRES flag, then the second parameter should an address inside of your DLL; I have read that &__ImageBase or the HINSTANCE passed into DllMain should also do the trick. Also the third parameter will be used inside the thread proc.

Steps 3-7:

DWORD WINAPI Class::ThreadProc(void *pv)
{
if (S_OK == CoInitializeEx( NULL, COINIT_MULTITHREADED)) {
//...
// do work here
CoUninitialize();
}

FreeLibraryAndExitThread(s_hModule, 0);
return 0;
}


As always, read the documentation to go deeper in the topic.

Setting a Processor Breakpoint in WinDbg

If you have a variable like a pointer or a ref count that seems to be getting clobbered or incorrectly accessed, the ba (break on access) command can be a life saver. Setting a break on the memory address allows you to break in with windbg, inspect the stack, or do whatever else you need to do the moment the memory is accessed.

The basic usage is:
ba address access memory_size

Ex. Tracking write access of a pointer on an AMD64 machine:
ba 0000000070540018 w8

Ex. Tracking read/write of a DWORD ref count on x86:
ba 7124220 r4

Look at the windbg documentation for info.