PingPlugin.dll: Removed use of TerminateThread

Instead of termination in Finalize(), the thread is left to run and perform required cleanup before exit. This also fixes a memory leak caused by termination in some cases.
This commit is contained in:
Birunthan Mohanathas 2012-06-28 12:57:41 +03:00
parent 5634333e60
commit e4cceb8512

View File

@ -21,8 +21,6 @@
#include <Icmpapi.h> #include <Icmpapi.h>
#include "../../Library/Export.h" // Rainmeter's exported functions #include "../../Library/Export.h" // Rainmeter's exported functions
#include "../../Library/DisableThreadLibraryCalls.h" // contains DllMain entry point
struct MeasureData struct MeasureData
{ {
IPAddr destAddr; IPAddr destAddr;
@ -30,7 +28,7 @@ struct MeasureData
double timeoutValue; double timeoutValue;
DWORD updateRate; DWORD updateRate;
DWORD updateCounter; DWORD updateCounter;
HANDLE threadHandle; bool threadActive;
double value; double value;
MeasureData() : MeasureData() :
@ -39,26 +37,37 @@ struct MeasureData
timeoutValue(), timeoutValue(),
updateRate(), updateRate(),
updateCounter(), updateCounter(),
threadHandle(), threadActive(false),
value() value()
{ {
} }
}; };
static CRITICAL_SECTION g_CriticalSection; static CRITICAL_SECTION g_CriticalSection;
static UINT g_Instances = 0;
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
InitializeCriticalSection(&g_CriticalSection);
// Disable DLL_THREAD_ATTACH and DLL_THREAD_DETACH notification calls.
DisableThreadLibraryCalls(hinstDLL);
break;
case DLL_PROCESS_DETACH:
DeleteCriticalSection(&g_CriticalSection);
break;
}
return TRUE;
}
PLUGIN_EXPORT void Initialize(void** data, void* rm) PLUGIN_EXPORT void Initialize(void** data, void* rm)
{ {
MeasureData* measure = new MeasureData; MeasureData* measure = new MeasureData;
*data = measure; *data = measure;
if (g_Instances == 0)
{
InitializeCriticalSection(&g_CriticalSection);
}
++g_Instances;
} }
PLUGIN_EXPORT void Reload(void* data, void* rm, double* maxValue) PLUGIN_EXPORT void Reload(void* data, void* rm, double* maxValue)
@ -108,59 +117,71 @@ PLUGIN_EXPORT void Reload(void* data, void* rm, double* maxValue)
measure->timeoutValue = RmReadDouble(rm, L"TimeoutValue", 30000.0); measure->timeoutValue = RmReadDouble(rm, L"TimeoutValue", 30000.0);
} }
DWORD WINAPI NetworkThreadProc(LPVOID pParam) DWORD WINAPI NetworkThreadProc(void* pParam)
{ {
// NOTE: Do not use CRT functions (since thread was created by CreateThread())!
MeasureData* measure = (MeasureData*)pParam; MeasureData* measure = (MeasureData*)pParam;
const DWORD bufferSize = sizeof(ICMP_ECHO_REPLY) + 32;
BYTE buffer[bufferSize];
const DWORD replySize = sizeof(ICMP_ECHO_REPLY) + 32; double value = 0.0;
BYTE* reply = new BYTE[replySize];
HANDLE hIcmpFile = IcmpCreateFile(); HANDLE hIcmpFile = IcmpCreateFile();
if (hIcmpFile != INVALID_HANDLE_VALUE) if (hIcmpFile != INVALID_HANDLE_VALUE)
{ {
IcmpSendEcho(hIcmpFile, measure->destAddr, NULL, 0, NULL, reply, replySize, measure->timeout); IcmpSendEcho(hIcmpFile, measure->destAddr, NULL, 0, NULL, buffer, bufferSize, measure->timeout);
IcmpCloseHandle(hIcmpFile); IcmpCloseHandle(hIcmpFile);
ICMP_ECHO_REPLY* reply = (ICMP_ECHO_REPLY*)buffer;
value = (reply->Status == IP_REQ_TIMED_OUT) ? measure->timeoutValue : reply->RoundTripTime;
} }
EnterCriticalSection(&g_CriticalSection); HMODULE module = NULL;
ICMP_ECHO_REPLY* pReply = (ICMP_ECHO_REPLY*)reply; EnterCriticalSection(&g_CriticalSection);
if (pReply->Status == IP_REQ_TIMED_OUT) if (measure->threadActive)
{ {
measure->value = measure->timeoutValue; measure->value = value;
measure->threadActive = false;
} }
else else
{ {
measure->value = pReply->RoundTripTime; // Thread is not attached to an existing measure any longer, so delete
// unreferenced data.
delete measure;
DWORD flags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT;
GetModuleHandleEx(flags, (LPCWSTR)DllMain, &module);
} }
delete [] reply;
CloseHandle(measure->threadHandle);
measure->threadHandle = NULL;
LeaveCriticalSection(&g_CriticalSection); LeaveCriticalSection(&g_CriticalSection);
if (module)
{
// Decrement the ref count and possibly unload the module if this is
// the last instance.
FreeLibraryAndExitThread(module, 0);
}
return 0; return 0;
} }
PLUGIN_EXPORT double Update(void* data) PLUGIN_EXPORT double Update(void* data)
{ {
MeasureData* measure = (MeasureData*)data; MeasureData* measure = (MeasureData*)data;
double value = 0.0;
EnterCriticalSection(&g_CriticalSection); EnterCriticalSection(&g_CriticalSection);
value = measure->value; if (!measure->threadActive)
LeaveCriticalSection(&g_CriticalSection);
if (measure->threadHandle == NULL)
{ {
if (measure->updateCounter == 0) if (measure->updateCounter == 0)
{ {
// Launch a new thread to fetch the web data // Launch a new thread to fetch the web data
DWORD id; DWORD id;
measure->threadHandle = CreateThread(NULL, 0, NetworkThreadProc, measure, 0, &id); HANDLE thread = CreateThread(NULL, 0, NetworkThreadProc, measure, 0, &id);
if (thread)
{
CloseHandle(thread);
measure->threadActive = true;
}
} }
measure->updateCounter++; measure->updateCounter++;
@ -170,6 +191,9 @@ PLUGIN_EXPORT double Update(void* data)
} }
} }
double value = measure->value;
LeaveCriticalSection(&g_CriticalSection);
return value; return value;
} }
@ -178,18 +202,20 @@ PLUGIN_EXPORT void Finalize(void* data)
MeasureData* measure = (MeasureData*)data; MeasureData* measure = (MeasureData*)data;
EnterCriticalSection(&g_CriticalSection); EnterCriticalSection(&g_CriticalSection);
if (measure->threadHandle) if (measure->threadActive)
{ {
// Should really wait until the thread finishes instead terminating it... // Increment ref count of this module so that it will not be unloaded prior to
TerminateThread(measure->threadHandle, 0); // thread completion.
DWORD flags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS;
HMODULE module;
GetModuleHandleEx(flags, (LPCWSTR)DllMain, &module);
// Thread will perform cleanup.
measure->threadActive = false;
}
else
{
delete measure;
} }
LeaveCriticalSection(&g_CriticalSection); LeaveCriticalSection(&g_CriticalSection);
--g_Instances;
if (g_Instances == 0)
{
DeleteCriticalSection(&g_CriticalSection);
}
delete measure;
} }