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.

1277 lines
34 KiB

  1. /*++
  2. Copyright (C) 1996-2001 Microsoft Corporation
  3. Module Name:
  4. EXECQ.CPP
  5. Abstract:
  6. Implements classes related to abstract execution queues.
  7. Classes implemeted:
  8. CExecRequest An abstract request.
  9. CExecQueue A queue of requests with an associated thread
  10. History:
  11. 23-Jul-96 raymcc Created.
  12. 3/10/97 levn Fully documented (heh, heh)
  13. 14-Aug-99 raymcc Changed timeouts
  14. 30-Oct-99 raymcc Critsec changes for NT Wksta Stress Oct 30 1999
  15. --*/
  16. #include "precomp.h"
  17. #include <stdio.h>
  18. #include <wbemcomn.h>
  19. #include <execq.h>
  20. #include <cominit.h>
  21. #include <sync.h>
  22. #include "genutils.h"
  23. #define IDLE_THREAD_TIMEOUT 12000
  24. #define OVERLFLOW_TIMEOUT 5000
  25. //***************************************************************************
  26. // Local wrapper class. Does not initialize or clean up the critsec. Simply
  27. // used as a wrapper for scoping so that AV and exception stack unwinding will
  28. // cause the critsec to be exited properly once it is entered.
  29. class CCritSecWrapper
  30. {
  31. BOOL m_bIn;
  32. CRITICAL_SECTION *m_pcs;
  33. public:
  34. CCritSecWrapper(CRITICAL_SECTION *pcs) { m_pcs = pcs; m_bIn = FALSE; }
  35. ~CCritSecWrapper() { if (m_bIn) Leave(); }
  36. void Enter() { EnterCriticalSection(m_pcs); m_bIn = TRUE; }
  37. void Leave() { LeaveCriticalSection(m_pcs); m_bIn = FALSE; }
  38. };
  39. //***************************************************************************
  40. long CExecQueue::mstatic_lNumInits = -1;
  41. POLARITY DWORD mstatic_dwTlsIndex = 0xFFFFFFFF;
  42. class CTlsStaticCleanUp
  43. {
  44. public:
  45. CTlsStaticCleanUp() {}
  46. ~CTlsStaticCleanUp() { if (mstatic_dwTlsIndex != 0xFFFFFFFF) TlsFree(mstatic_dwTlsIndex); }
  47. };
  48. CTlsStaticCleanUp g_tlsStaticCleanup;
  49. #ifdef WINMGMT_THREAD_DEBUG
  50. CCritSec CExecRequest::mstatic_cs;
  51. CPointerArray<CExecRequest> CExecRequest::mstatic_apOut;
  52. #define THREADDEBUGTRACE DEBUGTRACE
  53. #else
  54. #define THREADDEBUGTRACE(X)
  55. #endif
  56. CExecRequest::CExecRequest() : m_hWhenDone(NULL), m_pNext(NULL), m_lPriority(0), m_fOk( true )
  57. {
  58. #ifdef WINMGMT_THREAD_DEBUG
  59. CInCritSec ics(&mstatic_cs);
  60. mstatic_apOut.Add(this);
  61. #endif
  62. }
  63. CExecRequest::~CExecRequest()
  64. {
  65. #ifdef WINMGMT_THREAD_DEBUG
  66. CInCritSec ics(&mstatic_cs);
  67. for(int i = 0; i < mstatic_apOut.GetSize(); i++)
  68. {
  69. if(mstatic_apOut[i] == this)
  70. {
  71. mstatic_apOut.RemoveAt(i);
  72. break;
  73. }
  74. }
  75. #endif
  76. }
  77. DWORD CExecQueue::GetTlsIndex()
  78. {
  79. return mstatic_dwTlsIndex;
  80. }
  81. CExecQueue::CThreadRecord::CThreadRecord(CExecQueue* pQueue)
  82. : m_pQueue(pQueue), m_pCurrentRequest(NULL), m_bReady(FALSE),
  83. m_bExitNow(FALSE)
  84. {
  85. m_hAttention = CreateEvent(NULL, FALSE, FALSE, NULL);
  86. }
  87. CExecQueue::CThreadRecord::~CThreadRecord()
  88. {
  89. CloseHandle(m_hAttention);
  90. CloseHandle(m_hThread);
  91. }
  92. void CExecQueue::CThreadRecord::Signal()
  93. {
  94. SetEvent(m_hAttention);
  95. }
  96. //******************************************************************************
  97. //
  98. // See execq.h for documentation
  99. //
  100. //******************************************************************************
  101. CExecQueue::CExecQueue() : m_lNumThreads(0), m_lMaxThreads(1), m_lNumIdle(0),
  102. m_lNumRequests(0), m_pHead(NULL), m_pTail(NULL), m_dwTimeout(IDLE_THREAD_TIMEOUT),
  103. m_dwOverflowTimeout(OVERLFLOW_TIMEOUT), m_lHiPriBound(-1), m_lHiPriMaxThreads(1),
  104. m_lRef(0)
  105. {
  106. InitTls();
  107. InitializeCriticalSection(&m_cs);
  108. SetRequestLimits(4000);
  109. }
  110. //******************************************************************************
  111. //
  112. // See execq.h for documentation
  113. //
  114. //******************************************************************************
  115. CExecQueue::~CExecQueue()
  116. {
  117. Shutdown();
  118. DeleteCriticalSection(&m_cs);
  119. }
  120. void CExecQueue::Shutdown()
  121. {
  122. CCritSecWrapper cs(&m_cs);
  123. // Get all member thread handles
  124. // =============================
  125. cs.Enter();
  126. int nNumHandles = m_aThreads.Size();
  127. HANDLE* ah = new HANDLE[nNumHandles];
  128. DEBUGTRACE((LOG_WBEMCORE, "Queue is shutting down!\n"));
  129. int i, j=0;
  130. for(i = 0; i < nNumHandles; i++)
  131. {
  132. CThreadRecord* pRecord = (CThreadRecord*)m_aThreads[i];
  133. if ( pRecord->m_hThread )
  134. {
  135. ah[j++] = pRecord->m_hThread;
  136. }
  137. // Inform the thread it should go away when ready
  138. // ==============================================
  139. pRecord->m_bExitNow = TRUE;
  140. // Wake it up if necessary
  141. // =======================
  142. pRecord->Signal();
  143. }
  144. cs.Leave();
  145. // Make sure all our threads are gone
  146. // ==================================
  147. if(j > 0)
  148. {
  149. DWORD dwRet = WaitForMultipleObjects(j, ah, TRUE, INFINITE);
  150. _DBG_ASSERT( dwRet != WAIT_FAILED );
  151. }
  152. for(i = 0; i < j; i++)
  153. CloseHandle(ah[i]);
  154. delete [] ah;
  155. // Remove all outstanding requests
  156. // ===============================
  157. while(m_pHead)
  158. {
  159. CExecRequest* pReq = m_pHead;
  160. m_pHead = m_pHead->GetNext();
  161. delete pReq;
  162. }
  163. }
  164. //******************************************************************************
  165. //
  166. // See execq.h for documentation
  167. //
  168. //******************************************************************************
  169. // static
  170. void CExecQueue::InitTls()
  171. {
  172. if(InterlockedIncrement(&mstatic_lNumInits) == 0)
  173. {
  174. mstatic_dwTlsIndex = TlsAlloc();
  175. }
  176. }
  177. void CExecQueue::Enter()
  178. {
  179. EnterCriticalSection(&m_cs);
  180. }
  181. void CExecQueue::Leave()
  182. {
  183. LeaveCriticalSection(&m_cs);
  184. }
  185. //******************************************************************************
  186. //
  187. // See dbgalloc.h for documentation
  188. //
  189. //******************************************************************************
  190. void CExecQueue::Register(CThreadRecord* pRecord)
  191. {
  192. TlsSetValue(mstatic_dwTlsIndex, (void*)pRecord);
  193. }
  194. BOOL CExecQueue::IsSuitableThread(CThreadRecord* pRecord, CExecRequest* pReq)
  195. {
  196. if(pRecord->m_pCurrentRequest == NULL)
  197. return TRUE;
  198. // This thread is in the middle of something. By default, ignore it
  199. // ================================================================
  200. return FALSE;
  201. }
  202. //******************************************************************************
  203. //
  204. // See dbgalloc.h for documentation
  205. //
  206. //******************************************************************************
  207. HRESULT CExecQueue::Enqueue(CExecRequest* pRequest, HANDLE* phWhenDone)
  208. {
  209. CCritSecWrapper cs(&m_cs);
  210. // Check if the request has a problem with it. If so, return the
  211. // appropriate error code.
  212. if ( !pRequest->IsOk() )
  213. {
  214. return WBEM_E_OUT_OF_MEMORY;
  215. }
  216. #ifdef __COLLECT_ALLOC_STAT
  217. pRequest->m_Stack.Create(0, FALSE);
  218. #endif
  219. // Create an event handle to signal when request is finished, if required
  220. // ======================================================================
  221. if(phWhenDone)
  222. {
  223. *phWhenDone = CreateEvent(NULL, FALSE, FALSE, NULL);
  224. pRequest->SetWhenDoneHandle(*phWhenDone);
  225. }
  226. cs.Enter();
  227. // Search for a suitable thread
  228. // ============================
  229. for(int i = 0; i < m_aThreads.Size(); i++)
  230. {
  231. CThreadRecord* pRecord = (CThreadRecord*)m_aThreads[i];
  232. if(pRecord->m_bReady)
  233. {
  234. // Free. Check if suitable
  235. // =======================
  236. if(IsSuitableThread(pRecord, pRequest))
  237. {
  238. THREADDEBUGTRACE((LOG_WBEMCORE, "Enqueueing request %p onto a "
  239. "thread record %p. Previous request: %p\n", pRequest,
  240. pRecord, pRecord->m_pCurrentRequest));
  241. pRecord->m_pCurrentRequest = pRequest;
  242. THREADDEBUGTRACE((LOG_WBEMCORE, "In Enqueue, changing m_bReady "
  243. "for thread %p to 0. Was: %d\n", pRecord,
  244. pRecord->m_bReady));
  245. pRecord->m_bReady = FALSE;
  246. pRecord->Signal();
  247. m_lNumIdle--;
  248. // Done!
  249. // =====
  250. cs.Leave();
  251. return WBEM_S_NO_ERROR;
  252. }
  253. }
  254. }
  255. THREADDEBUGTRACE((LOG_WBEMCORE, "Enqueueing request %p onto the queue\n",
  256. pRequest));
  257. // No suitable thread found. Add to the queue
  258. // ==========================================
  259. if(m_lNumRequests >= m_lAbsoluteLimitCount)
  260. {
  261. cs.Leave();
  262. return WBEM_E_FAILED;
  263. }
  264. // Search for insert position based on priority
  265. // ============================================
  266. AdjustInitialPriority(pRequest);
  267. CExecRequest* pCurrent = m_pHead;
  268. CExecRequest* pLast = NULL;
  269. while(pCurrent && pCurrent->GetPriority() <= pRequest->GetPriority())
  270. {
  271. pLast = pCurrent;
  272. pCurrent = pCurrent->GetNext();
  273. }
  274. // Insert
  275. // ======
  276. if(pCurrent)
  277. {
  278. pRequest->SetNext(pCurrent);
  279. }
  280. else
  281. {
  282. m_pTail = pRequest;
  283. }
  284. if(pLast)
  285. {
  286. pLast->SetNext(pRequest);
  287. }
  288. else
  289. {
  290. m_pHead= pRequest;
  291. }
  292. m_lNumRequests++;
  293. // Adjust priorities of the loosers
  294. // ================================
  295. while(pCurrent)
  296. {
  297. AdjustPriorityForPassing(pCurrent);
  298. pCurrent = pCurrent->GetNext();
  299. }
  300. // Create a new thread, if required
  301. // ================================
  302. if(DoesNeedNewThread(pRequest))
  303. CreateNewThread();
  304. long lIndex = m_lNumRequests;
  305. cs.Leave();
  306. // Sit out whatever penalty is imposed
  307. // ===================================
  308. SitOutPenalty(lIndex);
  309. return WBEM_S_NO_ERROR;
  310. }
  311. //******************************************************************************
  312. //
  313. // See dbgalloc.h for documentation
  314. //
  315. //******************************************************************************
  316. HRESULT CExecQueue::EnqueueWithoutSleep(CExecRequest* pRequest, HANDLE* phWhenDone)
  317. {
  318. CCritSecWrapper cs(&m_cs);
  319. // Check if the request has a problem with it. If so, return the
  320. // appropriate error code.
  321. if ( !pRequest->IsOk() )
  322. {
  323. return WBEM_E_OUT_OF_MEMORY;
  324. }
  325. #ifdef __COLLECT_ALLOC_STAT
  326. pRequest->m_Stack.Create(0, FALSE);
  327. #endif
  328. // Create an event handle to signal when request is finished, if required
  329. // ======================================================================
  330. if(phWhenDone)
  331. {
  332. *phWhenDone = CreateEvent(NULL, FALSE, FALSE, NULL);
  333. pRequest->SetWhenDoneHandle(*phWhenDone);
  334. }
  335. cs.Enter();
  336. // Search for a suitable thread
  337. // ============================
  338. for(int i = 0; i < m_aThreads.Size(); i++)
  339. {
  340. CThreadRecord* pRecord = (CThreadRecord*)m_aThreads[i];
  341. if(pRecord->m_bReady)
  342. {
  343. // Free. Check if suitable
  344. // =======================
  345. if(IsSuitableThread(pRecord, pRequest))
  346. {
  347. THREADDEBUGTRACE((LOG_WBEMCORE, "Enqueueing request %p onto a "
  348. "thread record %p. Previous request: %p\n", pRequest,
  349. pRecord, pRecord->m_pCurrentRequest));
  350. pRecord->m_pCurrentRequest = pRequest;
  351. THREADDEBUGTRACE((LOG_WBEMCORE, "In Enqueue, changing m_bReady "
  352. "for thread %p to 0. Was: %d\n", pRecord,
  353. pRecord->m_bReady));
  354. pRecord->m_bReady = FALSE;
  355. pRecord->Signal();
  356. m_lNumIdle--;
  357. // Done!
  358. // =====
  359. cs.Leave();
  360. return WBEM_S_NO_ERROR;
  361. }
  362. }
  363. }
  364. THREADDEBUGTRACE((LOG_WBEMCORE, "Enqueueing request %p onto the queue\n",
  365. pRequest));
  366. // No suitable thread found. Add to the queue
  367. // ==========================================
  368. if(m_lNumRequests >= m_lAbsoluteLimitCount)
  369. {
  370. cs.Leave();
  371. return WBEM_E_FAILED;
  372. }
  373. // Search for insert position based on priority
  374. // ============================================
  375. AdjustInitialPriority(pRequest);
  376. CExecRequest* pCurrent = m_pHead;
  377. CExecRequest* pLast = NULL;
  378. while(pCurrent && pCurrent->GetPriority() <= pRequest->GetPriority())
  379. {
  380. pLast = pCurrent;
  381. pCurrent = pCurrent->GetNext();
  382. }
  383. // Insert
  384. // ======
  385. if(pCurrent)
  386. {
  387. pRequest->SetNext(pCurrent);
  388. }
  389. else
  390. {
  391. m_pTail = pRequest;
  392. }
  393. if(pLast)
  394. {
  395. pLast->SetNext(pRequest);
  396. }
  397. else
  398. {
  399. m_pHead= pRequest;
  400. }
  401. m_lNumRequests++;
  402. // Adjust priorities of the loosers
  403. // ================================
  404. while(pCurrent)
  405. {
  406. AdjustPriorityForPassing(pCurrent);
  407. pCurrent = pCurrent->GetNext();
  408. }
  409. // Create a new thread, if required
  410. // ================================
  411. if(DoesNeedNewThread(pRequest))
  412. CreateNewThread();
  413. long lIndex = m_lNumRequests;
  414. cs.Leave();
  415. // Sit out whatever penalty is imposed
  416. // ===================================
  417. // DWORD dwSleep = CalcSitOutPenalty(lIndex);
  418. return WBEM_S_NO_ERROR;
  419. }
  420. DWORD CExecQueue::CalcSitOutPenalty(long lRequestIndex)
  421. {
  422. if(lRequestIndex <= m_lStartSlowdownCount)
  423. return 0; // no penalty
  424. if(lRequestIndex >= m_lAbsoluteLimitCount)
  425. lRequestIndex = m_lAbsoluteLimitCount;
  426. // Calculate the timeout
  427. // =====================
  428. double dblTimeout =
  429. m_dblAlpha / (m_lAbsoluteLimitCount - lRequestIndex) +
  430. m_dblBeta;
  431. // Return penalty
  432. // ===========
  433. return ((DWORD) dblTimeout);
  434. }
  435. void CExecQueue::SitOutPenalty(long lRequestIndex)
  436. {
  437. DWORD dwSitOutPenalty = CalcSitOutPenalty( lRequestIndex );
  438. // Sleep on it
  439. // ===========
  440. if ( 0 != dwSitOutPenalty )
  441. {
  442. Sleep( dwSitOutPenalty );
  443. }
  444. }
  445. HRESULT CExecQueue::EnqueueAndWait(CExecRequest* pRequest)
  446. {
  447. if(IsAppropriateThread())
  448. {
  449. pRequest->Execute();
  450. delete pRequest;
  451. return WBEM_S_NO_ERROR;
  452. }
  453. HANDLE hWhenDone;
  454. HRESULT hr = Enqueue(pRequest, &hWhenDone);
  455. if ( FAILED(hr) )
  456. {
  457. return hr;
  458. }
  459. DWORD dwRes = WbemWaitForSingleObject(hWhenDone, INFINITE);
  460. CloseHandle(hWhenDone);
  461. return ( dwRes == WAIT_OBJECT_0 ? WBEM_S_NO_ERROR : WBEM_E_FAILED );
  462. }
  463. BOOL CExecQueue::DoesNeedNewThread(CExecRequest* pRequest)
  464. {
  465. if(m_lNumIdle > 0 || m_lNumRequests == 0)
  466. return FALSE;
  467. if(m_lNumThreads < m_lMaxThreads)
  468. return TRUE;
  469. else if(pRequest->GetPriority() <= m_lHiPriBound &&
  470. m_lNumThreads < m_lHiPriMaxThreads)
  471. return TRUE;
  472. else
  473. return FALSE;
  474. }
  475. //******************************************************************************
  476. //
  477. // See dbgalloc.h for documentation
  478. //
  479. //******************************************************************************
  480. BOOL CExecQueue::Execute(CThreadRecord* pRecord)
  481. {
  482. CExecRequest* pReq = pRecord->m_pCurrentRequest;
  483. #ifdef __COLLECT_ALLOC_STAT
  484. CStackContinuation Cont;
  485. Cont.m_pPrevStack = &pReq->m_Stack;
  486. CStackContinuation* pPrev = CStackContinuation::Set(&Cont);
  487. #endif
  488. HRESULT hres = pReq->Execute();
  489. #ifdef __COLLECT_ALLOC_STAT
  490. CStackContinuation::Set(pPrev);
  491. #endif
  492. if(hres == RPC_E_RETRY)
  493. {
  494. // The request has been postponed
  495. // ==============================
  496. THREADDEBUGTRACE((LOG_WBEMCORE, "Thread %p postponed request %p\n",
  497. pRecord, pReq));
  498. }
  499. else
  500. {
  501. if(hres != WBEM_NO_ERROR)
  502. {
  503. LogError(pReq, hres);
  504. }
  505. HANDLE hWhenDone = pReq->GetWhenDoneHandle();
  506. if(hWhenDone != NULL)
  507. {
  508. SetEvent(hWhenDone);
  509. }
  510. THREADDEBUGTRACE((LOG_WBEMCORE, "Thread %p done with request %p\n",
  511. pRecord, pReq));
  512. delete pReq;
  513. }
  514. pRecord->m_pCurrentRequest = NULL;
  515. return TRUE;
  516. }
  517. //******************************************************************************
  518. //
  519. // See dbgalloc.h for documentation
  520. //
  521. //******************************************************************************
  522. void CExecQueue::LogError(CExecRequest* pRequest, int nRes)
  523. {
  524. DEBUGTRACE((LOG_WBEMCORE,
  525. "Error %X occured executing queued request\n", nRes));
  526. pRequest->DumpError();
  527. }
  528. void CExecQueue::InitializeThread()
  529. {
  530. InitializeCom();
  531. }
  532. void CExecQueue::UninitializeThread()
  533. {
  534. if(IsDcomEnabled() || IsNT())
  535. CoUninitialize();
  536. }
  537. CExecRequest* CExecQueue::SearchForSuitableRequest(CThreadRecord* pRecord)
  538. {
  539. // Assumes in critical section
  540. // ===========================
  541. CExecRequest* pCurrent = m_pHead;
  542. CExecRequest* pPrev = NULL;
  543. while(pCurrent)
  544. {
  545. if(IsSuitableThread(pRecord, pCurrent))
  546. {
  547. // Found one --- take it
  548. // =====================
  549. if(pPrev)
  550. pPrev->SetNext(pCurrent->GetNext());
  551. else
  552. m_pHead = pCurrent->GetNext();
  553. if(pCurrent == m_pTail)
  554. m_pTail = pPrev;
  555. m_lNumRequests--;
  556. break;
  557. }
  558. pPrev = pCurrent;
  559. pCurrent = pCurrent->GetNext();
  560. }
  561. return pCurrent;
  562. }
  563. //******************************************************************************
  564. //
  565. // See dbgalloc.h for documentation
  566. //
  567. //******************************************************************************
  568. void CExecQueue::ThreadMain(CThreadRecord* pRecord)
  569. {
  570. CCritSecWrapper cs(&m_cs);
  571. InitializeThread();
  572. // Register this queue with this thread, so any further wait would be
  573. // interruptable
  574. // ==================================================================
  575. Register(pRecord);
  576. while (1)
  577. {
  578. // Returning from work. At this point, our event is not signaled,
  579. // our m_pCurrentRequest is NULL and our m_bReady is FALSE
  580. // ====================================================================
  581. // Search for work in the queue
  582. // ============================
  583. cs.Enter();
  584. CExecRequest* pCurrent = SearchForSuitableRequest(pRecord);
  585. if(pCurrent)
  586. {
  587. // Found some. Take it
  588. // ===================
  589. pRecord->m_pCurrentRequest = pCurrent;
  590. }
  591. else
  592. {
  593. // No work in the queue. Wait
  594. // ==========================
  595. THREADDEBUGTRACE((LOG_WBEMCORE, "ThreadMain of %p is setting Ready to "
  596. "TRUE. Was: %d\n", pRecord, pRecord->m_bReady));
  597. pRecord->m_bReady = TRUE;
  598. m_lNumIdle++;
  599. DWORD dwTimeout = GetIdleTimeout(pRecord);
  600. cs.Leave();
  601. DWORD dwRes = WbemWaitForSingleObject(pRecord->m_hAttention,
  602. dwTimeout);
  603. cs.Enter();
  604. if(dwRes != WAIT_OBJECT_0)
  605. {
  606. // Check if someone managed to place a request in our record
  607. // after the timeout.
  608. // =========================================================
  609. if(WbemWaitForSingleObject(pRecord->m_hAttention, 0) ==
  610. WAIT_OBJECT_0)
  611. {
  612. DEBUGTRACE((LOG_WBEMCORE, "AMAZING: Thread %p received "
  613. "request %p after timing out. Returning to the "
  614. "queue\n", pRecord, pRecord->m_pCurrentRequest));
  615. if(pRecord->m_bExitNow || pRecord->m_pCurrentRequest == NULL)
  616. {
  617. THREADDEBUGTRACE((LOG_WBEMCORE, "Thread %p being stopped --- %d "
  618. "requests in the queue (head = %p)\n", pRecord,
  619. pRecord->m_pQueue->m_lNumRequests,
  620. pRecord->m_pQueue->m_pHead));
  621. ShutdownThread(pRecord);
  622. cs.Leave();
  623. return;
  624. }
  625. pRecord->m_pQueue->Enqueue(pRecord->m_pCurrentRequest);
  626. pRecord->m_pCurrentRequest = NULL;
  627. }
  628. // Timeout. See if it is time to quit
  629. // ==================================
  630. THREADDEBUGTRACE((LOG_WBEMCORE, "ThreadMain of %p is setting Ready "
  631. " to FALSE on timeout. Was: %d\n",
  632. pRecord, pRecord->m_bReady));
  633. pRecord->m_bReady = FALSE;
  634. if(IsIdleTooLong(pRecord, dwTimeout))
  635. {
  636. THREADDEBUGTRACE((LOG_WBEMCORE, "Thread %p timing out --- %d "
  637. "requests in the queue (head = %p)\n", pRecord,
  638. pRecord->m_pQueue->m_lNumRequests,
  639. pRecord->m_pQueue->m_pHead));
  640. ShutdownThread(pRecord);
  641. cs.Leave();
  642. return;
  643. }
  644. // Go and wait a little more
  645. // =========================
  646. m_lNumIdle--;
  647. cs.Leave();
  648. continue;
  649. }
  650. else
  651. {
  652. // Check why we were awaken
  653. // ========================
  654. if(pRecord->m_bExitNow || pRecord->m_pCurrentRequest == NULL)
  655. {
  656. THREADDEBUGTRACE((LOG_WBEMCORE, "Thread %p being stopped --- %d "
  657. "requests in the queue (head = %p)\n", pRecord,
  658. pRecord->m_pQueue->m_lNumRequests,
  659. pRecord->m_pQueue->m_pHead));
  660. ShutdownThread(pRecord);
  661. cs.Leave();
  662. return;
  663. }
  664. // We have a request. Enqueue already adjusted lNumIdle and
  665. // our m_bReady;
  666. }
  667. }
  668. // Execute the request
  669. // ===================
  670. #ifdef WINMGMT_THREAD_DEBUG
  671. if(pRecord->m_bReady)
  672. DebugBreak();
  673. #endif
  674. THREADDEBUGTRACE((LOG_WBEMCORE, "Thread %p picking up normal request %p\n",
  675. pRecord, pRecord->m_pCurrentRequest));
  676. cs.Leave();
  677. Execute(pRecord);
  678. #ifdef WINMGMT_THREAD_DEBUG
  679. if(pRecord->m_bReady)
  680. DebugBreak();
  681. #endif
  682. }
  683. }
  684. DWORD CExecQueue::GetIdleTimeout(CThreadRecord* pRecord)
  685. {
  686. if(m_lNumThreads > m_lMaxThreads)
  687. return m_dwOverflowTimeout;
  688. else
  689. return m_dwTimeout;
  690. }
  691. BOOL CExecQueue::IsIdleTooLong(CThreadRecord* pRecord, DWORD dwTimeout)
  692. {
  693. if(m_lNumThreads > m_lMaxThreads)
  694. return TRUE;
  695. else if(dwTimeout < m_dwTimeout)
  696. return FALSE;
  697. else
  698. return TRUE;
  699. }
  700. void CExecQueue::ShutdownThread(CThreadRecord* pRecord)
  701. {
  702. CCritSecWrapper cs(&m_cs);
  703. cs.Enter();
  704. TlsSetValue(mstatic_dwTlsIndex, NULL);
  705. for(int i = 0; i < m_aThreads.Size(); i++)
  706. {
  707. if(m_aThreads[i] == pRecord)
  708. {
  709. m_aThreads.RemoveAt(i);
  710. // Make sure we don't close the handle if the queue's Shutdown is
  711. // waiting on it
  712. // ==============================================================
  713. if(pRecord->m_bExitNow)
  714. pRecord->m_hThread = NULL;
  715. delete pRecord;
  716. m_lNumIdle--;
  717. m_lNumThreads--;
  718. break;
  719. }
  720. }
  721. UninitializeThread();
  722. cs.Leave();
  723. }
  724. //******************************************************************************
  725. //
  726. // See dbgalloc.h for documentation
  727. //
  728. //******************************************************************************
  729. // static
  730. DWORD WINAPI CExecQueue::_ThreadEntry(LPVOID pObj)
  731. {
  732. CThreadRecord* pRecord = (CThreadRecord*)pObj;
  733. pRecord->m_pQueue->ThreadMain(pRecord);
  734. return 0;
  735. }
  736. //******************************************************************************
  737. //
  738. // See dbgalloc.h for documentation
  739. //
  740. //******************************************************************************
  741. BOOL CExecQueue::CreateNewThread()
  742. {
  743. CCritSecWrapper cs(&m_cs);
  744. BOOL bRet;
  745. cs.Enter();
  746. // Create new thread record
  747. // ========================
  748. CThreadRecord* pNewRecord = new CThreadRecord(this);
  749. if (pNewRecord)
  750. {
  751. m_aThreads.Add(pNewRecord);
  752. DWORD dwId;
  753. pNewRecord->m_hThread = CreateThread(0, 0, _ThreadEntry, pNewRecord, 0,
  754. &dwId);
  755. if(pNewRecord->m_hThread == NULL)
  756. {
  757. m_aThreads.RemoveAt(m_aThreads.Size()-1);
  758. delete pNewRecord;
  759. bRet = FALSE;
  760. }
  761. else
  762. {
  763. m_lNumThreads++;
  764. bRet = TRUE;
  765. }
  766. }
  767. else
  768. bRet = FALSE;
  769. cs.Leave();
  770. return bRet;
  771. }
  772. DWORD CompensateForBug(DWORD dwOriginal, DWORD dwElapsed)
  773. {
  774. if(dwOriginal == 0xFFFFFFFF)
  775. return 0xFFFFFFFF;
  776. DWORD dwLeft = dwOriginal - dwElapsed;
  777. if(dwLeft > 0x7FFFFFFF)
  778. dwLeft = 0x7FFFFFFF;
  779. return dwLeft;
  780. }
  781. DWORD CExecQueue::WaitForSingleObjectWhileBusy(HANDLE hHandle, DWORD dwWait,
  782. CThreadRecord* pRecord)
  783. {
  784. CCritSecWrapper cs(&m_cs);
  785. CExecRequest* pOld = pRecord->m_pCurrentRequest;
  786. DWORD dwStart = GetTickCount();
  787. while (dwWait > GetTickCount() - dwStart)
  788. {
  789. // Search for work in the queue
  790. // ============================
  791. cs.Enter();
  792. CExecRequest* pCurrent = SearchForSuitableRequest(pRecord);
  793. if(pCurrent != NULL)
  794. {
  795. THREADDEBUGTRACE((LOG_WBEMCORE, "QUEUE: While busy, found work in the "
  796. "queue: thread %p, request %p, old request %p\n", pRecord,
  797. pCurrent, pRecord->m_pCurrentRequest));
  798. pRecord->m_pCurrentRequest = pCurrent;
  799. if(pRecord->m_pCurrentRequest == pOld)
  800. {
  801. // Something is very wrong
  802. // =======================
  803. #ifdef WINMGMT_THREAD_DEBUG
  804. DebugBreak();
  805. #endif
  806. }
  807. }
  808. else
  809. {
  810. // No work in the queue. Wait
  811. // ==========================
  812. THREADDEBUGTRACE((LOG_WBEMCORE, "While Busy thread %p is setting Ready "
  813. "to TRUE. Was: %d\n", pRecord, pRecord->m_bReady));
  814. pRecord->m_bReady = TRUE;
  815. // Block until a request comes through.
  816. // ====================================
  817. HANDLE ahSems[2];
  818. ahSems[0] = hHandle;
  819. ahSems[1] = pRecord->m_hAttention;
  820. cs.Leave();
  821. DWORD dwLeft = CompensateForBug(dwWait, (GetTickCount() - dwStart));
  822. DWORD dwRes = WbemWaitForMultipleObjects(2, ahSems, dwLeft);
  823. cs.Enter();
  824. THREADDEBUGTRACE((LOG_WBEMCORE, "While Busy thread %p is setting Ready "
  825. "to FALSE. Was: %d\n", pRecord, pRecord->m_bReady));
  826. pRecord->m_bReady = FALSE;
  827. if(dwRes != WAIT_OBJECT_0 + 1)
  828. {
  829. // Either our target handle is ready or we timed out
  830. // =================================================
  831. // Check if anyone placed a request in our record
  832. // ==============================================
  833. if(pRecord->m_pCurrentRequest != pOld)
  834. {
  835. // Re-issue it to the queue
  836. // ========================
  837. DEBUGTRACE((LOG_WBEMCORE, "SURPRIZE: Somebody placed "
  838. "request %p into thread record %p while it was getting "
  839. "ready to continue. Reissuing\n",
  840. pRecord->m_pCurrentRequest, pRecord));
  841. pRecord->m_pQueue->Enqueue(pRecord->m_pCurrentRequest);
  842. pRecord->m_pCurrentRequest = pOld;
  843. // Decrement our semaphore
  844. // =======================
  845. dwRes = WaitForSingleObject(pRecord->m_hAttention, 0);
  846. if(dwRes != WAIT_OBJECT_0)
  847. {
  848. // Internal error --- whoever placed the request had
  849. // to have upped the semaphore
  850. // =================================================
  851. ERRORTRACE((LOG_WBEMCORE, "Internal error: queue "
  852. "semaphore is too low\n"));
  853. }
  854. }
  855. cs.Leave();
  856. return dwRes;
  857. }
  858. else
  859. {
  860. // Check why we were awaken
  861. // ========================
  862. if(pRecord->m_bExitNow || pRecord->m_pCurrentRequest == NULL)
  863. {
  864. // Can't exit in the middle of a request. Leave it for later
  865. // =========================================================
  866. pRecord->Signal();
  867. cs.Leave();
  868. DWORD dwLeft2 = CompensateForBug(dwWait,
  869. (GetTickCount() - dwStart));
  870. return WbemWaitForSingleObject(hHandle, dwLeft2);
  871. }
  872. // We've got work to do
  873. // ====================
  874. if(pRecord->m_pCurrentRequest == pOld)
  875. {
  876. // Something is very wrong
  877. // =======================
  878. #ifdef WINMGMT_THREAD_DEBUG
  879. DebugBreak();
  880. #endif
  881. }
  882. }
  883. }
  884. // Execute the request
  885. // ===================
  886. THREADDEBUGTRACE((LOG_WBEMCORE, "Switching to a dependent request %p. "
  887. "Previous was %p\n", pRecord->m_pCurrentRequest, pOld));
  888. cs.Leave();
  889. Execute(pRecord);
  890. pRecord->m_pCurrentRequest = pOld;
  891. THREADDEBUGTRACE((LOG_WBEMCORE, "Going back to request %p\n", pOld));
  892. }
  893. return WAIT_TIMEOUT;
  894. }
  895. DWORD CExecQueue::UnblockedWaitForSingleObject(HANDLE hHandle, DWORD dwWait,
  896. CThreadRecord* pRecord)
  897. {
  898. CCritSecWrapper cs(&m_cs);
  899. // Silently bump the max threads count. We will not allow the queue to reuse
  900. // this thread, so we need to account for this missing thread while we
  901. // are blocked. Essentially, we are hijacking the code that was hijacking
  902. // the thread
  903. cs.Enter();
  904. m_lMaxThreads++;
  905. m_lHiPriMaxThreads++;
  906. cs.Leave();
  907. DWORD dwRet = WbemWaitForSingleObject( hHandle, dwWait );
  908. // The thread is back, so bump down the max threads number. If extra threads were in
  909. // fact created, they should eventually peter out and go away.
  910. cs.Enter();
  911. m_lMaxThreads--;
  912. m_lHiPriMaxThreads--;
  913. cs.Leave();
  914. return dwRet;
  915. }
  916. //******************************************************************************
  917. //
  918. // See dbgalloc.h for documentation
  919. //
  920. //******************************************************************************
  921. // static
  922. DWORD CExecQueue::QueueWaitForSingleObject(HANDLE hHandle, DWORD dwWait)
  923. {
  924. InitTls();
  925. // Get the queue that is registered for this thread, if any
  926. // ========================================================
  927. CThreadRecord* pRecord = (CThreadRecord*)TlsGetValue(mstatic_dwTlsIndex);
  928. if(pRecord == NULL)
  929. {
  930. // No queue is registered with this thread. Just wait
  931. // ==================================================
  932. return WbemWaitForSingleObject(hHandle, dwWait);
  933. }
  934. CExecQueue* pQueue = pRecord->m_pQueue;
  935. return pQueue->WaitForSingleObjectWhileBusy(hHandle, dwWait, pRecord);
  936. }
  937. // static
  938. DWORD CExecQueue::QueueUnblockedWaitForSingleObject(HANDLE hHandle, DWORD dwWait)
  939. {
  940. InitTls();
  941. // Get the queue that is registered for this thread, if any
  942. // ========================================================
  943. CThreadRecord* pRecord = (CThreadRecord*)TlsGetValue(mstatic_dwTlsIndex);
  944. if(pRecord == NULL)
  945. {
  946. // No queue is registered with this thread. Just wait
  947. // ==================================================
  948. return WbemWaitForSingleObject(hHandle, dwWait);
  949. }
  950. CExecQueue* pQueue = pRecord->m_pQueue;
  951. return pQueue->UnblockedWaitForSingleObject(hHandle, dwWait, pRecord);
  952. }
  953. void CExecQueue::SetThreadLimits(long lMaxThreads, long lHiPriMaxThreads,
  954. long lHiPriBound)
  955. {
  956. m_lMaxThreads = lMaxThreads;
  957. if(lHiPriMaxThreads == -1)
  958. m_lHiPriMaxThreads = lMaxThreads * 1.1;
  959. else
  960. m_lHiPriMaxThreads = lHiPriMaxThreads;
  961. m_lHiPriBound = lHiPriBound;
  962. while(DoesNeedNewThread(NULL))
  963. CreateNewThread();
  964. }
  965. BOOL CExecQueue::IsAppropriateThread()
  966. {
  967. // Get the queue that is registered for this thread, if any
  968. // ========================================================
  969. CThreadRecord* pRecord = (CThreadRecord*)TlsGetValue(mstatic_dwTlsIndex);
  970. if(pRecord == NULL)
  971. return FALSE;
  972. CExecQueue* pQueue = pRecord->m_pQueue;
  973. if(pQueue != this)
  974. return FALSE;
  975. return TRUE;
  976. }
  977. BOOL CExecQueue::IsSTAThread()
  978. {
  979. // Get the queue that is registered for this thread, if any
  980. // ========================================================
  981. CThreadRecord* pRecord = (CThreadRecord*)TlsGetValue(mstatic_dwTlsIndex);
  982. if(pRecord == NULL) return FALSE;
  983. return pRecord->m_pQueue->IsSTA();
  984. }
  985. void CExecQueue::SetRequestLimits(long lAbsoluteLimitCount,
  986. long lStartSlowdownCount,
  987. long lOneSecondDelayCount)
  988. {
  989. CCritSecWrapper cs(&m_cs);
  990. cs.Enter();
  991. m_lAbsoluteLimitCount = lAbsoluteLimitCount;
  992. m_lStartSlowdownCount = lStartSlowdownCount;
  993. if(m_lStartSlowdownCount < 0)
  994. {
  995. m_lStartSlowdownCount = m_lAbsoluteLimitCount / 2;
  996. }
  997. m_lOneSecondDelayCount = lOneSecondDelayCount;
  998. if(m_lOneSecondDelayCount < 0)
  999. {
  1000. m_lOneSecondDelayCount =
  1001. m_lAbsoluteLimitCount * 0.2 + m_lStartSlowdownCount * 0.8;
  1002. }
  1003. // Calculate coefficients
  1004. // ======================
  1005. m_dblBeta =
  1006. 1000 *
  1007. ((double)m_lAbsoluteLimitCount - (double)m_lOneSecondDelayCount) /
  1008. ((double)m_lStartSlowdownCount - (double)m_lOneSecondDelayCount);
  1009. m_dblAlpha = m_dblBeta *
  1010. ((double)m_lStartSlowdownCount - (double)m_lAbsoluteLimitCount);
  1011. cs.Leave();
  1012. }