views:

424

answers:

4

I'm looking for some random crashes in an old c++ application. Using sysinternals process explorer, I noticed the app losing handles, and extracted the exact situation, where the program is losing handles to a very short piece of code.

DWORD WINAPI MyTestThread( void*  PThread)
{
 _endthreadex(0);
 return 0;
}

int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR  PParameter, int)
{
 for (int i=0;i<10000;i++)
 {
   unsigned int threadID;
   HANDLE hThread= (HANDLE)_beginthreadex( (void*)NULL, (unsigned int)32768, (unsigned int (__stdcall *)(void *))MyTestThread, (void*)NULL, (unsigned int)0, &threadID);
   WaitForSingleObject((HANDLE)hThread, 1000);
   CloseHandle((HANDLE)hThread);
 }
 return 0;
}

My problem: I can't figure out what's wrong with this code. It loses exactly 5 handles on every iteration, but it looks OK to me.
Funny thing: it seems not to lose handles on windows vista, but I'd be very surprised if this should be a bug in windows 7.

[Update] I tried using _beginthread/_endthread and CreateThread/ExitThread instead, those two are losing 5 handles, too, just like _beginthreadex.

[2nd Update] the code does run as expected. All return values are good. It is 'just' losing handles like there is no tomorrow.

[3rd Update] Big new Info The code only loses handles, if compiled with /clr! And more, if I call GC::Collect() on each iteration the handles will be reclaimed!
So, how do I find what clr-objects are being collected there?

A: 

Have you tried testing this with Win32's CreateThread? That could possibly narrow down the problem to the CRT.

zildjohn01
Since I would expect CreateThread to lose even more handles or be prone to crashes due to missing CLR initialisation, I've not tried it. But I will, to check it out, thanks!
Sam
Tried CreateThread, it is losing 5 handles, just like the beginthread/beginthreadex.
Sam
+1  A: 

Have you checked if the functions succeed? The return values and GetLastError() could give some hints what's going wrong.

sth
The code does run. The threads are started. _beginthreadex returns a handle, just like it should. WaitForSingleObject return 0, CloseHandle returns true. So everything works, as it should. It is just losing handles.
Sam
+1  A: 

From http://msdn.microsoft.com/en-us/library/kdzttdcb.aspx

"If successful, each of these functions returns a handle to the newly created thread; however, if the newly created thread exits too quickly, _beginthread might not return a valid handle (see the discussion in the Remarks section). _beginthread returns -1L on an error, in which case errno is set to EAGAIN if there are too many threads, to EINVAL if the argument is invalid or the stack size is incorrect, or to EACCES in the case of insufficient resources (such as memory). _beginthreadex returns 0 on an error, in which case errno and _doserrno are set. "

Your thread does exit kind of quickly doesn't it.

Paul Mitchell
Yeah, I read that, too, I checked all my return values, every single _beginthreadex returns a valid handle (724 on first call, 748 on every subsequent call of _beginthreadex). The id of the thread seems to change for every thread, though - can't see it being reused.
Sam
Oh, wait, after creating 3328 threads the value of the handle does change again. Probably some other service caught the other handle value by that time.
Sam
+2  A: 

Check whether some DLL which is linked to your exe is doing something strange in its DLLMain in response to DLL_THREAD_ATTACH notifications.

Carlos A. Ibarra
Woah, you hit gold, I'd say - I compiled this without any dll's at all, as a simple console program, and it is not losing handles anymore. Now I gotta check which dlls are losing handles, any why. Thanks!
Sam
Dammit, it looked so good! But it was no DLL. I removed them all from the main project, still losing handles. But thanks to your hint now I got one version losing handles, one not losing handles - now I'm going to check out every single difference! Thanks!
Sam
Ah yes, the small example does lose handles if compiled with /clr. So it's got something to do with managed c++
Sam