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
12 KiB

  1. /*++
  2. Copyright (C) 1996-2001 Microsoft Corporation
  3. Module Name:
  4. MERGERTHROTTLING.CPP
  5. Abstract:
  6. CMergerThrottling clas
  7. History:
  8. 30-Nov-00 sanjes Created.
  9. --*/
  10. #include "precomp.h"
  11. #pragma warning (disable : 4786)
  12. #include <wbemcore.h>
  13. #include <map>
  14. #include <vector>
  15. #include <genutils.h>
  16. #include <oahelp.inl>
  17. #include <wqllex.h>
  18. #include "wmimerger.h"
  19. #include "mergerthrottling.h"
  20. static long g_lNumMergers = 0L;
  21. //***************************************************************************
  22. //
  23. //***************************************************************************
  24. //
  25. CMergerThrottling::CMergerThrottling( void )
  26. : m_hParentThrottlingEvent( NULL ), m_hChildThrottlingEvent( NULL ), m_dwNumChildObjects( 0 ),
  27. m_dwNumParentObjects( 0 ), m_dwNumThrottledThreads( 0 ), m_bParentThrottled( false ),
  28. m_bChildThrottled( true ), m_bChildDone( false ), m_bParentDone( false ),
  29. m_dwThrottlingThreshold( 0 ), m_dwReleaseThreshold( 0 ), m_dwLastParentPing( 0 ),
  30. m_dwLastChildPing( 0 ), m_dwProviderDeliveryTimeout( 0xFFFFFFFF ), m_dwBatchingThreshold( 0 ),
  31. m_cs()
  32. {
  33. }
  34. //***************************************************************************
  35. //
  36. //***************************************************************************
  37. //
  38. CMergerThrottling::~CMergerThrottling( void )
  39. {
  40. _DBG_ASSERT( m_dwNumChildObjects == 0 && m_dwNumParentObjects == 0 );
  41. if ( NULL != m_hParentThrottlingEvent )
  42. {
  43. CloseHandle( m_hParentThrottlingEvent );
  44. }
  45. if ( NULL != m_hChildThrottlingEvent )
  46. {
  47. CloseHandle( m_hChildThrottlingEvent );
  48. }
  49. }
  50. // Two step initialization. This retrieves values from registry to configure the
  51. // behavior of our throttling mechanisms
  52. HRESULT CMergerThrottling::Initialize( void )
  53. {
  54. HRESULT hr = WBEM_S_NO_ERROR;
  55. ConfigMgr::GetMergerThresholdValues( &m_dwThrottlingThreshold, &m_dwReleaseThreshold,
  56. &m_dwBatchingThreshold );
  57. // Hold off on this until we work our way through
  58. // m_dwProviderDeliveryTimeout = ConfigMgr::GetProviderDeliveryTimeout();
  59. return hr;
  60. }
  61. // Call this function to perform proper throttling based on our registry
  62. // configured values.
  63. HRESULT CMergerThrottling::Throttle( bool bParent, CWmiMergerRecord* pMergerRecord )
  64. {
  65. bool bContinue = true;
  66. bool bTimedOut = false;
  67. HRESULT hr = WBEM_S_NO_ERROR;
  68. while ( bContinue && SUCCEEDED( hr ) )
  69. {
  70. // Scoped for proper cleanup if anything bad happens
  71. CCheckedInCritSec ics( &m_cs ); // SEC:REVIEWED 2002-03-22 : Assumes entry
  72. DWORD dwAdjust = 0L;
  73. DWORD dwWait = 0L;
  74. // If the timed out flag is set, we need to check if we are
  75. // really timed out
  76. if ( bTimedOut )
  77. {
  78. bTimedOut = VerifyTimeout( pMergerRecord->GetWmiMerger()->GetLastDeliveryTime(),
  79. pMergerRecord->GetWmiMerger()->NumArbitratorThrottling(), &dwAdjust );
  80. }
  81. if ( bTimedOut )
  82. {
  83. hr = WBEM_E_PROVIDER_TIMED_OUT;
  84. continue;
  85. }
  86. HANDLE hEvent = ( bParent ? m_hParentThrottlingEvent : m_hChildThrottlingEvent );
  87. bool bThrottle = ShouldThrottle( bParent );
  88. // These should NEVER both be TRUE
  89. _DBG_ASSERT( !( m_bParentThrottled && m_bChildThrottled ) );
  90. if ( m_bParentThrottled && m_bChildThrottled )
  91. {
  92. hr = WBEM_E_FAILED;
  93. continue;
  94. }
  95. if ( bThrottle )
  96. {
  97. hr = PrepareThrottle( bParent, &hEvent );
  98. if (FAILED(hr)) continue;
  99. dwWait = m_dwProviderDeliveryTimeout - dwAdjust;
  100. }
  101. // Since we will wait if we choose to throttle, we should do
  102. // this OUTSIDE of our critical section
  103. ics.Leave();
  104. // Throttle only if appropriate
  105. if ( !bThrottle )
  106. {
  107. bContinue = false;
  108. continue;
  109. }
  110. // If we are about to throttle a parent, then we need to ensure a
  111. // child delivery request is scheduled
  112. if ( bParent )
  113. {
  114. hr = pMergerRecord->GetWmiMerger()->ScheduleMergerChildRequest( pMergerRecord );
  115. if (FAILED(hr)) continue;
  116. }
  117. InterlockedIncrement( (long*) &m_dwNumThrottledThreads );
  118. #ifdef __DEBUG_MERGER_THROTTLING
  119. DbgPrintfA(0,"Thread 0x%x throttled in merger %p for 0x%x ms.\nParent Objects: %d, Child Objects: %d, Num Throttled Threads: %d\n",
  120. GetCurrentThreadId(),
  121. pMergerRecord->GetWmiMerger(),
  122. dwWait,
  123. m_dwNumParentObjects,
  124. m_dwNumChildObjects,
  125. m_dwNumThrottledThreads );
  126. #endif
  127. DEBUGTRACE((LOG_WBEMCORE,
  128. "Thread 0x%x throttled in merger for 0x%x ms.\n"
  129. "Parent Objects: %d, Child Objects: %d, Num Throttled Threads: %d\n",
  130. GetCurrentThreadId(), dwWait,
  131. m_dwNumParentObjects, m_dwNumChildObjects, m_dwNumThrottledThreads));
  132. DWORD dwRet = CCoreQueue::QueueWaitForSingleObject( hEvent, dwWait );
  133. DEBUGTRACE((LOG_WBEMCORE, "Thread 0x%x woken up in merger.\n",
  134. GetCurrentThreadId() ) );
  135. #ifdef __DEBUG_MERGER_THROTTLING
  136. DbgPrintfA(0, L"Thread 0x%x woken up in merger %p.\n",
  137. GetCurrentThreadId(),
  138. pMergerRecord->GetWmiMerger());
  139. #endif
  140. InterlockedDecrement( (long*) &m_dwNumThrottledThreads );
  141. // Check for error return codes.
  142. if ( dwRet == WAIT_OBJECT_0 ) break;
  143. if ( dwRet == WAIT_TIMEOUT )
  144. {
  145. bTimedOut = true;
  146. }
  147. else
  148. {
  149. hr = WBEM_E_FAILED;
  150. }
  151. } // WHILE check for throttling
  152. return hr;
  153. }
  154. // Call this to release any actual throttled threads.
  155. bool CMergerThrottling::ReleaseThrottle( bool bForce /* = false */ )
  156. {
  157. bool bRelease = bForce;
  158. // Scoped for proper cleanup if anything bad happens
  159. CCheckedInCritSec ics( &m_cs ); // SEC:REVIEWED 2002-03-22 : Assumes entry
  160. if ( !bForce && ( m_bParentThrottled || m_bChildThrottled ) )
  161. {
  162. // These should NEVER both be TRUE
  163. _DBG_ASSERT( !( m_bParentThrottled && m_bChildThrottled ) );
  164. if ( !( m_bParentThrottled && m_bChildThrottled ) )
  165. {
  166. if ( m_bParentThrottled )
  167. {
  168. // We only release if we have exceeded the threshold.
  169. if ( m_dwNumParentObjects > m_dwNumChildObjects )
  170. {
  171. DWORD dwDiff = m_dwNumParentObjects - m_dwNumChildObjects;
  172. bRelease = ( dwDiff < m_dwReleaseThreshold );
  173. }
  174. else
  175. {
  176. // Always release if we are not greater than number of
  177. // child objects
  178. bRelease = true;
  179. }
  180. }
  181. else if ( m_bChildThrottled )
  182. {
  183. // We only release if we have exceeded the threshold.
  184. if ( m_dwNumChildObjects > m_dwNumParentObjects )
  185. {
  186. DWORD dwDiff = m_dwNumChildObjects - m_dwNumParentObjects;
  187. bRelease = ( dwDiff < m_dwReleaseThreshold );
  188. }
  189. else
  190. {
  191. // Always release if we are not greater than number of
  192. // child objects
  193. bRelease = true;
  194. }
  195. }
  196. } // Only if NOT both
  197. else
  198. {
  199. // looks like both are throttled - we shouldn't be here, but go ahead and
  200. // release anyway
  201. bRelease = true;
  202. }
  203. } // IF not bForce and something is throttled
  204. if ( bRelease )
  205. {
  206. m_bParentThrottled = false;
  207. m_bChildThrottled = false;
  208. // Should release everyone
  209. if ( NULL != m_hParentThrottlingEvent )
  210. {
  211. SetEvent( m_hParentThrottlingEvent );
  212. }
  213. // Should release everyone
  214. if ( NULL != m_hChildThrottlingEvent )
  215. {
  216. SetEvent( m_hChildThrottlingEvent );
  217. }
  218. } // IF bRelease
  219. return bRelease;
  220. }
  221. // Called to log the fact that children instances are done
  222. void CMergerThrottling::SetChildrenDone( void )
  223. {
  224. // Scoped for proper cleanup if anything bad happens
  225. CCheckedInCritSec ics( &m_cs ); // SEC:REVIEWED 2002-03-22 : Assumes entry
  226. // Child is done - we should release throttling as well
  227. m_bChildDone = true;
  228. ReleaseThrottle( true );
  229. }
  230. // Called to log the fact that parent instances are done
  231. void CMergerThrottling::SetParentDone( void )
  232. {
  233. // Scoped for proper cleanup if anything bad happens
  234. CCheckedInCritSec ics( &m_cs ); // SEC:REVIEWED 2002-03-22 : Assumes entry
  235. // Parent is done - we should release throttling as well
  236. m_bParentDone = true;
  237. ReleaseThrottle( true );
  238. }
  239. // Causes us to clear any throttling we are doing
  240. void CMergerThrottling::Cancel( void )
  241. {
  242. // Scoped for proper cleanup if anything bad happens
  243. CCheckedInCritSec ics( &m_cs ); // SEC:REVIEWED 2002-03-22 : Assumes entry
  244. // Everything is just over with - release the throttle as well
  245. m_bChildDone = true;
  246. m_bParentDone = true;
  247. // No point in tracking these anymore.
  248. m_dwNumChildObjects = 0;
  249. m_dwNumParentObjects = 0;
  250. ReleaseThrottle( true );
  251. }
  252. // Helper function to check if we should throttle
  253. bool CMergerThrottling::ShouldThrottle( bool bParent )
  254. {
  255. bool bThrottle = false;
  256. if ( bParent )
  257. {
  258. // If the child is done, no point in throttling
  259. if ( !m_bChildDone )
  260. {
  261. // If for some reason parent objects are coming in on multiple threads,
  262. // we *could* theoretically have to throttle multiple threads. If we're
  263. // not already throttling, we should check if we need to.
  264. if ( !m_bParentThrottled )
  265. {
  266. // We only throttle if we have exceeded the threshold.
  267. if ( m_dwNumParentObjects > m_dwNumChildObjects )
  268. {
  269. DWORD dwDiff = m_dwNumParentObjects - m_dwNumChildObjects;
  270. bThrottle = ( dwDiff > m_dwThrottlingThreshold );
  271. m_bParentThrottled = bThrottle;
  272. }
  273. }
  274. else
  275. {
  276. bThrottle = true;;
  277. }
  278. } // IF !m_bChildDone
  279. }
  280. else
  281. {
  282. // No point in continuing if the parent is done
  283. if ( !m_bParentDone )
  284. {
  285. // More likely that multiple child threads could be coming in (e.g. multiple
  286. // classes inheriting from a base class).
  287. if ( !m_bChildThrottled )
  288. {
  289. // We only throttle if we have exceeded the threshold.
  290. if ( m_dwNumChildObjects > m_dwNumParentObjects )
  291. {
  292. DWORD dwDiff = m_dwNumChildObjects - m_dwNumParentObjects;
  293. bThrottle = ( dwDiff > m_dwThrottlingThreshold );
  294. m_bChildThrottled = bThrottle;
  295. }
  296. }
  297. else
  298. {
  299. bThrottle = true;
  300. }
  301. } // IF !m_bParentDone
  302. }
  303. return bThrottle;
  304. }
  305. // Helper function to prepare us for throttling
  306. HRESULT CMergerThrottling::PrepareThrottle( bool bParent, HANDLE* phEvent )
  307. {
  308. HRESULT hr = WBEM_S_NO_ERROR;
  309. // Create the event if we have to, otherwise reset it
  310. if ( NULL == *phEvent )
  311. {
  312. // Creates in an unsignalled state
  313. *phEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); // SEC:REVIEWED 2002-03-22 : OK
  314. if ( NULL == *phEvent )
  315. {
  316. hr = WBEM_E_OUT_OF_MEMORY;
  317. }
  318. else if ( bParent )
  319. {
  320. m_hParentThrottlingEvent = *phEvent;
  321. }
  322. else
  323. {
  324. m_hChildThrottlingEvent = *phEvent;
  325. }
  326. }
  327. else
  328. {
  329. // Make sure the event is reset
  330. BOOL fSuccess = ResetEvent( *phEvent );
  331. // What to do here?
  332. _DBG_ASSERT( fSuccess );
  333. if ( !fSuccess )
  334. {
  335. hr = WBEM_E_FAILED;
  336. }
  337. }
  338. return hr;
  339. }
  340. // Helper function to verify if we timed out. For example, we may have timed out on throttling
  341. // but, actually be receiving (albeit slowly) objects from a child or parent. Since we are getting
  342. // stuff, we aren't really timed out, but we should adjust our wait time based on the difference
  343. // since the last ping.
  344. bool CMergerThrottling::VerifyTimeout( DWORD dwLastTick, long lNumArbThrottledThreads, DWORD* pdwAdjust )
  345. {
  346. // We only do this of no threads are throttled in the arbitrator - since we may actually
  347. // just be slow. So if there are throttled threads, we just return that we are not timed
  348. //out
  349. _DBG_ASSERT( lNumArbThrottledThreads >= 0 );
  350. if ( lNumArbThrottledThreads > 0 )
  351. {
  352. return false;
  353. }
  354. DWORD dwCurrent = GetTickCount();
  355. // We must deal with the fact that a rollover *can* occur
  356. if ( dwCurrent >= dwLastTick )
  357. {
  358. *pdwAdjust = dwCurrent - dwLastTick;
  359. }
  360. else
  361. {
  362. // Accounts for rollover - 0xFFFFFFFF minus the last tick, plus the current
  363. // plus 1 will give us the number of elapsed ticks
  364. *pdwAdjust = dwCurrent + ( 0xFFFFFFFF - dwLastTick );
  365. }
  366. // If the difference is greater
  367. return ( *pdwAdjust > m_dwProviderDeliveryTimeout );
  368. }
  369. // Sets the proper ping variable and sends it to the main merger
  370. DWORD CMergerThrottling::Ping( bool bParent, CWmiMerger* pWmiMerger )
  371. {
  372. DWORD dwTick = GetTickCount();
  373. if ( bParent )
  374. {
  375. m_dwLastParentPing = dwTick;
  376. }
  377. else
  378. {
  379. m_dwLastChildPing = dwTick;
  380. }
  381. // Sets the ping delivery
  382. pWmiMerger->PingDelivery( dwTick );
  383. return dwTick;
  384. }