|
|
/*++
Copyright (c) 2000-2001 Microsoft Corporation
Module Name:
thread_manager.cxx
Abstract:
implementation for THREAD_MANAGER
Author:
Jeffrey Wall (jeffwall) 11-28-2000
Revision History:
--*/
#include <iis.h>
#include <dbgutil.h>
#include "thread_manager.h"
#include "thread_pool_private.h"
TCHAR g_szModuleName[MAX_PATH]; DWORD g_dwcCPU = 1;
HMODULE g_hmodW3TPDLL = NULL;
BOOL TooMuchContextSwitchingLoad(ULONG ulFirstSample, DWORD dwFirstSampleTime, ULONG ulPerSecondSwitchRateMax, DWORD dwNumProcs);
BOOL TooMuchProcessorUsage(LARGE_INTEGER liOriginalBusy, LARGE_INTEGER liOriginalTotal, LONG lPercentageUseMax, DWORD dwNumProcs);
BOOL GetContextSwitchCount(ULONG * pulSwitchCount);
HRESULT GetCPUData(LARGE_INTEGER * pBusyTime, LARGE_INTEGER * pTotalTime, DWORD dwNumProcs);
//static
HRESULT THREAD_MANAGER::CreateThreadManager(THREAD_MANAGER ** ppManager, THREAD_POOL * pPool, THREAD_POOL_DATA * pPoolData) /*++
Routine Description: Allocate and initialize a THREAD_MANAGER
Arguments:
ppManager - where to store allocated manager pointer pPool - pointer to THREAD_POOL associated with this manager pPoolData - pointer to THREAD_POOL_DATA associated with this manager
Return Value:
HRESULT --*/ { DBG_ASSERT(NULL != ppManager); DBG_ASSERT(NULL != pPool); DBG_ASSERT(NULL != pPoolData);
*ppManager = NULL;
HRESULT hr = S_OK;
THREAD_MANAGER * pManager = new THREAD_MANAGER(pPool, pPoolData); if (NULL == pManager) { hr = E_OUTOFMEMORY; goto done; }
hr = pManager->Initialize(); if (FAILED(hr)) { // don't call TerminateThreadManager - just delete it
delete pManager;
goto done; }
*ppManager = pManager;
hr = S_OK; done: return hr; }
VOID THREAD_MANAGER::TerminateThreadManager(LPTHREAD_STOP_ROUTINE lpStopAddress, LPVOID lpParameter) /*++
Routine Description: Shutdown ALL THREAD_MANAGER theads and destroy a THREAD_MANAGER
Arguments:
lpStopAddress - address of function that will stop threads lpParameter - argument to pass to stop function
Return Value:
VOID --*/ { // block until threads are gone
DrainThreads(lpStopAddress, lpParameter);
// do some initializion succeeded only cleanup
if (m_hShutdownEvent) { CloseHandle(m_hShutdownEvent); m_hShutdownEvent = NULL; } if (m_hParkEvent) { CloseHandle(m_hParkEvent); m_hParkEvent = NULL; }
DeleteCriticalSection(&m_CriticalSection);
// don't do anything else after deletion
delete this;
return; }
THREAD_MANAGER::THREAD_MANAGER(THREAD_POOL * pPool, THREAD_POOL_DATA * pPoolData) : m_dwSignature(SIGNATURE_THREAD_MANAGER), m_fShuttingDown(FALSE), m_fWaitingForCreationCallback(FALSE), m_hTimer(NULL), m_pParam(NULL), m_hParkEvent(NULL), m_hShutdownEvent(NULL), m_lParkedThreads(0), m_pPool(pPool), m_pPoolData(pPoolData), m_lTotalThreads(0) /*++
Routine Description:
Constructs the ThreadManager
Arguments:
None
Return Value:
None
--*/ { DBG_ASSERT(m_pPool); DBG_ASSERT(m_pPoolData); ZeroMemory(&m_liOriginalBusy, sizeof(LARGE_INTEGER)); ZeroMemory(&m_liOriginalTotal, sizeof(LARGE_INTEGER));
return; }
HRESULT THREAD_MANAGER::Initialize() /*++
Routine Description: Do initialization for THREAD_MANAGER
Arguments:
VOID Return Value:
HRESULT --*/ { HRESULT hr = S_OK;
DWORD dwRet; HKEY hKey; BOOL fRet;
// Must be set by the DLL main of the DLL loading w3tp
DBG_ASSERT( NULL != g_hmodW3TPDLL ); if ( GetModuleFileNameW( g_hmodW3TPDLL, g_szModuleName, MAX_PATH ) == 0 ) { DBG_ASSERT(FALSE && "Failed getting module name for w3tp.dll"); hr = E_FAIL; goto done; }
m_hParkEvent = CreateEvent(NULL, // security descriptor
FALSE, // auto reset
FALSE, // not signaled at creation
NULL // event name
); if (NULL == m_hParkEvent) { DBG_ASSERT(FALSE && "Could not create parking event"); hr = E_FAIL; goto done; } m_hShutdownEvent = CreateEvent(NULL, // security descriptor
TRUE, // manual reset
FALSE, // not signaled at creation
NULL // event name
); if (NULL == m_hShutdownEvent) { DBG_ASSERT(FALSE && "Could not create shutdown event"); hr = E_FAIL; goto done; }
// keep this at the end of Initialize - if it fails, no need to clean it up ever
// If it succeededs, no need to clean it up in this function.
// By setting the high order bit for dwSpinCount, we preallocate the CriticalSection
fRet = InitializeCriticalSectionAndSpinCount(&m_CriticalSection, 0x80000000 ); if (FALSE == fRet) { DBG_ASSERT(FALSE && "Could not initialize critical section!"); hr = E_FAIL; goto done; }
hr = S_OK; done: if (FAILED(hr)) { if (m_hParkEvent) { CloseHandle(m_hParkEvent); m_hParkEvent = NULL; } if (m_hShutdownEvent) { CloseHandle(m_hShutdownEvent); m_hShutdownEvent = NULL; } }
return hr; }
THREAD_MANAGER::~THREAD_MANAGER() /*++
Routine Description:
Destructs the ThreadManager
Arguments:
None
Return Value:
None
--*/ { DBG_ASSERT(SIGNATURE_THREAD_MANAGER == m_dwSignature); m_dwSignature = SIGNATURE_THREAD_MANAGER_FREE;
DBG_ASSERT(TRUE == m_fShuttingDown && "DrainThreads was not called!");
DBG_ASSERT(NULL == m_hTimer); DBG_ASSERT(NULL == m_pParam); DBG_ASSERT(0 == m_lParkedThreads); DBG_ASSERT(0 == m_lTotalThreads);
m_pPool = NULL; m_pPoolData = NULL; }
//static
DWORD THREAD_MANAGER::ThreadManagerThread(LPVOID ThreadParam) /*++
Routine Description: Starter thread for THREAD_MANAGER created threads Takes a reference against the current DLL Notifies THREAD_MANAGER that it has started execution Calls out to "real" thread procedure Notifies THREAD_MANAGER that it is about to terminate Releases reference to current DLL and terminates
Arguments:
ThreadParam - parameters for control of thread
Return Value:
win32 error or return value from "real" thread proc --*/ { HMODULE hModuleDll; DWORD dwReturnCode; THREAD_PARAM *pParam = NULL; pParam = (THREAD_PARAM*)ThreadParam;
// grab a reference to this DLL
hModuleDll = LoadLibrary(g_szModuleName); if (NULL == hModuleDll) { dwReturnCode = GetLastError(); goto done; }
// verify the thread parameter passed was reasonable
DBG_ASSERT(NULL != pParam); DBG_ASSERT(NULL != pParam->pThreadManager); DBG_ASSERT(NULL != pParam->pThreadFunc);
if (pParam->fCallbackOnCreation) { // Inform thread manager that this thread has successfully gotten through the loader lock
pParam->pThreadManager->CreatedSuccessfully(pParam); }
// actually do work thread is supposed to do
dwReturnCode = pParam->pThreadFunc(pParam->pvThreadArg);
done: // Inform thread manager that this thread is going away
pParam->pThreadManager->RemoveThread(pParam);
// Thread owns[ed] memory passed
delete pParam;
// release reference to this DLL
FreeLibraryAndExitThread(hModuleDll, dwReturnCode);
// never executed
return dwReturnCode; }
VOID THREAD_MANAGER::RequestThread(LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpStartParameter) /*++
Routine Description:
Creates a timer to determine the correct thread action to take.
May create a thread in a little while May take away a thread in a little while May not create the timer if there is another thread creation going on
Arguments:
lpStartAddress - address of function to begin thread execution lpParameter - argument to pass to start function
Return Value:
VOID
--*/ { BOOL fRet = FALSE; HRESULT hr; DBG_ASSERT(NULL != lpStartAddress);
if (TRUE == m_fShuttingDown || TRUE == m_fWaitingForCreationCallback) { return; }
//
// only want to create one timer at a time
//
EnterCriticalSection(&m_CriticalSection);
if (TRUE == m_fShuttingDown || TRUE == m_fWaitingForCreationCallback) { LeaveCriticalSection(&m_CriticalSection); return; }
// only want one thread at a time to be created
m_fWaitingForCreationCallback = TRUE;
DBGPRINTF(( DBG_CONTEXT, "W3TP: Thread Request received\n"));
DWORD dwCurrentTime = GetTickCount();
fRet = GetContextSwitchCount(&m_ulContextSwitchCount); if (FALSE == fRet) { goto done; }
if ( THREAD_POOL_MAX_CPU_USAGE_DEFAULT != m_pPoolData->m_poolConfig.dwMaxCPUUsage) { hr = GetCPUData(&m_liOriginalBusy, &m_liOriginalTotal, g_dwcCPU); if (FAILED(hr)) { fRet = FALSE; goto done; } } DBG_ASSERT(NULL == m_pParam); m_pParam = new THREAD_PARAM; if (NULL == m_pParam) { fRet = FALSE; goto done; }
m_pParam->pThreadFunc = lpStartAddress; m_pParam->pvThreadArg = lpStartParameter; m_pParam->pThreadManager = this; m_pParam->dwRequestTime = dwCurrentTime; m_pParam->fCallbackOnCreation = TRUE;
if (NULL != m_hTimer) { // if this isn't the first time we've requested a thread,
// we have a previous TimerQueueTimer. This timer was not
// removed during the callback, so we have to clean it up now.
// this is the blocking form of the delete operation, however
// since the timer has already fired it will not block.
fRet = DeleteTimerQueueTimer(NULL, // default timer queue
m_hTimer, // previous timer handle
INVALID_HANDLE_VALUE // wait until it is removed
); m_hTimer = NULL; }
DBG_ASSERT(NULL == m_hTimer); fRet = CreateTimerQueueTimer(&m_hTimer, // storage for timer handle
NULL, // default timer queue
ControlTimerCallback, // callback function
this, // callback argument
m_pPoolData->m_poolConfig.dwTimerPeriod, // time til callback
0, // repeat time
WT_EXECUTEONLYONCE // no repition
); if (FALSE == fRet) { goto done; }
fRet = TRUE; done: if (FALSE == fRet) { delete m_pParam; m_pParam = NULL;
m_fWaitingForCreationCallback = FALSE; }
LeaveCriticalSection(&m_CriticalSection); return; }
//static
VOID THREAD_MANAGER::ControlTimerCallback(PVOID lpParameter, BOOLEAN TimerOrWaitFired) /*++
Routine Description:
Callback for timer created in RequestThread
restores this pointer and forwards to DetermineThreadAction
Arguments: lpParameter - THREAD_MANAGER this pointer
Return Value: VOID --*/ { THREAD_MANAGER* pThreadManager = (THREAD_MANAGER*)lpParameter;
DBG_ASSERT(NULL != pThreadManager); DBG_ASSERT(NULL != pThreadManager->m_pParam); DBG_ASSERT(TRUE == pThreadManager->m_fWaitingForCreationCallback);
pThreadManager->DetermineThreadAction();
return; }
VOID THREAD_MANAGER::DetermineThreadAction() /*++
Routine Description:
Try to determine correct action, create or take away a thread
Will take away a thread if context switch rate is too high
m_pParam must be populated Arguments:
VOID
Return Value:
VOID
--*/ { BOOL fRet = FALSE; DWORD dwElapsedTime = 0; DWORD dwCurrentTime = 0;
EnterCriticalSection(&m_CriticalSection);
DBG_ASSERT(NULL != m_pParam);
if (TRUE == m_fShuttingDown) { fRet = FALSE; goto done; }
DBG_ASSERT(TRUE == m_fWaitingForCreationCallback);
if (THREAD_POOL_MAX_CPU_USAGE_DEFAULT != m_pPoolData->m_poolConfig.dwMaxCPUUsage) { if (TooMuchProcessorUsage(m_liOriginalBusy, m_liOriginalTotal, m_pPoolData->m_poolConfig.dwMaxCPUUsage, g_dwcCPU)) { // Too much processor usage to create a new thread
fRet = FALSE; goto done; } } if (TooMuchContextSwitchingLoad(m_ulContextSwitchCount, m_pParam->dwRequestTime, m_pPoolData->m_poolConfig.dwPerSecondContextSwitchMax, g_dwcCPU)) { // Switching too much
DoThreadParking(); fRet = FALSE; goto done; }
fRet = DoThreadCreation(m_pParam); if (!fRet) { goto done; }
fRet = TRUE; done: if (FALSE == fRet) { delete m_pParam; m_pParam = NULL;
m_fWaitingForCreationCallback = FALSE; } else { // thread now has responsibility for the memory
m_pParam = NULL; }
LeaveCriticalSection(&m_CriticalSection);
return; }
BOOL THREAD_MANAGER::DoThreadCreation(THREAD_PARAM *pParam) /*++
Routine Description:
If there are no threads in parked state
Creates a thread, add the HANDLE to the internal list of handles add the creation time to the internal list of times
Otherwise, bring a thread out of parked state
Arguments: pParam - parameter to pass to created thread
Return Value:
TRUE if thread is created successfully FALSE if thread is not created - either a thread was returned from parked state, OR there was a problem with creation.
--*/ { BOOL fRet = FALSE; HANDLE hThread = NULL;
if (DoThreadUnParking()) { // we have not created a new thread
DBGPRINTF(( DBG_CONTEXT, "W3TP: Signaled a thread to be unparked\n"));
// return false - signal that pParam needs to be freed by the caller
fRet = FALSE; goto done; }
// bugbug: use _beginthreadex?
hThread = ::CreateThread( NULL, // default security descriptor
m_pPoolData->m_poolConfig.dwInitialStackSize, // Initial size as configured
ThreadManagerThread, // thread function
pParam, // thread argument
0, // create running
NULL // don't care for thread identifier
); if( NULL == hThread ) { fRet = FALSE; goto done; }
// don't keep the handle around
CloseHandle(hThread);
DBGPRINTF(( DBG_CONTEXT, "W3TP: Created a new thread\n"));
InterlockedIncrement(&m_lTotalThreads);
// we've successfully created the thread!
fRet = TRUE; done: return fRet; }
VOID THREAD_MANAGER::DoThreadParking() /*++
Routine Description:
Called when context switch rate has been determined to be too high Removes a thread from participation in the thread pool, and parks it
Arguments: none
Return Value:
VOID --*/ { BOOL fRet;
// make sure that we leave the starting number of threads in the pool
if (m_pPoolData && m_lTotalThreads - m_lParkedThreads <= (LONG) m_pPoolData->m_poolConfig.dwInitialThreadCount) { return; }
DBGPRINTF(( DBG_CONTEXT, "W3TP: Posting to park a thread\n"));
fRet = m_pPool->PostCompletion(0, ParkThread, (LPOVERLAPPED)this); DBG_ASSERT(TRUE == fRet); return; }
BOOL THREAD_MANAGER::DoThreadUnParking() /*++
Routine Description: Release one thread from the parked state
Arguments:
VOID Return Value:
BOOL - TRUE if thread was released FALSE if no threads were available to release --*/ { if (0 == m_lParkedThreads) { return FALSE; } SetEvent(m_hParkEvent); return TRUE; }
//static
VOID THREAD_MANAGER::ParkThread(DWORD dwErrorCode, DWORD dwNumberOfBytesTransferred, LPOVERLAPPED lpo) /*++
Routine Description: Put a THREAD_MANAGER thread in a parked state
Arguments:
dwErrorCode - not used dwNumberOfBytesTransferred - not used lpo - pointer to overlapped that is really a pointer to a THREAD_MANAGER Return Value:
VOID --*/ { DWORD dwRet = 0; THREAD_MANAGER * pThis= (THREAD_MANAGER*)lpo; DBG_ASSERT(NULL != pThis);
HANDLE arrHandles[2]; arrHandles[0] = pThis->m_hParkEvent; arrHandles[1] = pThis->m_hShutdownEvent;
DBGPRINTF(( DBG_CONTEXT, "W3TP: Thread parking\n"));
LONG lNewParkedThreads = InterlockedIncrement(&pThis->m_lParkedThreads);
if (pThis->m_pPoolData && pThis->m_lTotalThreads - lNewParkedThreads <= (LONG) pThis->m_pPoolData->m_poolConfig.dwInitialThreadCount) { InterlockedDecrement(&pThis->m_lParkedThreads); return; } THREAD_POOL_DATA * pPoolData = pThis->m_pPoolData; DWORD dwTimeout = pPoolData->m_poolConfig.dwThreadTimeout; dwRet = WaitForMultipleObjects(2, arrHandles, FALSE, dwTimeout);
InterlockedDecrement(&pThis->m_lParkedThreads);
DBGPRINTF(( DBG_CONTEXT, "W3TP: Thread unparked\n"));
if (WAIT_TIMEOUT == dwRet) { THREAD_POOL_DATA::ThreadPoolStop(pPoolData); return; } DBG_ASSERT(WAIT_OBJECT_0 == dwRet || WAIT_OBJECT_0 + 1 == dwRet);
return; }
BOOL THREAD_MANAGER::CreateThread(LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter) /*++
Routine Description:
Creates a thread if no other thread is being created currently
Arguments:
lpStartAddress - address of function to begin thread execution lpParameter - argument to pass to start function
Return Value:
TRUE if thread is created successfully FALSE if thread is not created
--*/ { BOOL fRet = FALSE; THREAD_PARAM * pParam = NULL;
EnterCriticalSection(&m_CriticalSection);
if (TRUE == m_fShuttingDown) { fRet = FALSE; goto done; }
pParam = new THREAD_PARAM; if (NULL == pParam) { fRet = FALSE; goto done; }
DBGPRINTF(( DBG_CONTEXT, "W3TP: CreateThread thread creation\n"));
pParam->pThreadFunc = lpStartAddress; pParam->pvThreadArg = lpParameter; pParam->pThreadManager = this; pParam->dwRequestTime = GetTickCount(); pParam->fCallbackOnCreation = FALSE;
fRet = DoThreadCreation(pParam); if (FALSE == fRet) { goto done; }
// created thread has responsibility for this memory
pParam = NULL;
fRet = TRUE; done: if (FALSE == fRet) { delete pParam; pParam = NULL;
m_fWaitingForCreationCallback = FALSE; }
LeaveCriticalSection(&m_CriticalSection); return fRet; }
VOID THREAD_MANAGER::RemoveThread(THREAD_PARAM * pParam) /*++
Routine Description:
Removes given thread from list of active threads and closes handle
Arguments:
hThreadSelf - handle to current thread
Return Value:
void --*/ { InterlockedDecrement(&m_lTotalThreads); return; }
VOID THREAD_MANAGER::CreatedSuccessfully(THREAD_PARAM * pParam) /*++
Routine Description:
Notification that given thread has successfully started
Arguments:
hThread - current thread handle
Return Value:
VOID
--*/ { DBG_ASSERT(pParam);
EnterCriticalSection(&m_CriticalSection);
DBG_ASSERT(m_fWaitingForCreationCallback);
m_fWaitingForCreationCallback = FALSE;
LeaveCriticalSection(&m_CriticalSection);
return; }
VOID THREAD_MANAGER::DrainThreads(LPTHREAD_STOP_ROUTINE lpStopAddress, LPVOID lpParameter) /*++
Routine Description:
stop all threads currently being managed. Doesn't return until all threads are stopped.
Arguments:
lpStopAddress - address of function to call to signal one thread to stop
Return Value:
TRUE if all threads are stopped FALSE if one or more threads could not be stopped
--*/ { if (TRUE == m_fShuttingDown) { DBG_ASSERT(FALSE && "DrainThreads has been called previously!"); return; }
EnterCriticalSection(&m_CriticalSection); // stop any additional thread creation
m_fShuttingDown = TRUE; LeaveCriticalSection(&m_CriticalSection);
// release all parked threads
SetEvent(m_hShutdownEvent);
// push as many stops are there are threads running
for (INT i = m_lTotalThreads; i >= 0; i--) //TODO: think about sync with interlocked and m_lTotalThreads - is there a race here?
{ lpStopAddress(lpParameter); }
// stop the callback timer
if (NULL != m_hTimer) { // block until timer is deleted
DeleteTimerQueueTimer(NULL, m_hTimer, INVALID_HANDLE_VALUE); m_hTimer = NULL; if (m_pParam) { // the ownership for m_pParam moves from the creator
// to the timer to the thread.
// However, we just destroyed the timer - need to cleanup
// the memory
delete m_pParam; m_pParam = NULL; } }
while(m_lTotalThreads > 0) { DBGPRINTF(( DBG_CONTEXT, "W3TP: Waiting for threads to drain, sleep 1000 \n")); Sleep(1000); }
DBGPRINTF(( DBG_CONTEXT, "W3TP: All threads drained\n"));
return; }
BOOL GetContextSwitchCount(ULONG * pulSwitchCount) /*++
Routine Description:
Get the current machine context switch count
Arguments:
pulSwitchCount - where to store switch count
Return Value:
TRUE if context switch count is read correctly FALSE if context switch count could not be read
--*/ { DBG_ASSERT(NULL != pulSwitchCount);
SYSTEM_PERFORMANCE_INFORMATION spi; ULONG ulReturnLength; NTSTATUS status; status = NtQuerySystemInformation(SystemPerformanceInformation, &spi, sizeof(spi), &ulReturnLength); if (!NT_SUCCESS(status)) { return FALSE; }
*pulSwitchCount = spi.ContextSwitches;
return TRUE; }
BOOL TooMuchContextSwitchingLoad(ULONG ulFirstSample, DWORD dwFirstSampleTime, ULONG ulPerSecondSwitchRateMax, DWORD dwNumProcs) /*++
Routine Description:
Determine if the system is under too much load in terms of context switches / second
If dwNumProcs > 1 - the per second switch rate per processor is multiplied by two
Arguments:
ulFirstSample - first context switch count number dwSampleTimeInMilliseconds - how much time between first sample and calling this function ulPerSecondSwitchRateMax - Maximum switch rate per processor dwNumProcs - number of processors on machine
Return Value:
TRUE if context switch rate per second per processor is > ulPerSecondSwitchRateMax FALSE if context switch rate is below ulPerSecondSwitchRateMax
--*/ { ULONG ulSecondSample = 0; ULONG ulContextSwitchDifference = 0; double dblPerSecondSwitchRate = 0; double dblPerSecondSwitchRatePerProcessor = 0; DWORD dwCurrentTime = 0; DWORD dwElapsedTime = 0;
BOOL fRet = FALSE;
fRet = GetContextSwitchCount(&ulSecondSample); if (FALSE == fRet) { goto done; }
dwCurrentTime = GetTickCount(); if (dwCurrentTime <= dwFirstSampleTime) { // wrap around on time occurred - assume only one wrap around
const DWORD MAXDWORD = MAXULONG; dwElapsedTime = MAXDWORD - dwFirstSampleTime + dwCurrentTime; } else { // no wrap around
dwElapsedTime = dwCurrentTime - dwFirstSampleTime; } DBG_ASSERT(dwElapsedTime > 0);
if (ulSecondSample <= ulFirstSample) { // wrap around on counter occurred - assume only one wrap around
ulContextSwitchDifference = (MAXULONG - ulFirstSample) + ulSecondSample; } else { // no wrap around
ulContextSwitchDifference = ulSecondSample - ulFirstSample; } DBG_ASSERT(ulContextSwitchDifference > 0);
dblPerSecondSwitchRate = ulContextSwitchDifference / ( dwElapsedTime / 1000.0);
dblPerSecondSwitchRatePerProcessor = dblPerSecondSwitchRate / dwNumProcs;
if (dwNumProcs > 1) { // on multiproc boxes, double the allowed context switch rate per processor
ulPerSecondSwitchRateMax *= 2; }
if (dblPerSecondSwitchRatePerProcessor > ulPerSecondSwitchRateMax) { DBGPRINTF(( DBG_CONTEXT, "W3TP: Not creating thread, ContextSwitch rate is: %g\n", dblPerSecondSwitchRate )); fRet = TRUE; goto done; }
DBGPRINTF(( DBG_CONTEXT, "W3TP: OK to create thread, ContextSwitch rate is: %g\n", dblPerSecondSwitchRate ));
fRet = FALSE; done: return fRet; }
HRESULT GetCPUData(LARGE_INTEGER * pBusyTime, LARGE_INTEGER * pTotalTime, DWORD dwNumProcs) /*++
Routine Description:
Collects the percent CPU load for all machine processors.
Arguments:
None
Return Value:
Std HRESULT.
--*/
{ DBG_ASSERT(pBusyTime && pTotalTime); DBG_ASSERT(dwNumProcs > 0); HRESULT hr = S_OK; NTSTATUS status = STATUS_SUCCESS;
LARGE_INTEGER cpuIdleTime = {0}, cpuUserTime = {0}, cpuKernelTime = {0}, cpuBusyTime = {0}, cpuTotalTime = {0}, sumBusyTime = {0}, sumTotalTime = {0};
SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION * psppi = new SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION[dwNumProcs]; if (NULL == psppi) { hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY); goto CLEANUP; } // get the new snapshot
status = NtQuerySystemInformation( SystemProcessorPerformanceInformation, psppi, sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) * dwNumProcs, NULL );
if(status != STATUS_SUCCESS){
hr = HRESULT_FROM_NT(status); goto CLEANUP; }
// calculate
for (DWORD i = 0; i < dwNumProcs; i++) { cpuTotalTime = RtlLargeIntegerAdd(psppi[i].UserTime, psppi[i].KernelTime); cpuBusyTime = RtlLargeIntegerSubtract(cpuTotalTime, psppi[i].IdleTime);
sumBusyTime = RtlLargeIntegerAdd(sumBusyTime, cpuBusyTime); sumTotalTime = RtlLargeIntegerAdd(sumTotalTime, cpuTotalTime); }
*pBusyTime = sumBusyTime; *pTotalTime = sumTotalTime;
CLEANUP:
delete [] psppi; psppi = NULL;
return hr; }
LONG GetPercentage(LARGE_INTEGER part, LARGE_INTEGER total) { if (0 == total.QuadPart) { return 100; }
LARGE_INTEGER li; part.QuadPart *= 100; li.QuadPart = part.QuadPart / total.QuadPart;
return li.LowPart; }
BOOL TooMuchProcessorUsage(LARGE_INTEGER liOriginalBusy, LARGE_INTEGER liOriginalTotal, LONG lPercentageUseMax, DWORD dwNumProcs) { HRESULT hr = S_OK; LARGE_INTEGER liNewBusy = {0}, liNewTotal = {0}, liDiffBusy = {0}, liDiffTotal = {0}; hr = GetCPUData(&liNewBusy, &liNewTotal, dwNumProcs); if (FAILED(hr)) { return TRUE; }
liDiffBusy = RtlLargeIntegerSubtract(liNewBusy, liOriginalBusy); liDiffTotal = RtlLargeIntegerSubtract(liNewTotal, liOriginalTotal);
LONG lPercentageUse = GetPercentage(liDiffBusy, liDiffTotal); if (lPercentageUse >= lPercentageUseMax) { return TRUE; }
return FALSE; }
|