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.

1262 lines
30 KiB

  1. /*++
  2. Copyright (c) 2000-2001 Microsoft Corporation
  3. Module Name:
  4. thread_manager.cxx
  5. Abstract:
  6. implementation for THREAD_MANAGER
  7. Author:
  8. Jeffrey Wall (jeffwall) 11-28-2000
  9. Revision History:
  10. --*/
  11. #include <iis.h>
  12. #include <dbgutil.h>
  13. #include "thread_manager.h"
  14. #include "thread_pool_private.h"
  15. TCHAR g_szModuleName[MAX_PATH];
  16. DWORD g_dwcCPU = 1;
  17. HMODULE g_hmodW3TPDLL = NULL;
  18. BOOL
  19. TooMuchContextSwitchingLoad(ULONG ulFirstSample,
  20. DWORD dwFirstSampleTime,
  21. ULONG ulPerSecondSwitchRateMax,
  22. DWORD dwNumProcs);
  23. BOOL
  24. TooMuchProcessorUsage(LARGE_INTEGER liOriginalBusy,
  25. LARGE_INTEGER liOriginalTotal,
  26. LONG lPercentageUseMax,
  27. DWORD dwNumProcs);
  28. BOOL GetContextSwitchCount(ULONG * pulSwitchCount);
  29. HRESULT
  30. GetCPUData(LARGE_INTEGER * pBusyTime,
  31. LARGE_INTEGER * pTotalTime,
  32. DWORD dwNumProcs);
  33. //static
  34. HRESULT
  35. THREAD_MANAGER::CreateThreadManager(THREAD_MANAGER ** ppManager,
  36. THREAD_POOL * pPool,
  37. THREAD_POOL_DATA * pPoolData)
  38. /*++
  39. Routine Description:
  40. Allocate and initialize a THREAD_MANAGER
  41. Arguments:
  42. ppManager - where to store allocated manager pointer
  43. pPool - pointer to THREAD_POOL associated with this manager
  44. pPoolData - pointer to THREAD_POOL_DATA associated with this manager
  45. Return Value:
  46. HRESULT
  47. --*/
  48. {
  49. DBG_ASSERT(NULL != ppManager);
  50. DBG_ASSERT(NULL != pPool);
  51. DBG_ASSERT(NULL != pPoolData);
  52. *ppManager = NULL;
  53. HRESULT hr = S_OK;
  54. THREAD_MANAGER * pManager = new THREAD_MANAGER(pPool, pPoolData);
  55. if (NULL == pManager)
  56. {
  57. hr = E_OUTOFMEMORY;
  58. goto done;
  59. }
  60. hr = pManager->Initialize();
  61. if (FAILED(hr))
  62. {
  63. // don't call TerminateThreadManager - just delete it
  64. delete pManager;
  65. goto done;
  66. }
  67. *ppManager = pManager;
  68. hr = S_OK;
  69. done:
  70. return hr;
  71. }
  72. VOID
  73. THREAD_MANAGER::TerminateThreadManager(LPTHREAD_STOP_ROUTINE lpStopAddress,
  74. LPVOID lpParameter)
  75. /*++
  76. Routine Description:
  77. Shutdown ALL THREAD_MANAGER theads and destroy a THREAD_MANAGER
  78. Arguments:
  79. lpStopAddress - address of function that will stop threads
  80. lpParameter - argument to pass to stop function
  81. Return Value:
  82. VOID
  83. --*/
  84. {
  85. // block until threads are gone
  86. DrainThreads(lpStopAddress, lpParameter);
  87. // do some initializion succeeded only cleanup
  88. if (m_hShutdownEvent)
  89. {
  90. CloseHandle(m_hShutdownEvent);
  91. m_hShutdownEvent = NULL;
  92. }
  93. if (m_hParkEvent)
  94. {
  95. CloseHandle(m_hParkEvent);
  96. m_hParkEvent = NULL;
  97. }
  98. DeleteCriticalSection(&m_CriticalSection);
  99. // don't do anything else after deletion
  100. delete this;
  101. return;
  102. }
  103. THREAD_MANAGER::THREAD_MANAGER(THREAD_POOL * pPool,
  104. THREAD_POOL_DATA * pPoolData) :
  105. m_dwSignature(SIGNATURE_THREAD_MANAGER),
  106. m_fShuttingDown(FALSE),
  107. m_fWaitingForCreationCallback(FALSE),
  108. m_hTimer(NULL),
  109. m_pParam(NULL),
  110. m_hParkEvent(NULL),
  111. m_hShutdownEvent(NULL),
  112. m_lParkedThreads(0),
  113. m_pPool(pPool),
  114. m_pPoolData(pPoolData),
  115. m_lTotalThreads(0)
  116. /*++
  117. Routine Description:
  118. Constructs the ThreadManager
  119. Arguments:
  120. None
  121. Return Value:
  122. None
  123. --*/
  124. {
  125. DBG_ASSERT(m_pPool);
  126. DBG_ASSERT(m_pPoolData);
  127. ZeroMemory(&m_liOriginalBusy, sizeof(LARGE_INTEGER));
  128. ZeroMemory(&m_liOriginalTotal, sizeof(LARGE_INTEGER));
  129. return;
  130. }
  131. HRESULT
  132. THREAD_MANAGER::Initialize()
  133. /*++
  134. Routine Description:
  135. Do initialization for THREAD_MANAGER
  136. Arguments:
  137. VOID
  138. Return Value:
  139. HRESULT
  140. --*/
  141. {
  142. HRESULT hr = S_OK;
  143. DWORD dwRet;
  144. HKEY hKey;
  145. BOOL fRet;
  146. // Must be set by the DLL main of the DLL loading w3tp
  147. DBG_ASSERT( NULL != g_hmodW3TPDLL );
  148. if ( GetModuleFileNameW(
  149. g_hmodW3TPDLL,
  150. g_szModuleName,
  151. MAX_PATH
  152. ) == 0 )
  153. {
  154. DBG_ASSERT(FALSE && "Failed getting module name for w3tp.dll");
  155. hr = E_FAIL;
  156. goto done;
  157. }
  158. m_hParkEvent = CreateEvent(NULL, // security descriptor
  159. FALSE, // auto reset
  160. FALSE, // not signaled at creation
  161. NULL // event name
  162. );
  163. if (NULL == m_hParkEvent)
  164. {
  165. DBG_ASSERT(FALSE && "Could not create parking event");
  166. hr = E_FAIL;
  167. goto done;
  168. }
  169. m_hShutdownEvent = CreateEvent(NULL, // security descriptor
  170. TRUE, // manual reset
  171. FALSE, // not signaled at creation
  172. NULL // event name
  173. );
  174. if (NULL == m_hShutdownEvent)
  175. {
  176. DBG_ASSERT(FALSE && "Could not create shutdown event");
  177. hr = E_FAIL;
  178. goto done;
  179. }
  180. // keep this at the end of Initialize - if it fails, no need to clean it up ever
  181. // If it succeededs, no need to clean it up in this function.
  182. // By setting the high order bit for dwSpinCount, we preallocate the CriticalSection
  183. fRet = InitializeCriticalSectionAndSpinCount(&m_CriticalSection,
  184. 0x80000000 );
  185. if (FALSE == fRet)
  186. {
  187. DBG_ASSERT(FALSE && "Could not initialize critical section!");
  188. hr = E_FAIL;
  189. goto done;
  190. }
  191. hr = S_OK;
  192. done:
  193. if (FAILED(hr))
  194. {
  195. if (m_hParkEvent)
  196. {
  197. CloseHandle(m_hParkEvent);
  198. m_hParkEvent = NULL;
  199. }
  200. if (m_hShutdownEvent)
  201. {
  202. CloseHandle(m_hShutdownEvent);
  203. m_hShutdownEvent = NULL;
  204. }
  205. }
  206. return hr;
  207. }
  208. THREAD_MANAGER::~THREAD_MANAGER()
  209. /*++
  210. Routine Description:
  211. Destructs the ThreadManager
  212. Arguments:
  213. None
  214. Return Value:
  215. None
  216. --*/
  217. {
  218. DBG_ASSERT(SIGNATURE_THREAD_MANAGER == m_dwSignature);
  219. m_dwSignature = SIGNATURE_THREAD_MANAGER_FREE;
  220. DBG_ASSERT(TRUE == m_fShuttingDown && "DrainThreads was not called!");
  221. DBG_ASSERT(NULL == m_hTimer);
  222. DBG_ASSERT(NULL == m_pParam);
  223. DBG_ASSERT(0 == m_lParkedThreads);
  224. DBG_ASSERT(0 == m_lTotalThreads);
  225. m_pPool = NULL;
  226. m_pPoolData = NULL;
  227. }
  228. //static
  229. DWORD
  230. THREAD_MANAGER::ThreadManagerThread(LPVOID ThreadParam)
  231. /*++
  232. Routine Description:
  233. Starter thread for THREAD_MANAGER created threads
  234. Takes a reference against the current DLL
  235. Notifies THREAD_MANAGER that it has started execution
  236. Calls out to "real" thread procedure
  237. Notifies THREAD_MANAGER that it is about to terminate
  238. Releases reference to current DLL and terminates
  239. Arguments:
  240. ThreadParam - parameters for control of thread
  241. Return Value:
  242. win32 error or return value from "real" thread proc
  243. --*/
  244. {
  245. HMODULE hModuleDll;
  246. DWORD dwReturnCode;
  247. THREAD_PARAM *pParam = NULL;
  248. pParam = (THREAD_PARAM*)ThreadParam;
  249. // grab a reference to this DLL
  250. hModuleDll = LoadLibrary(g_szModuleName);
  251. if (NULL == hModuleDll)
  252. {
  253. dwReturnCode = GetLastError();
  254. goto done;
  255. }
  256. // verify the thread parameter passed was reasonable
  257. DBG_ASSERT(NULL != pParam);
  258. DBG_ASSERT(NULL != pParam->pThreadManager);
  259. DBG_ASSERT(NULL != pParam->pThreadFunc);
  260. if (pParam->fCallbackOnCreation)
  261. {
  262. // Inform thread manager that this thread has successfully gotten through the loader lock
  263. pParam->pThreadManager->CreatedSuccessfully(pParam);
  264. }
  265. // actually do work thread is supposed to do
  266. dwReturnCode = pParam->pThreadFunc(pParam->pvThreadArg);
  267. done:
  268. // Inform thread manager that this thread is going away
  269. pParam->pThreadManager->RemoveThread(pParam);
  270. // Thread owns[ed] memory passed
  271. delete pParam;
  272. // release reference to this DLL
  273. FreeLibraryAndExitThread(hModuleDll, dwReturnCode);
  274. // never executed
  275. return dwReturnCode;
  276. }
  277. VOID
  278. THREAD_MANAGER::RequestThread(LPTHREAD_START_ROUTINE lpStartAddress,
  279. LPVOID lpStartParameter)
  280. /*++
  281. Routine Description:
  282. Creates a timer to determine the correct thread action to take.
  283. May create a thread in a little while
  284. May take away a thread in a little while
  285. May not create the timer if there is another thread creation going on
  286. Arguments:
  287. lpStartAddress - address of function to begin thread execution
  288. lpParameter - argument to pass to start function
  289. Return Value:
  290. VOID
  291. --*/
  292. {
  293. BOOL fRet = FALSE;
  294. HRESULT hr;
  295. DBG_ASSERT(NULL != lpStartAddress);
  296. if (TRUE == m_fShuttingDown ||
  297. TRUE == m_fWaitingForCreationCallback)
  298. {
  299. return;
  300. }
  301. //
  302. // only want to create one timer at a time
  303. //
  304. EnterCriticalSection(&m_CriticalSection);
  305. if (TRUE == m_fShuttingDown ||
  306. TRUE == m_fWaitingForCreationCallback)
  307. {
  308. LeaveCriticalSection(&m_CriticalSection);
  309. return;
  310. }
  311. // only want one thread at a time to be created
  312. m_fWaitingForCreationCallback = TRUE;
  313. DBGPRINTF(( DBG_CONTEXT, "W3TP: Thread Request received\n"));
  314. DWORD dwCurrentTime = GetTickCount();
  315. fRet = GetContextSwitchCount(&m_ulContextSwitchCount);
  316. if (FALSE == fRet)
  317. {
  318. goto done;
  319. }
  320. if ( THREAD_POOL_MAX_CPU_USAGE_DEFAULT != m_pPoolData->m_poolConfig.dwMaxCPUUsage)
  321. {
  322. hr = GetCPUData(&m_liOriginalBusy,
  323. &m_liOriginalTotal,
  324. g_dwcCPU);
  325. if (FAILED(hr))
  326. {
  327. fRet = FALSE;
  328. goto done;
  329. }
  330. }
  331. DBG_ASSERT(NULL == m_pParam);
  332. m_pParam = new THREAD_PARAM;
  333. if (NULL == m_pParam)
  334. {
  335. fRet = FALSE;
  336. goto done;
  337. }
  338. m_pParam->pThreadFunc = lpStartAddress;
  339. m_pParam->pvThreadArg = lpStartParameter;
  340. m_pParam->pThreadManager = this;
  341. m_pParam->dwRequestTime = dwCurrentTime;
  342. m_pParam->fCallbackOnCreation = TRUE;
  343. if (NULL != m_hTimer)
  344. {
  345. // if this isn't the first time we've requested a thread,
  346. // we have a previous TimerQueueTimer. This timer was not
  347. // removed during the callback, so we have to clean it up now.
  348. // this is the blocking form of the delete operation, however
  349. // since the timer has already fired it will not block.
  350. fRet = DeleteTimerQueueTimer(NULL, // default timer queue
  351. m_hTimer, // previous timer handle
  352. INVALID_HANDLE_VALUE // wait until it is removed
  353. );
  354. m_hTimer = NULL;
  355. }
  356. DBG_ASSERT(NULL == m_hTimer);
  357. fRet = CreateTimerQueueTimer(&m_hTimer, // storage for timer handle
  358. NULL, // default timer queue
  359. ControlTimerCallback, // callback function
  360. this, // callback argument
  361. m_pPoolData->m_poolConfig.dwTimerPeriod, // time til callback
  362. 0, // repeat time
  363. WT_EXECUTEONLYONCE // no repition
  364. );
  365. if (FALSE == fRet)
  366. {
  367. goto done;
  368. }
  369. fRet = TRUE;
  370. done:
  371. if (FALSE == fRet)
  372. {
  373. delete m_pParam;
  374. m_pParam = NULL;
  375. m_fWaitingForCreationCallback = FALSE;
  376. }
  377. LeaveCriticalSection(&m_CriticalSection);
  378. return;
  379. }
  380. //static
  381. VOID
  382. THREAD_MANAGER::ControlTimerCallback(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
  383. /*++
  384. Routine Description:
  385. Callback for timer created in RequestThread
  386. restores this pointer and forwards to DetermineThreadAction
  387. Arguments:
  388. lpParameter - THREAD_MANAGER this pointer
  389. Return Value:
  390. VOID
  391. --*/
  392. {
  393. THREAD_MANAGER* pThreadManager = (THREAD_MANAGER*)lpParameter;
  394. DBG_ASSERT(NULL != pThreadManager);
  395. DBG_ASSERT(NULL != pThreadManager->m_pParam);
  396. DBG_ASSERT(TRUE == pThreadManager->m_fWaitingForCreationCallback);
  397. pThreadManager->DetermineThreadAction();
  398. return;
  399. }
  400. VOID
  401. THREAD_MANAGER::DetermineThreadAction()
  402. /*++
  403. Routine Description:
  404. Try to determine correct action, create or take away a thread
  405. Will take away a thread if context switch rate is too high
  406. m_pParam must be populated
  407. Arguments:
  408. VOID
  409. Return Value:
  410. VOID
  411. --*/
  412. {
  413. BOOL fRet = FALSE;
  414. DWORD dwElapsedTime = 0;
  415. DWORD dwCurrentTime = 0;
  416. EnterCriticalSection(&m_CriticalSection);
  417. DBG_ASSERT(NULL != m_pParam);
  418. if (TRUE == m_fShuttingDown)
  419. {
  420. fRet = FALSE;
  421. goto done;
  422. }
  423. DBG_ASSERT(TRUE == m_fWaitingForCreationCallback);
  424. if (THREAD_POOL_MAX_CPU_USAGE_DEFAULT != m_pPoolData->m_poolConfig.dwMaxCPUUsage)
  425. {
  426. if (TooMuchProcessorUsage(m_liOriginalBusy,
  427. m_liOriginalTotal,
  428. m_pPoolData->m_poolConfig.dwMaxCPUUsage,
  429. g_dwcCPU))
  430. {
  431. // Too much processor usage to create a new thread
  432. fRet = FALSE;
  433. goto done;
  434. }
  435. }
  436. if (TooMuchContextSwitchingLoad(m_ulContextSwitchCount,
  437. m_pParam->dwRequestTime,
  438. m_pPoolData->m_poolConfig.dwPerSecondContextSwitchMax,
  439. g_dwcCPU))
  440. {
  441. // Switching too much
  442. DoThreadParking();
  443. fRet = FALSE;
  444. goto done;
  445. }
  446. fRet = DoThreadCreation(m_pParam);
  447. if (!fRet)
  448. {
  449. goto done;
  450. }
  451. fRet = TRUE;
  452. done:
  453. if (FALSE == fRet)
  454. {
  455. delete m_pParam;
  456. m_pParam = NULL;
  457. m_fWaitingForCreationCallback = FALSE;
  458. }
  459. else
  460. {
  461. // thread now has responsibility for the memory
  462. m_pParam = NULL;
  463. }
  464. LeaveCriticalSection(&m_CriticalSection);
  465. return;
  466. }
  467. BOOL
  468. THREAD_MANAGER::DoThreadCreation(THREAD_PARAM *pParam)
  469. /*++
  470. Routine Description:
  471. If there are no threads in parked state
  472. Creates a thread,
  473. add the HANDLE to the internal list of handles
  474. add the creation time to the internal list of times
  475. Otherwise, bring a thread out of parked state
  476. Arguments:
  477. pParam - parameter to pass to created thread
  478. Return Value:
  479. TRUE if thread is created successfully
  480. FALSE if thread is not created - either a thread was returned from
  481. parked state, OR there was a problem with creation.
  482. --*/
  483. {
  484. BOOL fRet = FALSE;
  485. HANDLE hThread = NULL;
  486. if (DoThreadUnParking())
  487. {
  488. // we have not created a new thread
  489. DBGPRINTF(( DBG_CONTEXT, "W3TP: Signaled a thread to be unparked\n"));
  490. // return false - signal that pParam needs to be freed by the caller
  491. fRet = FALSE;
  492. goto done;
  493. }
  494. // bugbug: use _beginthreadex?
  495. hThread = ::CreateThread( NULL, // default security descriptor
  496. m_pPoolData->m_poolConfig.dwInitialStackSize, // Initial size as configured
  497. ThreadManagerThread, // thread function
  498. pParam, // thread argument
  499. 0, // create running
  500. NULL // don't care for thread identifier
  501. );
  502. if( NULL == hThread )
  503. {
  504. fRet = FALSE;
  505. goto done;
  506. }
  507. // don't keep the handle around
  508. CloseHandle(hThread);
  509. DBGPRINTF(( DBG_CONTEXT, "W3TP: Created a new thread\n"));
  510. InterlockedIncrement(&m_lTotalThreads);
  511. // we've successfully created the thread!
  512. fRet = TRUE;
  513. done:
  514. return fRet;
  515. }
  516. VOID
  517. THREAD_MANAGER::DoThreadParking()
  518. /*++
  519. Routine Description:
  520. Called when context switch rate has been determined to be too high
  521. Removes a thread from participation in the thread pool, and parks it
  522. Arguments:
  523. none
  524. Return Value:
  525. VOID
  526. --*/
  527. {
  528. BOOL fRet;
  529. // make sure that we leave the starting number of threads in the pool
  530. if (m_pPoolData &&
  531. m_lTotalThreads - m_lParkedThreads <= (LONG) m_pPoolData->m_poolConfig.dwInitialThreadCount)
  532. {
  533. return;
  534. }
  535. DBGPRINTF(( DBG_CONTEXT, "W3TP: Posting to park a thread\n"));
  536. fRet = m_pPool->PostCompletion(0,
  537. ParkThread,
  538. (LPOVERLAPPED)this);
  539. DBG_ASSERT(TRUE == fRet);
  540. return;
  541. }
  542. BOOL
  543. THREAD_MANAGER::DoThreadUnParking()
  544. /*++
  545. Routine Description:
  546. Release one thread from the parked state
  547. Arguments:
  548. VOID
  549. Return Value:
  550. BOOL - TRUE if thread was released
  551. FALSE if no threads were available to release
  552. --*/
  553. {
  554. if (0 == m_lParkedThreads)
  555. {
  556. return FALSE;
  557. }
  558. SetEvent(m_hParkEvent);
  559. return TRUE;
  560. }
  561. //static
  562. VOID
  563. THREAD_MANAGER::ParkThread(DWORD dwErrorCode,
  564. DWORD dwNumberOfBytesTransferred,
  565. LPOVERLAPPED lpo)
  566. /*++
  567. Routine Description:
  568. Put a THREAD_MANAGER thread in a parked state
  569. Arguments:
  570. dwErrorCode - not used
  571. dwNumberOfBytesTransferred - not used
  572. lpo - pointer to overlapped that is really a pointer to a THREAD_MANAGER
  573. Return Value:
  574. VOID
  575. --*/
  576. {
  577. DWORD dwRet = 0;
  578. THREAD_MANAGER * pThis= (THREAD_MANAGER*)lpo;
  579. DBG_ASSERT(NULL != pThis);
  580. HANDLE arrHandles[2];
  581. arrHandles[0] = pThis->m_hParkEvent;
  582. arrHandles[1] = pThis->m_hShutdownEvent;
  583. DBGPRINTF(( DBG_CONTEXT, "W3TP: Thread parking\n"));
  584. LONG lNewParkedThreads = InterlockedIncrement(&pThis->m_lParkedThreads);
  585. if (pThis->m_pPoolData &&
  586. pThis->m_lTotalThreads - lNewParkedThreads <= (LONG) pThis->m_pPoolData->m_poolConfig.dwInitialThreadCount)
  587. {
  588. InterlockedDecrement(&pThis->m_lParkedThreads);
  589. return;
  590. }
  591. THREAD_POOL_DATA * pPoolData = pThis->m_pPoolData;
  592. DWORD dwTimeout = pPoolData->m_poolConfig.dwThreadTimeout;
  593. dwRet = WaitForMultipleObjects(2,
  594. arrHandles,
  595. FALSE,
  596. dwTimeout);
  597. InterlockedDecrement(&pThis->m_lParkedThreads);
  598. DBGPRINTF(( DBG_CONTEXT, "W3TP: Thread unparked\n"));
  599. if (WAIT_TIMEOUT == dwRet)
  600. {
  601. THREAD_POOL_DATA::ThreadPoolStop(pPoolData);
  602. return;
  603. }
  604. DBG_ASSERT(WAIT_OBJECT_0 == dwRet ||
  605. WAIT_OBJECT_0 + 1 == dwRet);
  606. return;
  607. }
  608. BOOL
  609. THREAD_MANAGER::CreateThread(LPTHREAD_START_ROUTINE lpStartAddress,
  610. LPVOID lpParameter)
  611. /*++
  612. Routine Description:
  613. Creates a thread if no other thread is being created currently
  614. Arguments:
  615. lpStartAddress - address of function to begin thread execution
  616. lpParameter - argument to pass to start function
  617. Return Value:
  618. TRUE if thread is created successfully
  619. FALSE if thread is not created
  620. --*/
  621. {
  622. BOOL fRet = FALSE;
  623. THREAD_PARAM * pParam = NULL;
  624. EnterCriticalSection(&m_CriticalSection);
  625. if (TRUE == m_fShuttingDown)
  626. {
  627. fRet = FALSE;
  628. goto done;
  629. }
  630. pParam = new THREAD_PARAM;
  631. if (NULL == pParam)
  632. {
  633. fRet = FALSE;
  634. goto done;
  635. }
  636. DBGPRINTF(( DBG_CONTEXT, "W3TP: CreateThread thread creation\n"));
  637. pParam->pThreadFunc = lpStartAddress;
  638. pParam->pvThreadArg = lpParameter;
  639. pParam->pThreadManager = this;
  640. pParam->dwRequestTime = GetTickCount();
  641. pParam->fCallbackOnCreation = FALSE;
  642. fRet = DoThreadCreation(pParam);
  643. if (FALSE == fRet)
  644. {
  645. goto done;
  646. }
  647. // created thread has responsibility for this memory
  648. pParam = NULL;
  649. fRet = TRUE;
  650. done:
  651. if (FALSE == fRet)
  652. {
  653. delete pParam;
  654. pParam = NULL;
  655. m_fWaitingForCreationCallback = FALSE;
  656. }
  657. LeaveCriticalSection(&m_CriticalSection);
  658. return fRet;
  659. }
  660. VOID
  661. THREAD_MANAGER::RemoveThread(THREAD_PARAM * pParam)
  662. /*++
  663. Routine Description:
  664. Removes given thread from list of active threads and closes handle
  665. Arguments:
  666. hThreadSelf - handle to current thread
  667. Return Value:
  668. void
  669. --*/
  670. {
  671. InterlockedDecrement(&m_lTotalThreads);
  672. return;
  673. }
  674. VOID
  675. THREAD_MANAGER::CreatedSuccessfully(THREAD_PARAM * pParam)
  676. /*++
  677. Routine Description:
  678. Notification that given thread has successfully started
  679. Arguments:
  680. hThread - current thread handle
  681. Return Value:
  682. VOID
  683. --*/
  684. {
  685. DBG_ASSERT(pParam);
  686. EnterCriticalSection(&m_CriticalSection);
  687. DBG_ASSERT(m_fWaitingForCreationCallback);
  688. m_fWaitingForCreationCallback = FALSE;
  689. LeaveCriticalSection(&m_CriticalSection);
  690. return;
  691. }
  692. VOID
  693. THREAD_MANAGER::DrainThreads(LPTHREAD_STOP_ROUTINE lpStopAddress,
  694. LPVOID lpParameter)
  695. /*++
  696. Routine Description:
  697. stop all threads currently being managed.
  698. Doesn't return until all threads are stopped.
  699. Arguments:
  700. lpStopAddress - address of function to call to signal one thread to stop
  701. Return Value:
  702. TRUE if all threads are stopped
  703. FALSE if one or more threads could not be stopped
  704. --*/
  705. {
  706. if (TRUE == m_fShuttingDown)
  707. {
  708. DBG_ASSERT(FALSE && "DrainThreads has been called previously!");
  709. return;
  710. }
  711. EnterCriticalSection(&m_CriticalSection);
  712. // stop any additional thread creation
  713. m_fShuttingDown = TRUE;
  714. LeaveCriticalSection(&m_CriticalSection);
  715. // release all parked threads
  716. SetEvent(m_hShutdownEvent);
  717. // push as many stops are there are threads running
  718. for (INT i = m_lTotalThreads; i >= 0; i--)
  719. //TODO: think about sync with interlocked and m_lTotalThreads - is there a race here?
  720. {
  721. lpStopAddress(lpParameter);
  722. }
  723. // stop the callback timer
  724. if (NULL != m_hTimer)
  725. {
  726. // block until timer is deleted
  727. DeleteTimerQueueTimer(NULL,
  728. m_hTimer,
  729. INVALID_HANDLE_VALUE);
  730. m_hTimer = NULL;
  731. if (m_pParam)
  732. {
  733. // the ownership for m_pParam moves from the creator
  734. // to the timer to the thread.
  735. // However, we just destroyed the timer - need to cleanup
  736. // the memory
  737. delete m_pParam;
  738. m_pParam = NULL;
  739. }
  740. }
  741. while(m_lTotalThreads > 0)
  742. {
  743. DBGPRINTF(( DBG_CONTEXT, "W3TP: Waiting for threads to drain, sleep 1000 \n"));
  744. Sleep(1000);
  745. }
  746. DBGPRINTF(( DBG_CONTEXT, "W3TP: All threads drained\n"));
  747. return;
  748. }
  749. BOOL
  750. GetContextSwitchCount(ULONG * pulSwitchCount)
  751. /*++
  752. Routine Description:
  753. Get the current machine context switch count
  754. Arguments:
  755. pulSwitchCount - where to store switch count
  756. Return Value:
  757. TRUE if context switch count is read correctly
  758. FALSE if context switch count could not be read
  759. --*/
  760. {
  761. DBG_ASSERT(NULL != pulSwitchCount);
  762. SYSTEM_PERFORMANCE_INFORMATION spi;
  763. ULONG ulReturnLength;
  764. NTSTATUS status;
  765. status = NtQuerySystemInformation(SystemPerformanceInformation,
  766. &spi,
  767. sizeof(spi),
  768. &ulReturnLength);
  769. if (!NT_SUCCESS(status))
  770. {
  771. return FALSE;
  772. }
  773. *pulSwitchCount = spi.ContextSwitches;
  774. return TRUE;
  775. }
  776. BOOL
  777. TooMuchContextSwitchingLoad(ULONG ulFirstSample,
  778. DWORD dwFirstSampleTime,
  779. ULONG ulPerSecondSwitchRateMax,
  780. DWORD dwNumProcs)
  781. /*++
  782. Routine Description:
  783. Determine if the system is under too much load in terms
  784. of context switches / second
  785. If dwNumProcs > 1 - the per second switch rate per processor is multiplied by two
  786. Arguments:
  787. ulFirstSample - first context switch count number
  788. dwSampleTimeInMilliseconds - how much time between first sample and calling this function
  789. ulPerSecondSwitchRateMax - Maximum switch rate per processor
  790. dwNumProcs - number of processors on machine
  791. Return Value:
  792. TRUE if context switch rate per second per processor is > ulPerSecondSwitchRateMax
  793. FALSE if context switch rate is below ulPerSecondSwitchRateMax
  794. --*/
  795. {
  796. ULONG ulSecondSample = 0;
  797. ULONG ulContextSwitchDifference = 0;
  798. double dblPerSecondSwitchRate = 0;
  799. double dblPerSecondSwitchRatePerProcessor = 0;
  800. DWORD dwCurrentTime = 0;
  801. DWORD dwElapsedTime = 0;
  802. BOOL fRet = FALSE;
  803. fRet = GetContextSwitchCount(&ulSecondSample);
  804. if (FALSE == fRet)
  805. {
  806. goto done;
  807. }
  808. dwCurrentTime = GetTickCount();
  809. if (dwCurrentTime <= dwFirstSampleTime)
  810. {
  811. // wrap around on time occurred - assume only one wrap around
  812. const DWORD MAXDWORD = MAXULONG;
  813. dwElapsedTime = MAXDWORD - dwFirstSampleTime + dwCurrentTime;
  814. }
  815. else
  816. {
  817. // no wrap around
  818. dwElapsedTime = dwCurrentTime - dwFirstSampleTime;
  819. }
  820. DBG_ASSERT(dwElapsedTime > 0);
  821. if (ulSecondSample <= ulFirstSample)
  822. {
  823. // wrap around on counter occurred - assume only one wrap around
  824. ulContextSwitchDifference = (MAXULONG - ulFirstSample) + ulSecondSample;
  825. }
  826. else
  827. {
  828. // no wrap around
  829. ulContextSwitchDifference = ulSecondSample - ulFirstSample;
  830. }
  831. DBG_ASSERT(ulContextSwitchDifference > 0);
  832. dblPerSecondSwitchRate = ulContextSwitchDifference / ( dwElapsedTime / 1000.0);
  833. dblPerSecondSwitchRatePerProcessor = dblPerSecondSwitchRate / dwNumProcs;
  834. if (dwNumProcs > 1)
  835. {
  836. // on multiproc boxes, double the allowed context switch rate per processor
  837. ulPerSecondSwitchRateMax *= 2;
  838. }
  839. if (dblPerSecondSwitchRatePerProcessor > ulPerSecondSwitchRateMax)
  840. {
  841. DBGPRINTF(( DBG_CONTEXT, "W3TP: Not creating thread, ContextSwitch rate is: %g\n", dblPerSecondSwitchRate ));
  842. fRet = TRUE;
  843. goto done;
  844. }
  845. DBGPRINTF(( DBG_CONTEXT, "W3TP: OK to create thread, ContextSwitch rate is: %g\n", dblPerSecondSwitchRate ));
  846. fRet = FALSE;
  847. done:
  848. return fRet;
  849. }
  850. HRESULT
  851. GetCPUData(LARGE_INTEGER * pBusyTime,
  852. LARGE_INTEGER * pTotalTime,
  853. DWORD dwNumProcs)
  854. /*++
  855. Routine Description:
  856. Collects the percent CPU load for all machine processors.
  857. Arguments:
  858. None
  859. Return Value:
  860. Std HRESULT.
  861. --*/
  862. {
  863. DBG_ASSERT(pBusyTime && pTotalTime);
  864. DBG_ASSERT(dwNumProcs > 0);
  865. HRESULT hr = S_OK;
  866. NTSTATUS status = STATUS_SUCCESS;
  867. LARGE_INTEGER
  868. cpuIdleTime = {0},
  869. cpuUserTime = {0},
  870. cpuKernelTime = {0},
  871. cpuBusyTime = {0},
  872. cpuTotalTime = {0},
  873. sumBusyTime = {0},
  874. sumTotalTime = {0};
  875. SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION * psppi = new SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION[dwNumProcs];
  876. if (NULL == psppi)
  877. {
  878. hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  879. goto CLEANUP;
  880. }
  881. // get the new snapshot
  882. status = NtQuerySystemInformation(
  883. SystemProcessorPerformanceInformation,
  884. psppi,
  885. sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) * dwNumProcs,
  886. NULL
  887. );
  888. if(status != STATUS_SUCCESS){
  889. hr = HRESULT_FROM_NT(status);
  890. goto CLEANUP;
  891. }
  892. // calculate
  893. for (DWORD i = 0; i < dwNumProcs; i++) {
  894. cpuTotalTime = RtlLargeIntegerAdd(psppi[i].UserTime, psppi[i].KernelTime);
  895. cpuBusyTime = RtlLargeIntegerSubtract(cpuTotalTime, psppi[i].IdleTime);
  896. sumBusyTime = RtlLargeIntegerAdd(sumBusyTime, cpuBusyTime);
  897. sumTotalTime = RtlLargeIntegerAdd(sumTotalTime, cpuTotalTime);
  898. }
  899. *pBusyTime = sumBusyTime;
  900. *pTotalTime = sumTotalTime;
  901. CLEANUP:
  902. delete [] psppi;
  903. psppi = NULL;
  904. return hr;
  905. }
  906. LONG
  907. GetPercentage(LARGE_INTEGER part, LARGE_INTEGER total)
  908. {
  909. if (0 == total.QuadPart)
  910. {
  911. return 100;
  912. }
  913. LARGE_INTEGER li;
  914. part.QuadPart *= 100;
  915. li.QuadPart = part.QuadPart / total.QuadPart;
  916. return li.LowPart;
  917. }
  918. BOOL
  919. TooMuchProcessorUsage(LARGE_INTEGER liOriginalBusy,
  920. LARGE_INTEGER liOriginalTotal,
  921. LONG lPercentageUseMax,
  922. DWORD dwNumProcs)
  923. {
  924. HRESULT hr = S_OK;
  925. LARGE_INTEGER
  926. liNewBusy = {0},
  927. liNewTotal = {0},
  928. liDiffBusy = {0},
  929. liDiffTotal = {0};
  930. hr = GetCPUData(&liNewBusy,
  931. &liNewTotal,
  932. dwNumProcs);
  933. if (FAILED(hr))
  934. {
  935. return TRUE;
  936. }
  937. liDiffBusy = RtlLargeIntegerSubtract(liNewBusy, liOriginalBusy);
  938. liDiffTotal = RtlLargeIntegerSubtract(liNewTotal, liOriginalTotal);
  939. LONG lPercentageUse = GetPercentage(liDiffBusy, liDiffTotal);
  940. if (lPercentageUse >= lPercentageUseMax)
  941. {
  942. return TRUE;
  943. }
  944. return FALSE;
  945. }