Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

430 lines
11 KiB

  1. /*===================================================================
  2. Microsoft IIS 5.0 (ASP)
  3. Microsoft Confidential.
  4. Copyright 1998 Microsoft Corporation. All Rights Reserved.
  5. Component: Thread Gate
  6. The thread gate limits number of threads executing at the
  7. moment by sleeping some of them.
  8. File: thrdgate.cpp
  9. Owner: DmitryR
  10. This file contains the code for the Thread Gate
  11. ===================================================================*/
  12. #include "denpre.h"
  13. #pragma hdrstop
  14. #include "thrdgate.h"
  15. #include "memchk.h"
  16. /*===================================================================
  17. Constants for tuning
  18. ===================================================================*/
  19. /*===================================================================
  20. Class to track the processor load
  21. ===================================================================*/
  22. inline DWORD GetNumberOfProcessors() {
  23. SYSTEM_INFO si;
  24. GetSystemInfo(&si);
  25. return si.dwNumberOfProcessors;
  26. }
  27. inline LONG GetPercentage(LARGE_INTEGER part, LARGE_INTEGER total) {
  28. if (total.HighPart == 0 && total.LowPart == 0) {
  29. return 100;
  30. }
  31. ULONG ul;
  32. LARGE_INTEGER t1, t2, t3;
  33. if (total.HighPart == 0) {
  34. t1 = RtlEnlargedIntegerMultiply(part.LowPart, 100);
  35. t2 = RtlExtendedLargeIntegerDivide(t1, total.LowPart, &ul);
  36. } else {
  37. t1 = RtlExtendedLargeIntegerDivide(total, 100, &ul);
  38. t2 = RtlLargeIntegerDivide(part, t1, &t3);
  39. }
  40. return t2.LowPart;
  41. }
  42. class CCPULoad {
  43. private:
  44. DWORD m_cCPU;
  45. DWORD m_cbData; // data struct length
  46. SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *m_psppiOld;
  47. SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *m_psppiNew;
  48. public:
  49. /*===================================================================
  50. Constructor
  51. ===================================================================*/
  52. CCPULoad() {
  53. // get the CPU count
  54. m_cCPU = GetNumberOfProcessors();
  55. m_cbData = m_cCPU * sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION);
  56. m_psppiOld = new SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION[m_cCPU];
  57. m_psppiNew = new SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION[m_cCPU];
  58. if (m_psppiOld == NULL || m_psppiNew == NULL) {
  59. return;
  60. }
  61. // get the original snapshot
  62. NtQuerySystemInformation(
  63. SystemProcessorPerformanceInformation,
  64. m_psppiOld,
  65. m_cbData,
  66. NULL
  67. );
  68. }
  69. /*===================================================================
  70. Destructor
  71. ===================================================================*/
  72. ~CCPULoad() {
  73. if (m_psppiOld != NULL) {
  74. delete m_psppiOld;
  75. }
  76. if (m_psppiNew != NULL) {
  77. delete m_psppiNew;
  78. }
  79. }
  80. /*===================================================================
  81. GetReading
  82. get the current reading as a percentage of CPU load
  83. averaged across processors
  84. ===================================================================*/
  85. DWORD GetReading() {
  86. if (m_psppiOld == NULL || m_psppiNew == NULL) {
  87. return 0;
  88. }
  89. // get the new snapshot
  90. NtQuerySystemInformation(
  91. SystemProcessorPerformanceInformation,
  92. m_psppiNew,
  93. m_cbData,
  94. NULL
  95. );
  96. // calculate
  97. LARGE_INTEGER cpuIdleTime, cpuUserTime, cpuKernelTime,
  98. cpuBusyTime, cpuTotalTime,
  99. sumBusyTime = RtlConvertLongToLargeInteger(0),
  100. sumTotalTime = RtlConvertLongToLargeInteger(0);
  101. for (DWORD i = 0; i < m_cCPU; i++) {
  102. cpuIdleTime = RtlLargeIntegerSubtract(m_psppiNew[i].IdleTime, m_psppiOld[i].IdleTime);
  103. cpuUserTime = RtlLargeIntegerSubtract(m_psppiNew[i].UserTime, m_psppiOld[i].UserTime);
  104. cpuKernelTime = RtlLargeIntegerSubtract(m_psppiNew[i].KernelTime, m_psppiOld[i].KernelTime);
  105. cpuTotalTime = RtlLargeIntegerAdd(cpuUserTime, cpuKernelTime);
  106. cpuBusyTime = RtlLargeIntegerSubtract(cpuTotalTime, cpuIdleTime);
  107. IF_DEBUG(THREADGATE)
  108. {
  109. LONG p = GetPercentage(cpuBusyTime, cpuTotalTime);
  110. DBGPRINTF((DBG_CONTEXT, "ThreadGate: load(%d)=%d", i+1, p));
  111. }
  112. sumBusyTime = RtlLargeIntegerAdd(sumBusyTime, cpuBusyTime);
  113. sumTotalTime = RtlLargeIntegerAdd(sumTotalTime, cpuTotalTime);
  114. }
  115. LONG nPercentage = GetPercentage(sumBusyTime, sumTotalTime);
  116. IF_DEBUG(THREADGATE)
  117. {
  118. DBGPRINTF((DBG_CONTEXT, "ThreadGate: **** load = %d\r\n", nPercentage));
  119. }
  120. // move new to old
  121. memcpy(m_psppiOld, m_psppiNew, m_cbData);
  122. return nPercentage;
  123. }
  124. /*=================================================================*/
  125. }; // class CCPULoad
  126. /*===================================================================
  127. The thread gate class
  128. ===================================================================*/
  129. class CThreadGate {
  130. private:
  131. DWORD m_msSlice; // granularity
  132. DWORD m_msSleep; // sleep length
  133. DWORD m_cSleepsMax; // max wait 50 sleeps
  134. LONG m_nLowLoad; // low CPU load is < 75%
  135. LONG m_nHighLoad; // hight CPU load is > 90%
  136. LONG m_cThreadLimitMin; // hunting range low
  137. LONG m_cThreadLimitMax; // hunting range high
  138. LONG m_cThreadLimit; // current limit
  139. LONG m_nTrend; // last change
  140. DWORD m_msT0; // starting time
  141. LONG m_iCurrentSlice; // current time slice index
  142. LONG m_nRequests; // number of active requests
  143. CCPULoad m_CPULoad; // track the CPU load
  144. public:
  145. /*===================================================================
  146. Constructor
  147. ===================================================================*/
  148. CThreadGate(
  149. DWORD msSlice,
  150. DWORD msSleep,
  151. DWORD cSleepsMax,
  152. DWORD nLowLoad,
  153. DWORD nHighLoad,
  154. DWORD cLimitMin,
  155. DWORD cLimitMax
  156. ) {
  157. m_msSlice = msSlice;
  158. m_msSleep = msSleep;
  159. m_cSleepsMax = cSleepsMax;
  160. m_nLowLoad = nLowLoad,
  161. m_nHighLoad = nHighLoad;
  162. m_cThreadLimitMin = cLimitMin;
  163. m_cThreadLimitMax = cLimitMax;
  164. m_cThreadLimit = m_cThreadLimitMin;
  165. m_nTrend = 0;
  166. m_msT0 = GetTickCount();
  167. m_iCurrentSlice = 0;
  168. m_nRequests = 0;
  169. }
  170. /*===================================================================
  171. Destructor
  172. ===================================================================*/
  173. ~CThreadGate() {
  174. }
  175. /*===================================================================
  176. HuntLoad
  177. Do the load hunting
  178. ===================================================================*/
  179. void HuntLoad() {
  180. LONG nLoad = m_CPULoad.GetReading();
  181. if (m_nRequests == 0) {
  182. // no requests - don't change
  183. m_nTrend = 0;
  184. return;
  185. }
  186. LONG cThreadLimit = m_cThreadLimit;
  187. LONG nTrend = m_nTrend;
  188. if (nLoad < m_nLowLoad) {
  189. nTrend = nTrend <= 0 ? 1 : nTrend+3; // grow faster
  190. cThreadLimit += nTrend;
  191. if (cThreadLimit >= m_cThreadLimitMax) {
  192. cThreadLimit = m_cThreadLimitMax;
  193. nTrend = 0;
  194. }
  195. }
  196. else if (nLoad > m_nHighLoad) {
  197. nTrend = nTrend > 0 ? -1 : nTrend-1;
  198. cThreadLimit += nTrend;
  199. if (cThreadLimit <= m_cThreadLimitMin) {
  200. cThreadLimit = m_cThreadLimitMin;
  201. nTrend = 0;
  202. }
  203. }
  204. // set the new limit and trend
  205. m_cThreadLimit = cThreadLimit;
  206. m_nTrend = nTrend;
  207. }
  208. /*===================================================================
  209. Enter
  210. Pass through the gate. Can make the thread sleep
  211. Returns
  212. Thread Gate Pass
  213. ===================================================================*/
  214. void Enter(DWORD msCurrentTickCount) {
  215. DWORD cSleeps = 0;
  216. while (cSleeps++ < m_cSleepsMax) {
  217. // if shutting down, let the request go. Later it will find
  218. // out again that the server is shutting down and not actually
  219. // fire the request.
  220. if (IsShutDownInProgress()) {
  221. break;
  222. }
  223. // calculate the current time slice
  224. DWORD msElapsedSinceT0 = (msCurrentTickCount >= m_msT0) ?
  225. (msCurrentTickCount - m_msT0) :
  226. ((0xffffffff - m_msT0) + msCurrentTickCount);
  227. LONG iSlice = msElapsedSinceT0 / m_msSlice;
  228. if (iSlice > m_iCurrentSlice) {
  229. // set it as the new one
  230. if (InterlockedExchange(&m_iCurrentSlice, iSlice) != iSlice) {
  231. // this is the first thread to jump the time slice - go hunting
  232. HuntLoad();
  233. }
  234. }
  235. // enforce the gate limit
  236. if (m_nRequests < m_cThreadLimit) {
  237. break;
  238. }
  239. // Too many active threads -- sleep
  240. Sleep(m_msSleep);
  241. }
  242. // let it through
  243. InterlockedIncrement(&m_nRequests);
  244. }
  245. /*===================================================================
  246. Leave
  247. Return. The user lets us know that the request finished.
  248. ===================================================================*/
  249. void Leave() {
  250. InterlockedDecrement(&m_nRequests);
  251. }
  252. /*=================================================================*/
  253. }; // class CThreadGate
  254. // Pointer to the sole instance of the above
  255. static CThreadGate *gs_pThreadGate = NULL;
  256. /*===================================================================
  257. E x t e r n a l A P I
  258. ===================================================================*/
  259. /*===================================================================
  260. InitThreadGate
  261. Initialization
  262. Parameters
  263. ptgc configuration
  264. Returns:
  265. HRESULT
  266. ===================================================================*/
  267. HRESULT InitThreadGate(THREADGATE_CONFIG *ptgc) {
  268. DWORD cCPU = GetNumberOfProcessors();
  269. if (ptgc->fEnabled) {
  270. gs_pThreadGate = new CThreadGate(
  271. ptgc->msTimeSlice,
  272. ptgc->msSleepDelay,
  273. ptgc->nSleepMax,
  274. ptgc->nLoadLow,
  275. ptgc->nLoadHigh,
  276. ptgc->nMinProcessorThreads * cCPU,
  277. ptgc->nMaxProcessorThreads * cCPU
  278. );
  279. return (gs_pThreadGate != NULL) ? S_OK : E_OUTOFMEMORY;
  280. }
  281. gs_pThreadGate = NULL;
  282. return S_OK;
  283. }
  284. /*===================================================================
  285. UnInitThreadGate
  286. To be called from DllUnInit()
  287. Parameters
  288. Returns:
  289. n/a
  290. ===================================================================*/
  291. void UnInitThreadGate() {
  292. if (gs_pThreadGate) {
  293. delete gs_pThreadGate;
  294. gs_pThreadGate = NULL;
  295. }
  296. }
  297. /*===================================================================
  298. PassThroughThreadGate
  299. Pass through the gate. The current thread could be delayed in
  300. case there are too many running threads at this moment
  301. Parameters
  302. msCurrentTickCount current tick count
  303. ===================================================================*/
  304. void EnterThreadGate(DWORD msCurrentTickCount) {
  305. if (gs_pThreadGate) {
  306. gs_pThreadGate->Enter(msCurrentTickCount);
  307. }
  308. }
  309. /*===================================================================
  310. LeaveThreadGate
  311. Request done executing
  312. ===================================================================*/
  313. void LeaveThreadGate() {
  314. if (gs_pThreadGate) {
  315. gs_pThreadGate->Leave();
  316. }
  317. }