Leaked source code of windows server 2003
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.

482 lines
16 KiB

  1. //-----------------------------------------------------------------------------
  2. //
  3. //
  4. // File: Hndlmgmr.cpp
  5. //
  6. // Description:
  7. // Contains implementation of the CQueueHandleManager class
  8. //
  9. // Author: mikeswa
  10. //
  11. // Copyright (C) 2001 Microsoft Corporation
  12. //
  13. //-----------------------------------------------------------------------------
  14. #include "aqprecmp.h"
  15. #include "hndlmgr.h"
  16. //
  17. // Initialize statics
  18. //
  19. DWORD CQueueHandleManager::s_cNumQueueInstances = 0;
  20. DWORD CQueueHandleManager::s_cNumQueueInstancesWithLowBackLog = 0;
  21. DWORD CQueueHandleManager::s_cReservedHandles = 0;
  22. DWORD CQueueHandleManager::s_cMaxSharedConcurrentItems = 0;
  23. //---[ CQueueHandleManager::CQueueHandleManager ]------------------------------
  24. //
  25. //
  26. // Description:
  27. // Constructor for CQueueHandleManger
  28. // Parameters:
  29. // -
  30. // Returns:
  31. // -
  32. // History:
  33. // 05/12/2001 - MikeSwa Created
  34. //
  35. //-----------------------------------------------------------------------------
  36. CQueueHandleManager::CQueueHandleManager()
  37. {
  38. m_dwSignature = CQueueHandleManager_Sig;
  39. //
  40. // Users must call into ReportMaxConcurrentItems first
  41. //
  42. m_dwCurrentState = QUEUE_STATE_UNITIALIZED;
  43. m_cMaxPrivateConcurrentItems = 0;
  44. m_cMaxSharedConcurrentItems = 0;
  45. m_cDbgStateTransitions = 0;
  46. m_cDbgCallsToUpdateStateIfNecessary = 0;
  47. }
  48. //---[ CQueueHandleManager::~CQueueHandleManager ]-----------------------------
  49. //
  50. //
  51. // Description:
  52. // Destructor for CQueueHandleManger
  53. // Parameters:
  54. // -
  55. // Returns:
  56. // -
  57. // History:
  58. // 05/12/2001 - MikeSwa Created
  59. //
  60. //-----------------------------------------------------------------------------
  61. CQueueHandleManager::~CQueueHandleManager()
  62. {
  63. DeinitializeStaticsAndStateIfNecessary();
  64. m_dwSignature = CQueueHandleManager_Sig;
  65. m_dwCurrentState = QUEUE_STATE_UNITIALIZED;
  66. };
  67. //---[ CQueueHandleManager::DeinitializeStaticsAndStateIfNecessary ]-----------
  68. //
  69. //
  70. // Description:
  71. // Update statics based on this queue instance's state. This is used
  72. // in the desctructor and when the config is updated.
  73. // Parameters:
  74. // -
  75. // Returns:
  76. // -
  77. // History:
  78. // 05/12/2001 - MikeSwa Created
  79. //
  80. //-----------------------------------------------------------------------------
  81. void CQueueHandleManager::DeinitializeStaticsAndStateIfNecessary()
  82. {
  83. DWORD dwOldState = QUEUE_STATE_UNITIALIZED;
  84. if (fIsInitialized())
  85. {
  86. if (m_cMaxPrivateConcurrentItems)
  87. {
  88. dwInterlockedAddSubtractDWORD(&s_cReservedHandles,
  89. m_cMaxPrivateConcurrentItems, FALSE);
  90. }
  91. dwOldState = InterlockedExchange((PLONG) &m_dwCurrentState,
  92. QUEUE_STATE_UNITIALIZED);
  93. //
  94. // Update statics based on previous states
  95. //
  96. if ((QUEUE_STATE_LOW_BACKLOG == dwOldState) ||
  97. (QUEUE_STATE_ASYNC_BACKLOG == dwOldState))
  98. InterlockedDecrement((PLONG) &s_cNumQueueInstancesWithLowBackLog);
  99. //
  100. // The last one here gets to updated the shared count
  101. //
  102. if (0 == InterlockedDecrement((PLONG) &s_cNumQueueInstances) &&
  103. s_cMaxSharedConcurrentItems)
  104. {
  105. dwInterlockedAddSubtractDWORD(&s_cReservedHandles,
  106. s_cMaxSharedConcurrentItems, FALSE);
  107. }
  108. }
  109. }
  110. //---[ CQueueHandleManager::SetMaxConcurrentItems ]-------------------------
  111. //
  112. //
  113. // Description:
  114. // Sets appropriate settings for this queue instance
  115. // Parameters:
  116. // -
  117. // Returns:
  118. // -
  119. // History:
  120. // 05/12/2001 - MikeSwa Created
  121. //
  122. //-----------------------------------------------------------------------------
  123. void CQueueHandleManager::SetMaxConcurrentItems(
  124. DWORD cMaxSharedConcurrentItems,
  125. DWORD cMaxPrivateConcurrentItems)
  126. {
  127. DWORD dwRefCount = 0;
  128. DWORD dwOldState = QUEUE_STATE_UNITIALIZED;
  129. //
  130. // Odd things will happen if this is called multiple times... since
  131. // part of the initialization is undone before it is finalized
  132. //
  133. _ASSERT(!fIsInitialized() && "Already initialized");
  134. //
  135. // I don't think this is possible (hence above assert), but this
  136. // will at least prevent the static data from becoming invald and
  137. // will only lead to transient oddities.
  138. //
  139. DeinitializeStaticsAndStateIfNecessary();
  140. dwRefCount = InterlockedIncrement((PLONG) &s_cNumQueueInstances);
  141. dwOldState = InterlockedExchange((PLONG) &m_dwCurrentState,
  142. QUEUE_STATE_NO_BACKLOG);
  143. _ASSERT(QUEUE_STATE_UNITIALIZED == dwOldState);
  144. //
  145. // Update statics based on previous states - again this
  146. // should not be necessary - firewall anyway
  147. //
  148. if ((QUEUE_STATE_LOW_BACKLOG == dwOldState) ||
  149. (QUEUE_STATE_ASYNC_BACKLOG == dwOldState))
  150. InterlockedDecrement((PLONG) &s_cNumQueueInstancesWithLowBackLog);
  151. //
  152. // Calculate the appropriate reserve handle count. Eaxh queue
  153. // can handle a certain number of items concurrently. Some of these
  154. // are constrained by process-wide resources (such as a thread pool),
  155. // but others are things like async completions and
  156. //
  157. m_cMaxPrivateConcurrentItems = cMaxPrivateConcurrentItems;
  158. m_cMaxSharedConcurrentItems = cMaxSharedConcurrentItems;
  159. if (m_cMaxPrivateConcurrentItems)
  160. {
  161. dwInterlockedAddSubtractDWORD(&s_cReservedHandles,
  162. m_cMaxPrivateConcurrentItems, TRUE);
  163. }
  164. if (m_cMaxSharedConcurrentItems && (1 == dwRefCount))
  165. {
  166. //
  167. // The expectation is that there will not be multiple threads
  168. // bouncing the refcount off zero, since VSI start/stop is
  169. // single threaded, and an instance has at least one static
  170. // instance.
  171. //
  172. _ASSERT(s_cNumQueueInstances && "threading violation");
  173. s_cMaxSharedConcurrentItems = m_cMaxSharedConcurrentItems;
  174. dwInterlockedAddSubtractDWORD(&s_cReservedHandles,
  175. s_cMaxSharedConcurrentItems, TRUE);
  176. }
  177. }
  178. //---[ CQueueHandleManager::fShouldCloseHandle ]-------------------------------
  179. //
  180. //
  181. // Description:
  182. // Called by queue instances to determine if they should close handles.
  183. // Must be preceeded by a call to SetMaxConcurrentItems to initialize
  184. // configuration.
  185. // Parameters:
  186. // IN cItemsPending Number of items currently waiting to be
  187. // processed on this queue.
  188. // IN cItemsPendingAsyncCompletions - Items that are actually opened
  189. // and currently being processed
  190. // IN cCurrentMsgsOpenInSystem - The current number of messages
  191. // open in the process (ie - how much resources
  192. // are being consumed).
  193. // Returns:
  194. // TRUE - Caller should close messages now
  195. // FALSE - Caller should *not* close messages now
  196. // History:
  197. // 05/12/2001 - MikeSwa Created
  198. //
  199. //-----------------------------------------------------------------------------
  200. BOOL CQueueHandleManager::fShouldCloseHandle(
  201. DWORD cItemsPending,
  202. DWORD cItemsPendingAsyncCompletions,
  203. DWORD cCurrentMsgsOpenInSystem)
  204. {
  205. TraceFunctEnterEx((LPARAM) this, "CQueueHandleManager::fShouldCloseHandle");
  206. _ASSERT(fIsInitialized());
  207. DWORD dwState = m_dwCurrentState;
  208. DWORD cHandleLimit = 0;
  209. BOOL fShouldClose = TRUE;
  210. if (QUEUE_STATE_UNITIALIZED != dwState)
  211. {
  212. //
  213. // See if state needs to be updated
  214. //
  215. dwState = dwUpdateCurrentStateIfNeccessary(cItemsPending,
  216. cItemsPendingAsyncCompletions);
  217. _ASSERT(QUEUE_STATE_UNITIALIZED != dwState);
  218. }
  219. //
  220. // Code defensively - Assume worst case
  221. //
  222. if (QUEUE_STATE_UNITIALIZED == dwState)
  223. {
  224. ErrorTrace((LPARAM) this, "Queue state is unitialized");
  225. dwState = QUEUE_STATE_BACKLOG; //defensive code
  226. }
  227. cHandleLimit = cGetHandleLimitForState(dwState);
  228. //
  229. // Now that we have the limit on the number of handles, the math is easy
  230. //
  231. if (cHandleLimit > cCurrentMsgsOpenInSystem)
  232. fShouldClose = FALSE;
  233. DebugTrace((LPARAM) this,
  234. "%s Handle - %d pending, %d pending async, 0x%X state, %d open msgs, %d handle limit",
  235. (fShouldClose ? "Closing" : "Not closing"),
  236. cItemsPending, cItemsPendingAsyncCompletions, dwState,
  237. cCurrentMsgsOpenInSystem, cHandleLimit);
  238. TraceFunctLeave();
  239. return fShouldClose;
  240. }
  241. //---[ CQueueHandleManager::cGetHandleLimitForState ]--------------------------
  242. //
  243. //
  244. // Description:
  245. // Called by queue instances to determine if they should close handles.
  246. // Must be preceeded by a call to SetMaxConcurrentItems to initialize
  247. // configuration. - Static method
  248. // Parameters:
  249. // IN dwState The state to calculate the limit for
  250. // Returns:
  251. // The handle limti for the given state
  252. // History:
  253. // 05/17/2001 - MikeSwa Created
  254. //
  255. //-----------------------------------------------------------------------------
  256. DWORD CQueueHandleManager::cGetHandleLimitForState(DWORD dwState)
  257. {
  258. DWORD cHandleLimit = g_cMaxIMsgHandlesThreshold;
  259. DWORD cReserve = s_cReservedHandles;
  260. //
  261. // Allow registry configurable limit
  262. //
  263. if (s_cReservedHandles > g_cMaxHandleReserve)
  264. cReserve = g_cMaxHandleReserve;
  265. //
  266. // Our logic only makes sense if this is true
  267. //
  268. _ASSERT(g_cMaxIMsgHandlesThreshold >= g_cMaxIMsgHandlesLowThreshold);
  269. //
  270. // If the handle limit is actually... zero, then close without
  271. // regard for the queue state.
  272. //
  273. if (!cHandleLimit)
  274. goto Exit;
  275. switch(dwState)
  276. {
  277. //
  278. // The number of messages pending is equal to the number
  279. // of messages that can be concurrently processed. In this case,
  280. // we should try hard not to close handles. To accomplish this,
  281. // we will dip into our reserve.
  282. //
  283. // Async backlog is a similar case...we have no backlog of items
  284. // pending, but we have a large number of pending completions
  285. //
  286. case QUEUE_STATE_NO_BACKLOG:
  287. case QUEUE_STATE_ASYNC_BACKLOG:
  288. cHandleLimit += cReserve;
  289. break;
  290. //
  291. // In the case where there are some of messages queued up (to
  292. // a configured percentage of the max handle limit), we will
  293. // continue closing handles normally
  294. //
  295. case QUEUE_STATE_LOW_BACKLOG:
  296. break; //use handle limit as-is
  297. //
  298. // In the case where there is a significant backlog, we
  299. // would like to use handles if available... but not to the
  300. // determent of shorter queues. If there are other queues
  301. // have a low backlog... we will defer to them. Otherwise,
  302. // we will use as many handles as we can.
  303. //
  304. case QUEUE_STATE_BACKLOG:
  305. if (s_cNumQueueInstancesWithLowBackLog)
  306. cHandleLimit = g_cMaxIMsgHandlesLowThreshold;
  307. break;
  308. //
  309. // Queue is either non initialized or in an invalid state.
  310. // We will err on the side of caution and treat this as
  311. //
  312. default:
  313. _ASSERT(0 && "Invalid Queue State");
  314. cHandleLimit = 0;
  315. }
  316. Exit:
  317. return cHandleLimit;
  318. }
  319. //---[ CQueueHandleManager::dwUpdateCurrentStateIfNeccessary ]----------------
  320. //
  321. //
  322. // Description:
  323. // Will update this queues state if necessary and return the resulting
  324. // state.
  325. // Parameters:
  326. // IN cItemsPending Number of items currently waiting to be
  327. // processed on this queue.
  328. // IN cItemsPendingAsyncCompletions - Items that are actually opened
  329. // and currently being processed
  330. // Returns:
  331. // The state for given lengths
  332. // History:
  333. // 05/17/2001 - MikeSwa Created
  334. //
  335. //-----------------------------------------------------------------------------
  336. DWORD CQueueHandleManager::dwUpdateCurrentStateIfNeccessary(
  337. DWORD cItemsPending,
  338. DWORD cItemsPendingAsyncCompletions)
  339. {
  340. TraceFunctEnterEx((LPARAM) this,
  341. "CQueueHandleManager::dwUpdateCurrentStateIfNeccessary");
  342. DWORD dwOldState = m_dwCurrentState;
  343. DWORD dwNewState = dwOldState;
  344. DWORD dwCompare = dwOldState;
  345. _ASSERT(fIsInitialized());
  346. if (!fIsInitialized())
  347. {
  348. ErrorTrace((LPARAM) this, "Queue is not initialized");
  349. goto Exit;
  350. }
  351. m_cDbgCallsToUpdateStateIfNecessary++;
  352. dwNewState = dwGetStateForLengths(cItemsPending, cItemsPendingAsyncCompletions);
  353. //
  354. // We need to update the state... do this in a thread-safe manner
  355. //
  356. do
  357. {
  358. dwOldState = m_dwCurrentState;
  359. if (dwNewState == dwOldState)
  360. goto Exit;
  361. dwCompare = InterlockedCompareExchange((PLONG) &m_dwCurrentState,
  362. dwNewState, dwOldState);
  363. } while (dwCompare != dwOldState);
  364. //
  365. // Now that we have changed state, we are responsible for updating
  366. // the static counters for the old and new states
  367. //
  368. if ((QUEUE_STATE_LOW_BACKLOG == dwNewState) ||
  369. (QUEUE_STATE_ASYNC_BACKLOG == dwNewState))
  370. InterlockedIncrement((PLONG) &s_cNumQueueInstancesWithLowBackLog);
  371. if ((QUEUE_STATE_LOW_BACKLOG == dwOldState) ||
  372. (QUEUE_STATE_ASYNC_BACKLOG == dwOldState))
  373. InterlockedDecrement((PLONG) &s_cNumQueueInstancesWithLowBackLog);
  374. m_cDbgStateTransitions++;
  375. Exit:
  376. TraceFunctLeave();
  377. return dwNewState;
  378. }
  379. //---[ CQueueHandleManager::dwGetStateForLengths ]-----------------------------
  380. //
  381. //
  382. // Description:
  383. // Static method to determine the appropriate state for a given
  384. // set of lenghts
  385. // Parameters:
  386. // IN cItemsPending Number of items currently waiting to be
  387. // processed on this queue.
  388. // IN cItemsPendingAsyncCompletions - Items that are actually opened
  389. // and currently being processed
  390. // Returns:
  391. // The state for given lengths
  392. // History:
  393. // 05/17/2001 - MikeSwa Created
  394. //
  395. //-----------------------------------------------------------------------------
  396. DWORD CQueueHandleManager::dwGetStateForLengths(
  397. DWORD cItemsPending,
  398. DWORD cItemsPendingAsyncCompletions)
  399. {
  400. DWORD dwState = QUEUE_STATE_BACKLOG;
  401. //
  402. // If we are at or less than our max number of concurrent items
  403. //
  404. if (cItemsPending <= s_cReservedHandles)
  405. dwState = QUEUE_STATE_NO_BACKLOG;
  406. else if (cItemsPending <= g_cMaxIMsgHandlesLowThreshold)
  407. dwState = QUEUE_STATE_LOW_BACKLOG;
  408. //
  409. // Async completions are slightly tricky. We don't want to bounce
  410. // a handle simply because we have a large number of async completions.
  411. // We also want to identify ourselves as a potential user of handles.
  412. //
  413. // We have a state (QUEUE_STATE_ASYNC_BACKLOG) that is used to
  414. // indicate that while there is no backlog of items pending, there
  415. // may be a large number of items owned by this queue (with open
  416. // handles). This state has the effect of:
  417. // - Flagging this queue has one with a low backlog
  418. // - Managing handles for *this* queue as if there was no backlog
  419. //
  420. // If there is any backlog of any kind for cItemsPending, we will
  421. // treat the queue based on those results
  422. //
  423. if ((QUEUE_STATE_NO_BACKLOG == dwState) &&
  424. (cItemsPendingAsyncCompletions >= s_cReservedHandles))
  425. dwState = QUEUE_STATE_ASYNC_BACKLOG;
  426. return dwState;
  427. }