コードの単体テスト フレームワークにハンドル リーク検出を追加しようとしています。(Windows 7、x64 VS2010)
私は基本的に、各単体テストの前後に GetProcessHandleCount() を呼び出します。これは、スレッドがテストの一部として作成/破棄される場合を除き、正常に機能します。
Windows は、スレッドのシャットダウン時に 1 ~ 3 のイベントを作成しているようです。同じテストをループで実行しても、イベントの作成数は増加しません。(たとえば、テストをループで 5000 回実行すると、1 ~ 3 個の余分なイベントが作成されるだけです)
自分のコードでイベントを手動で作成することはありません。
これは次の問題に似ているようです: boost::thread の原因となる小さなイベント ハンドル リーク? しかし、手動でスレッドの作成/シャットダウンを行っています。
私はこのコードに従いました: http://blogs.technet.com/b/yongrhee/archive/2011/12/19/how-to-troubleshoot-a-handle-leak.aspx
そして、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
--------------------------------------
ご覧のとおり、これはスレッドのシャットダウンで作成されたイベントです。
単体テストでこのハンドルリーク検出を行うより良い方法はありますか? 私の現在の唯一のオプションは次のとおりです。
- このハンドル リーク検出を行うのを忘れる
- これらの偽のイベントを作成するために、いくつかのダミー タスクを起動します。
- わずかな漏れの許容値を許容し、各テストを数百回実行します (したがって、実際の漏れは多数になります)。
- イベントを除いたハンドル数を取得する (難しいコード量)
私もVS2013でstd::threadを使用するように切り替えてみましたが、使用すると多くのバックグラウンドスレッドとハンドルが作成されるようです。(カウントの差をさらに悪化させます)
これは自己完結型の例で、(私のコンピューター上で) 99% 以上の確率でイベントが舞台裏で作成されます。(ハンドル数は異なります)。起動/シャットダウン コードをループに入れると、直接リークしないことを示しますが、時折のイベントが蓄積されます。
#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;
}