3

I am attempting to add handle leak detection to the unit test framework on my code. (Windows 7, x64 VS2010)

I basically call GetProcessHandleCount() before and after each unit test. This works fine except when threads are created/destroyed as part of the test.

It seems that windows is occasionally creating an 1-3 events on thread shutdown. Running the same test in a loop does not increase the event creation count. (eg running the test 5000 times in a loop only results in 1-3 extra events being created)

I do not create events manually in my own code.

It seems that this is similar to this problem: boost::thread causing small event handle leak? but I am doing manual thread creation/shutdown.

I followed this code: http://blogs.technet.com/b/yongrhee/archive/2011/12/19/how-to-troubleshoot-a-handle-leak.aspx

And got this callstack from WinDbg:

Outstanding handles opened since the previous snapshot:
--------------------------------------
Handle = 0x0000000000000108 - OPEN
Thread ID = 0x00000000000030dc, Process ID = 0x0000000000000c90

0x000000007715173a: ntdll!NtCreateEvent+0x000000000000000a
0x0000000077133f26: ntdll!RtlpCreateCriticalSectionSem+0x0000000000000026
0x0000000077133ee3: ntdll!RtlpWaitOnCriticalSection+0x000000000000014e
0x000000007714e40b: ntdll!RtlEnterCriticalSection+0x00000000000000d1
0x0000000077146ad2: ntdll!LdrShutdownThread+0x0000000000000072
0x0000000077146978: ntdll!RtlExitUserThread+0x0000000000000038
0x0000000076ef59f5: kernel32!BaseThreadInitThunk+0x0000000000000015
0x000000007712c541: ntdll!RtlUserThreadStart+0x000000000000001d
--------------------------------------

As you can see, this is an event created on the thread shutdown.

Is there a better way of doing this handle leak detection in unit tests? My only current options are:

  • Forget trying to do this handle leak detection
  • Spin up some dummy tasks to attempt to create these spurious events.
  • Allow some small tolerance value in leaks and run each test 100's of times (so actual leaks will be a large number)
  • Get the handle count excluding events (difficult amount of code)

I have also tried switching to using std::thread in VS2013, but it seems that it creates a lot of background threads and handles when used. (makes the count difference much worse)

Here is a self contained example where 99+% of the time (on my computer) an event is created behind the scenes. (handle count is different). Putting the startup/shutdown code in a loop indicates it does not directly leak, but accumulates the occasional events:

#include "stdio.h"
#include <Windows.h>
#include <process.h>

#define THREADCOUNT 3
static HANDLE s_semCommand, s_semRender;

static unsigned __stdcall ExecutiveThread(void *)
{
  WaitForSingleObject(s_semCommand, INFINITE);
  ReleaseSemaphore(s_semRender, THREADCOUNT - 1, NULL);
  return 0;
}

static unsigned __stdcall WorkerThread(void *)
{
  WaitForSingleObject(s_semRender, INFINITE);
  return 0;
}

int main(int argc, char* argv[])
{
  DWORD oldHandleCount = 0;
  GetProcessHandleCount(GetCurrentProcess(), &oldHandleCount);

  s_semCommand = CreateSemaphoreA(NULL, 0, 0xFFFF, NULL);
  s_semRender  = CreateSemaphoreA(NULL, 0, 0xFFFF, NULL);

  // Spool threads up
  HANDLE threads[THREADCOUNT];
  for (int i = 0; i < THREADCOUNT; i++)
  {
    threads[i] = (HANDLE)_beginthreadex(NULL, 4096, (i==0) ? ExecutiveThread : WorkerThread, NULL, 0, NULL);
  }

  // Signal shutdown - Wait for threads and close semaphores
  ReleaseSemaphore(s_semCommand, 1, NULL);
  for (int i = 0; i < THREADCOUNT; i++)
  {
    WaitForSingleObject(threads[i], INFINITE);
    CloseHandle(threads[i]);
  }
  CloseHandle(s_semCommand);
  CloseHandle(s_semRender);

  DWORD newHandleCount = 0;
  GetProcessHandleCount(GetCurrentProcess(), &newHandleCount);
  printf("Handle %d -> %d", oldHandleCount, newHandleCount);
    return 0;
}
Community
  • 1
  • 1
  • Can you post a *complete* thread startup, run, wait-for, and cleanup of your manual thread management? The unit test itself from you description is irrelevant (unless it *is* the thread proc, but by the sound of it , it isn't). I doubt you made any critical mistakes, as it certainly seems you're familiar with the API, but worth checking regardless. If nothing else, it contributes to a better documented question. – WhozCraig Jul 07 '14 at 06:15
  • Added a self contained example you can compile to see the problem. – Damian Trebilco Jul 07 '14 at 07:46
  • It's even stranger than expected, I debugged a bit and saw that the init phase of the thread (the thread itself) *might* create an event and that terminating the thread *might* create an event. I saw it with Process Explorer and changed the code so that all threads are created suspended, then resumed, then triggered, then wait for, then closed. I added some Sleep calls. Do you mind me doing the changes to your question? – Werner Henze Jul 07 '14 at 09:48
  • @WernerHenze: not all *that* strange, really. There's a critical section involved; it's natural for a critical section to contain an event, but for efficiency such an event might well only be created when it is actually needed. – Harry Johnston Jul 07 '14 at 10:49

0 Answers0