431 lines
11 KiB
C++
431 lines
11 KiB
C++
/*===================================================================
|
|
Microsoft IIS 5.0 (ASP)
|
|
|
|
Microsoft Confidential.
|
|
Copyright 1998 Microsoft Corporation. All Rights Reserved.
|
|
|
|
Component: Thread Gate
|
|
|
|
The thread gate limits number of threads executing at the
|
|
moment by sleeping some of them.
|
|
|
|
File: thrdgate.cpp
|
|
|
|
Owner: DmitryR
|
|
|
|
This file contains the code for the Thread Gate
|
|
===================================================================*/
|
|
|
|
#include "denpre.h"
|
|
#pragma hdrstop
|
|
|
|
#include "thrdgate.h"
|
|
#include "memchk.h"
|
|
|
|
/*===================================================================
|
|
Constants for tuning
|
|
===================================================================*/
|
|
|
|
/*===================================================================
|
|
Class to track the processor load
|
|
===================================================================*/
|
|
|
|
inline DWORD GetNumberOfProcessors() {
|
|
SYSTEM_INFO si;
|
|
GetSystemInfo(&si);
|
|
return si.dwNumberOfProcessors;
|
|
}
|
|
|
|
inline LONG GetPercentage(LARGE_INTEGER part, LARGE_INTEGER total) {
|
|
|
|
if (total.HighPart == 0 && total.LowPart == 0) {
|
|
return 100;
|
|
}
|
|
|
|
ULONG ul;
|
|
LARGE_INTEGER t1, t2, t3;
|
|
if (total.HighPart == 0) {
|
|
t1 = RtlEnlargedIntegerMultiply(part.LowPart, 100);
|
|
t2 = RtlExtendedLargeIntegerDivide(t1, total.LowPart, &ul);
|
|
} else {
|
|
t1 = RtlExtendedLargeIntegerDivide(total, 100, &ul);
|
|
t2 = RtlLargeIntegerDivide(part, t1, &t3);
|
|
}
|
|
return t2.LowPart;
|
|
}
|
|
|
|
class CCPULoad {
|
|
|
|
private:
|
|
DWORD m_cCPU;
|
|
DWORD m_cbData; // data struct length
|
|
SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *m_psppiOld;
|
|
SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *m_psppiNew;
|
|
|
|
public:
|
|
|
|
/*===================================================================
|
|
Constructor
|
|
===================================================================*/
|
|
CCPULoad() {
|
|
|
|
// get the CPU count
|
|
m_cCPU = GetNumberOfProcessors();
|
|
|
|
m_cbData = m_cCPU * sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION);
|
|
|
|
m_psppiOld = new SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION[m_cCPU];
|
|
m_psppiNew = new SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION[m_cCPU];
|
|
if (m_psppiOld == NULL || m_psppiNew == NULL) {
|
|
return;
|
|
}
|
|
|
|
// get the original snapshot
|
|
NtQuerySystemInformation(
|
|
SystemProcessorPerformanceInformation,
|
|
m_psppiOld,
|
|
m_cbData,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
/*===================================================================
|
|
Destructor
|
|
===================================================================*/
|
|
~CCPULoad() {
|
|
|
|
if (m_psppiOld != NULL) {
|
|
delete m_psppiOld;
|
|
}
|
|
if (m_psppiNew != NULL) {
|
|
delete m_psppiNew;
|
|
}
|
|
}
|
|
|
|
/*===================================================================
|
|
GetReading
|
|
get the current reading as a percentage of CPU load
|
|
averaged across processors
|
|
===================================================================*/
|
|
DWORD GetReading() {
|
|
|
|
if (m_psppiOld == NULL || m_psppiNew == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
// get the new snapshot
|
|
NtQuerySystemInformation(
|
|
SystemProcessorPerformanceInformation,
|
|
m_psppiNew,
|
|
m_cbData,
|
|
NULL
|
|
);
|
|
|
|
// calculate
|
|
LARGE_INTEGER cpuIdleTime, cpuUserTime, cpuKernelTime,
|
|
cpuBusyTime, cpuTotalTime,
|
|
sumBusyTime = RtlConvertLongToLargeInteger(0),
|
|
sumTotalTime = RtlConvertLongToLargeInteger(0);
|
|
|
|
for (DWORD i = 0; i < m_cCPU; i++) {
|
|
|
|
cpuIdleTime = RtlLargeIntegerSubtract(m_psppiNew[i].IdleTime, m_psppiOld[i].IdleTime);
|
|
cpuUserTime = RtlLargeIntegerSubtract(m_psppiNew[i].UserTime, m_psppiOld[i].UserTime);
|
|
cpuKernelTime = RtlLargeIntegerSubtract(m_psppiNew[i].KernelTime, m_psppiOld[i].KernelTime);
|
|
|
|
cpuTotalTime = RtlLargeIntegerAdd(cpuUserTime, cpuKernelTime);
|
|
cpuBusyTime = RtlLargeIntegerSubtract(cpuTotalTime, cpuIdleTime);
|
|
|
|
IF_DEBUG(THREADGATE)
|
|
{
|
|
LONG p = GetPercentage(cpuBusyTime, cpuTotalTime);
|
|
DBGPRINTF((DBG_CONTEXT, "ThreadGate: load(%d)=%d", i+1, p));
|
|
}
|
|
|
|
sumBusyTime = RtlLargeIntegerAdd(sumBusyTime, cpuBusyTime);
|
|
sumTotalTime = RtlLargeIntegerAdd(sumTotalTime, cpuTotalTime);
|
|
}
|
|
|
|
LONG nPercentage = GetPercentage(sumBusyTime, sumTotalTime);
|
|
|
|
IF_DEBUG(THREADGATE)
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT, "ThreadGate: **** load = %d\r\n", nPercentage));
|
|
}
|
|
|
|
// move new to old
|
|
memcpy(m_psppiOld, m_psppiNew, m_cbData);
|
|
|
|
return nPercentage;
|
|
}
|
|
|
|
/*=================================================================*/
|
|
|
|
}; // class CCPULoad
|
|
|
|
|
|
/*===================================================================
|
|
The thread gate class
|
|
===================================================================*/
|
|
|
|
class CThreadGate {
|
|
|
|
private:
|
|
|
|
DWORD m_msSlice; // granularity
|
|
DWORD m_msSleep; // sleep length
|
|
DWORD m_cSleepsMax; // max wait 50 sleeps
|
|
LONG m_nLowLoad; // low CPU load is < 75%
|
|
LONG m_nHighLoad; // hight CPU load is > 90%
|
|
|
|
LONG m_cThreadLimitMin; // hunting range low
|
|
LONG m_cThreadLimitMax; // hunting range high
|
|
|
|
LONG m_cThreadLimit; // current limit
|
|
LONG m_nTrend; // last change
|
|
|
|
DWORD m_msT0; // starting time
|
|
LONG m_iCurrentSlice; // current time slice index
|
|
LONG m_nRequests; // number of active requests
|
|
|
|
CCPULoad m_CPULoad; // track the CPU load
|
|
|
|
public:
|
|
|
|
/*===================================================================
|
|
Constructor
|
|
===================================================================*/
|
|
CThreadGate(
|
|
DWORD msSlice,
|
|
DWORD msSleep,
|
|
DWORD cSleepsMax,
|
|
DWORD nLowLoad,
|
|
DWORD nHighLoad,
|
|
DWORD cLimitMin,
|
|
DWORD cLimitMax
|
|
) {
|
|
|
|
m_msSlice = msSlice;
|
|
m_msSleep = msSleep;
|
|
m_cSleepsMax = cSleepsMax;
|
|
m_nLowLoad = nLowLoad,
|
|
m_nHighLoad = nHighLoad;
|
|
|
|
m_cThreadLimitMin = cLimitMin;
|
|
m_cThreadLimitMax = cLimitMax;
|
|
|
|
m_cThreadLimit = m_cThreadLimitMin;
|
|
m_nTrend = 0;
|
|
|
|
m_msT0 = GetTickCount();
|
|
m_iCurrentSlice = 0;
|
|
m_nRequests = 0;
|
|
}
|
|
|
|
/*===================================================================
|
|
Destructor
|
|
===================================================================*/
|
|
~CThreadGate() {
|
|
|
|
}
|
|
|
|
/*===================================================================
|
|
HuntLoad
|
|
|
|
Do the load hunting
|
|
===================================================================*/
|
|
void HuntLoad() {
|
|
|
|
LONG nLoad = m_CPULoad.GetReading();
|
|
|
|
if (m_nRequests == 0) {
|
|
// no requests - don't change
|
|
m_nTrend = 0;
|
|
return;
|
|
}
|
|
|
|
LONG cThreadLimit = m_cThreadLimit;
|
|
LONG nTrend = m_nTrend;
|
|
|
|
if (nLoad < m_nLowLoad) {
|
|
nTrend = nTrend <= 0 ? 1 : nTrend+3; // grow faster
|
|
cThreadLimit += nTrend;
|
|
|
|
if (cThreadLimit >= m_cThreadLimitMax) {
|
|
cThreadLimit = m_cThreadLimitMax;
|
|
nTrend = 0;
|
|
}
|
|
}
|
|
else if (nLoad > m_nHighLoad) {
|
|
nTrend = nTrend > 0 ? -1 : nTrend-1;
|
|
cThreadLimit += nTrend;
|
|
|
|
if (cThreadLimit <= m_cThreadLimitMin) {
|
|
cThreadLimit = m_cThreadLimitMin;
|
|
nTrend = 0;
|
|
}
|
|
}
|
|
|
|
// set the new limit and trend
|
|
m_cThreadLimit = cThreadLimit;
|
|
m_nTrend = nTrend;
|
|
}
|
|
|
|
/*===================================================================
|
|
Enter
|
|
|
|
Pass through the gate. Can make the thread sleep
|
|
|
|
Returns
|
|
Thread Gate Pass
|
|
===================================================================*/
|
|
void Enter(DWORD msCurrentTickCount) {
|
|
|
|
DWORD cSleeps = 0;
|
|
|
|
while (cSleeps++ < m_cSleepsMax) {
|
|
|
|
// if shutting down, let the request go. Later it will find
|
|
// out again that the server is shutting down and not actually
|
|
// fire the request.
|
|
|
|
if (IsShutDownInProgress()) {
|
|
break;
|
|
}
|
|
|
|
|
|
// calculate the current time slice
|
|
DWORD msElapsedSinceT0 = (msCurrentTickCount >= m_msT0) ?
|
|
(msCurrentTickCount - m_msT0) :
|
|
((0xffffffff - m_msT0) + msCurrentTickCount);
|
|
LONG iSlice = msElapsedSinceT0 / m_msSlice;
|
|
|
|
if (iSlice > m_iCurrentSlice) {
|
|
// set it as the new one
|
|
if (InterlockedExchange(&m_iCurrentSlice, iSlice) != iSlice) {
|
|
|
|
// this is the first thread to jump the time slice - go hunting
|
|
HuntLoad();
|
|
}
|
|
}
|
|
|
|
// enforce the gate limit
|
|
if (m_nRequests < m_cThreadLimit) {
|
|
break;
|
|
}
|
|
|
|
// Too many active threads -- sleep
|
|
Sleep(m_msSleep);
|
|
}
|
|
|
|
// let it through
|
|
InterlockedIncrement(&m_nRequests);
|
|
}
|
|
|
|
/*===================================================================
|
|
Leave
|
|
|
|
Return. The user lets us know that the request finished.
|
|
===================================================================*/
|
|
void Leave() {
|
|
|
|
InterlockedDecrement(&m_nRequests);
|
|
}
|
|
|
|
/*=================================================================*/
|
|
|
|
}; // class CThreadGate
|
|
|
|
|
|
// Pointer to the sole instance of the above
|
|
static CThreadGate *gs_pThreadGate = NULL;
|
|
|
|
|
|
/*===================================================================
|
|
E x t e r n a l A P I
|
|
===================================================================*/
|
|
|
|
/*===================================================================
|
|
InitThreadGate
|
|
|
|
Initialization
|
|
|
|
Parameters
|
|
ptgc configuration
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT InitThreadGate(THREADGATE_CONFIG *ptgc) {
|
|
|
|
DWORD cCPU = GetNumberOfProcessors();
|
|
|
|
if (ptgc->fEnabled) {
|
|
gs_pThreadGate = new CThreadGate(
|
|
ptgc->msTimeSlice,
|
|
ptgc->msSleepDelay,
|
|
ptgc->nSleepMax,
|
|
ptgc->nLoadLow,
|
|
ptgc->nLoadHigh,
|
|
ptgc->nMinProcessorThreads * cCPU,
|
|
ptgc->nMaxProcessorThreads * cCPU
|
|
);
|
|
|
|
return (gs_pThreadGate != NULL) ? S_OK : E_OUTOFMEMORY;
|
|
|
|
}
|
|
|
|
gs_pThreadGate = NULL;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/*===================================================================
|
|
UnInitThreadGate
|
|
|
|
To be called from DllUnInit()
|
|
|
|
Parameters
|
|
|
|
Returns:
|
|
n/a
|
|
===================================================================*/
|
|
void UnInitThreadGate() {
|
|
|
|
if (gs_pThreadGate) {
|
|
delete gs_pThreadGate;
|
|
gs_pThreadGate = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/*===================================================================
|
|
PassThroughThreadGate
|
|
|
|
Pass through the gate. The current thread could be delayed in
|
|
case there are too many running threads at this moment
|
|
|
|
Parameters
|
|
msCurrentTickCount current tick count
|
|
===================================================================*/
|
|
void EnterThreadGate(DWORD msCurrentTickCount) {
|
|
|
|
if (gs_pThreadGate) {
|
|
gs_pThreadGate->Enter(msCurrentTickCount);
|
|
}
|
|
}
|
|
|
|
|
|
/*===================================================================
|
|
LeaveThreadGate
|
|
|
|
Request done executing
|
|
===================================================================*/
|
|
void LeaveThreadGate() {
|
|
|
|
if (gs_pThreadGate) {
|
|
gs_pThreadGate->Leave();
|
|
}
|
|
}
|
|
|