Я создал простой тест, который создает два потока;первый ( WorkerThreadFun ) выполняет бесконечный цикл, а второй ( WorkerGuardThreadFun ) завершает его с небольшим тайм-аутом.
Поток, который должен быть прерван, не выполняет точное распределение (по крайней мере внутри WorkerThreadFun ) и использует только переменные стека простого типа C, поэтому я надеюсь, что стек будет освобожден TerminateThread () с CloseHandle ().
По какой-то причине этот тест приводит к утечке памяти на моем Win7.
Где находится несбалансированное распределение кучи?
#include <windows.h>
#include <synchapi.h>
#include <assert.h>
#include <stdio.h>
#define STK_SIZE 4097
typedef enum
{
GRD_IDLE,
GRD_READY,
GRD_TASKSTARTING,
GRD_TASKWAITING
} GuardThreadState;
typedef struct
{
HANDLE mHworkerThread;
HANDLE mHworkerGroupThread;
volatile int mIsWorkerStarted;
GuardThreadState mGuardThreadState;
CRITICAL_SECTION mLock;
CONDITION_VARIABLE mThreadReadyCond;
CONDITION_VARIABLE mStartTaskCond;
CONDITION_VARIABLE mTaskFinishedCond;
} WorkerThreadHolder;
/*
typedef VOID(WINAPI *PRtlFreeUserThreadStack)(HANDLE hProcess, HANDLE hThread);
static PRtlFreeUserThreadStack RtlFreeUserThreadStack = NULL;
*/
DWORD WINAPI WorkerThreadFun(_In_ LPVOID p);
DWORD WINAPI WorkerGuardThreadFun(_In_ LPVOID p);
void Start(WorkerThreadHolder *workerThreadHolderPtr);
void ExecuteTask(WorkerThreadHolder *workerThreadHolderPtr);
/*----------------------------------------------------------------------------*/
DWORD WINAPI WorkerThreadFun(_In_ LPVOID p)
{
/* use stack variables only in this thread in hope the stack will be deallocated by TerminateThread() */
WorkerThreadHolder *workerThreadHolderPtr = (WorkerThreadHolder *)p;
volatile int i;
workerThreadHolderPtr->mIsWorkerStarted = 1;
/*WakeAllConditionVariable(&workerThreadHolderPtr->mThreadReadyCond);*/
/* do nothing for infinite long time */
for(i = 0;; ++i)
i = i;
/*WakeAllConditionVariable(&workerThreadHolderPtr->mTaskFinishedCond);*/
return 0;
}
DWORD WINAPI WorkerGuardThreadFun(_In_ LPVOID p)
{
const DWORD taskExecutionTimeoutInMillisec = 1;
WorkerThreadHolder *workerThreadHolderPtr = (WorkerThreadHolder *)p;
EnterCriticalSection(&workerThreadHolderPtr->mLock);
workerThreadHolderPtr->mGuardThreadState = GRD_READY;
WakeAllConditionVariable(&workerThreadHolderPtr->mThreadReadyCond);
for (;;)
{
for (;;)
{
SleepConditionVariableCS(
&workerThreadHolderPtr->mStartTaskCond,
&workerThreadHolderPtr->mLock,
INFINITE);
if (workerThreadHolderPtr->mGuardThreadState == GRD_TASKSTARTING)
break;
}
workerThreadHolderPtr->mGuardThreadState = GRD_TASKWAITING;
{
BOOL isTaskFinishedOk = FALSE;
for (;;)
{
isTaskFinishedOk =
SleepConditionVariableCS(
&workerThreadHolderPtr->mTaskFinishedCond,
&workerThreadHolderPtr->mLock,
taskExecutionTimeoutInMillisec);
if (!isTaskFinishedOk)
break;
}
if (isTaskFinishedOk)
{
/* never happens in this test */
}
else
{
BOOL isClosed;
TerminateThread(workerThreadHolderPtr->mHworkerThread, 0);
/*if (RtlFreeUserThreadStack != NULL)
RtlFreeUserThreadStack(GetCurrentProcess(), workerThreadHolderPtr->mHworkerThread);*/
isClosed = CloseHandle(workerThreadHolderPtr->mHworkerThread);
workerThreadHolderPtr->mIsWorkerStarted = 0;
workerThreadHolderPtr->mHworkerThread =
CreateThread(
NULL,
STK_SIZE,
WorkerThreadFun,
(PVOID)workerThreadHolderPtr,
STACK_SIZE_PARAM_IS_A_RESERVATION,
NULL);
}
}
workerThreadHolderPtr->mGuardThreadState = GRD_READY;
WakeAllConditionVariable(&workerThreadHolderPtr->mThreadReadyCond);
}
return 0;
}
void Start(WorkerThreadHolder *workerThreadHolderPtr)
{
workerThreadHolderPtr->mGuardThreadState = GRD_IDLE;
workerThreadHolderPtr->mIsWorkerStarted = 0;
InitializeConditionVariable(&workerThreadHolderPtr->mThreadReadyCond);
InitializeConditionVariable(&workerThreadHolderPtr->mStartTaskCond);
InitializeConditionVariable(&workerThreadHolderPtr->mTaskFinishedCond);
InitializeCriticalSection(&workerThreadHolderPtr->mLock);
workerThreadHolderPtr->mHworkerThread =
CreateThread(
NULL,
STK_SIZE,
WorkerThreadFun,
(LPVOID)workerThreadHolderPtr,
STACK_SIZE_PARAM_IS_A_RESERVATION,
NULL);
workerThreadHolderPtr->mHworkerGroupThread =
CreateThread(
NULL,
0,
WorkerGuardThreadFun,
(LPVOID)workerThreadHolderPtr,
0,
NULL);
}
void ExecuteTask(WorkerThreadHolder *workerThreadHolderPtr)
{
assert(workerThreadHolderPtr->mHworkerThread != NULL);
assert(workerThreadHolderPtr->mHworkerGroupThread != NULL);
EnterCriticalSection(&workerThreadHolderPtr->mLock);
for (;;)
{
if (workerThreadHolderPtr->mGuardThreadState == GRD_READY /* && workerThreadHolderPtr->mIsWorkerStarted != 0 */)
break;
SleepConditionVariableCS(
&workerThreadHolderPtr->mThreadReadyCond,
&workerThreadHolderPtr->mLock,
INFINITE);
}
/* just poll */
for (;;)
{
if (workerThreadHolderPtr->mIsWorkerStarted != 0)
break;
}
workerThreadHolderPtr->mGuardThreadState = GRD_TASKSTARTING;
WakeAllConditionVariable(&workerThreadHolderPtr->mStartTaskCond);
LeaveCriticalSection(&workerThreadHolderPtr->mLock);
}
/*----------------------------------------------------------------------------*/
int main(int argc, char *argv[])
{
int i;
WorkerThreadHolder workerThreadHolder;
/*
HMODULE NTLibrary = GetModuleHandleW(L"ntdll.dll");
RtlFreeUserThreadStack = (PRtlFreeUserThreadStack)GetProcAddress(NTLibrary, "RtlFreeUserThreadStack");
*/
Start(&workerThreadHolder);
for(i = 0;; ++i)
{
ExecuteTask(&workerThreadHolder);
printf("%d Execution started...\n", i);
/*fflush(stdout);*/
}
return 0;
}
Протестировано с Visual Studio 2015, командная строка vc: / GS- / analysis- / W3 / Zc: wchar_t / ZI / Gm / Od/Fd"Debug\vc140.pdb "/ Zc: встроенный / fp: точный / D" WIN32 "/ D" _DEBUG "/ D" _CONSOLE "/ D" _UNICODE "/ D" UNICODE "/ errorReport: prompt / WX- /Zc: forScope / Gd / Oy / MDd / Fa "Debug \" / nologo / Fo "Debug \" /Fp"Debug\ConsoleApplication1.pch "
компоновщик: / OUT:" C: \ Users \ cherney\ Documents \ visual studio 2015 \ Проекты \ ConsoleApplication1 \ Debug \ ConsoleApplication1.exe "/ MANIFEST / NXCOMPAT / PDB:" C: \ Users \ cherney \ Documents \ visual studio 2015 \ Проекты \ ConsoleApplication1 \ Debug \ ConsoleApplication1.pdb "/ DYNAMICBASE"kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib""odbc32.lib" "odbccp32.lib" / DEBUG / MACHINE: X86 / INCREMENTAL / PGD: "C: \ Users \ cherney \ Documents \ visual studio 2015 \ Projects \ ConsoleApplication1 \ Debug \ ConsoleApplication1.pgd" / SUBSYSTEM: CONSOLE /MANIFESTUAC: "level = 'asInvoker' uiAccess = 'false'" /ManifestFile:"Debug\ConsoleApplication1.exe.intermediate.manifest "/ ERRORREPORT: PROMPT / NOLOGO / TLBID: 1