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.

1179 lines
31 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. long CExecQueue::mstatic_lNumInits = -1;
  27. POLARITY DWORD mstatic_dwTlsIndex = 0xFFFFFFFF;
  28. class CTlsStaticCleanUp
  29. {
  30. public:
  31. CTlsStaticCleanUp() {}
  32. ~CTlsStaticCleanUp() { if (mstatic_dwTlsIndex != 0xFFFFFFFF) TlsFree(mstatic_dwTlsIndex); }
  33. };
  34. CTlsStaticCleanUp g_tlsStaticCleanup;
  35. #ifdef WINMGMT_THREAD_DEBUG
  36. CCritSec CExecRequest::mstatic_cs;
  37. CPointerArray<CExecRequest> CExecRequest::mstatic_apOut;
  38. #define THREADDEBUGTRACE DEBUGTRACE
  39. #else
  40. #define THREADDEBUGTRACE(X)
  41. #endif
  42. CExecRequest::CExecRequest() : m_hWhenDone(NULL), m_pNext(NULL), m_lPriority(0), m_fOk( true )
  43. {
  44. #ifdef WINMGMT_THREAD_DEBUG
  45. CInCritSec ics(&mstatic_cs);
  46. mstatic_apOut.Add(this);
  47. #endif
  48. }
  49. CExecRequest::~CExecRequest()
  50. {
  51. #ifdef WINMGMT_THREAD_DEBUG
  52. CInCritSec ics(&mstatic_cs);
  53. for(int i = 0; i < mstatic_apOut.GetSize(); i++)
  54. {
  55. if(mstatic_apOut[i] == this)
  56. {
  57. mstatic_apOut.RemoveAt(i);
  58. break;
  59. }
  60. }
  61. #endif
  62. }
  63. DWORD CExecQueue::GetTlsIndex()
  64. {
  65. return mstatic_dwTlsIndex;
  66. }
  67. CExecQueue::CThreadRecord::CThreadRecord(CExecQueue* pQueue)
  68. : m_pQueue(pQueue), m_pCurrentRequest(NULL), m_bReady(FALSE),
  69. m_bExitNow(FALSE),m_hThread(NULL)
  70. {
  71. m_hAttention = CreateEvent(NULL, FALSE, FALSE, NULL);
  72. if (NULL == m_hAttention) throw CX_MemoryException();
  73. }
  74. CExecQueue::CThreadRecord::~CThreadRecord()
  75. {
  76. CloseHandle(m_hAttention);
  77. if (m_hThread) CloseHandle(m_hThread);
  78. }
  79. void CExecQueue::CThreadRecord::Signal()
  80. {
  81. SetEvent(m_hAttention);
  82. }
  83. //******************************************************************************
  84. //
  85. // See execq.h for documentation
  86. //
  87. //******************************************************************************
  88. CExecQueue::CExecQueue() :
  89. m_lNumThreads(0),
  90. m_lMaxThreads(1),
  91. m_lNumIdle(0),
  92. m_lNumRequests(0),
  93. m_pHead(NULL),
  94. m_pTail(NULL),
  95. m_dwTimeout(IDLE_THREAD_TIMEOUT),
  96. m_dwOverflowTimeout(OVERLFLOW_TIMEOUT),
  97. m_lHiPriBound(-1),
  98. m_lHiPriMaxThreads(1),
  99. m_lRef(0),
  100. m_bShutDonwCalled(FALSE)
  101. {
  102. InitTls();
  103. SetRequestLimits(4000);
  104. }
  105. //******************************************************************************
  106. //
  107. // See execq.h for documentation
  108. //
  109. //******************************************************************************
  110. CExecQueue::~CExecQueue()
  111. {
  112. Shutdown();
  113. }
  114. void CExecQueue::Shutdown()
  115. {
  116. CCritSecWrapper cs(&m_cs);
  117. // Get all member thread handles
  118. // =============================
  119. if (m_bShutDonwCalled) return;
  120. cs.Enter();
  121. if (m_bShutDonwCalled) return;
  122. m_bShutDonwCalled = TRUE;
  123. int nNumHandles = m_aThreads.Size();
  124. int i, j=0;
  125. HANDLE* ah = NULL;
  126. if (nNumHandles)
  127. {
  128. ah = new HANDLE[nNumHandles];
  129. DEBUGTRACE((LOG_WBEMCORE, "Queue is shutting down!\n"));
  130. for(i = 0; i < nNumHandles; i++)
  131. {
  132. CThreadRecord* pRecord = (CThreadRecord*)m_aThreads[i];
  133. if ( pRecord->m_hThread && ah)
  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. }
  145. cs.Leave();
  146. // Make sure all our threads are gone
  147. // ==================================
  148. for( i=0; i < j && ah; i++ )
  149. {
  150. DWORD dwRet = WaitForSingleObject( ah[i], INFINITE );
  151. _DBG_ASSERT( dwRet != WAIT_FAILED );
  152. CloseHandle(ah[i]);
  153. }
  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. m_cs.Enter();
  180. }
  181. void CExecQueue::Leave()
  182. {
  183. m_cs.Leave();
  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. if (m_bShutDonwCalled) return WBEM_E_FAILED;
  210. CCritSecWrapper cs(&m_cs);
  211. if (m_bShutDonwCalled) return WBEM_E_FAILED;
  212. // Check if the request has a problem with it. If so, return the
  213. // appropriate error code.
  214. if ( !pRequest->IsOk() )
  215. {
  216. return WBEM_E_OUT_OF_MEMORY;
  217. }
  218. // Create an event handle to signal when request is finished, if required
  219. // ======================================================================
  220. if(phWhenDone)
  221. {
  222. *phWhenDone = CreateEvent(NULL, FALSE, FALSE, NULL);
  223. if (NULL == *phWhenDone) return WBEM_E_OUT_OF_MEMORY;
  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. pRecord->m_pCurrentRequest = pRequest;
  239. pRecord->m_bReady = FALSE;
  240. pRecord->Signal();
  241. m_lNumIdle--;
  242. // Done!
  243. // =====
  244. cs.Leave();
  245. return WBEM_S_NO_ERROR;
  246. }
  247. }
  248. }
  249. // No suitable thread found. Add to the queue
  250. // ==========================================
  251. if(m_lNumRequests >= m_lAbsoluteLimitCount)
  252. {
  253. cs.Leave();
  254. return WBEM_E_FAILED;
  255. }
  256. // Search for insert position based on priority
  257. // ============================================
  258. AdjustInitialPriority(pRequest);
  259. CExecRequest* pCurrent = m_pHead;
  260. CExecRequest* pLast = NULL;
  261. while(pCurrent && pCurrent->GetPriority() <= pRequest->GetPriority())
  262. {
  263. pLast = pCurrent;
  264. pCurrent = pCurrent->GetNext();
  265. }
  266. // Insert
  267. // ======
  268. if(pCurrent)
  269. {
  270. pRequest->SetNext(pCurrent);
  271. }
  272. else
  273. {
  274. m_pTail = pRequest;
  275. }
  276. if(pLast)
  277. {
  278. pLast->SetNext(pRequest);
  279. }
  280. else
  281. {
  282. m_pHead= pRequest;
  283. }
  284. m_lNumRequests++;
  285. // Adjust priorities of the loosers
  286. // ================================
  287. while(pCurrent)
  288. {
  289. AdjustPriorityForPassing(pCurrent);
  290. pCurrent = pCurrent->GetNext();
  291. }
  292. // Create a new thread, if required
  293. // ================================
  294. if(DoesNeedNewThread(pRequest))
  295. CreateNewThread();
  296. long lIndex = m_lNumRequests;
  297. cs.Leave();
  298. // Sit out whatever penalty is imposed
  299. // ===================================
  300. SitOutPenalty(lIndex);
  301. return WBEM_S_NO_ERROR;
  302. }
  303. //******************************************************************************
  304. //
  305. // See dbgalloc.h for documentation
  306. //
  307. //******************************************************************************
  308. HRESULT CExecQueue::EnqueueWithoutSleep(CExecRequest* pRequest, HANDLE* phWhenDone)
  309. {
  310. if (m_bShutDonwCalled) return WBEM_E_FAILED;
  311. CCritSecWrapper cs(&m_cs);
  312. if (m_bShutDonwCalled) return WBEM_E_FAILED;
  313. // Check if the request has a problem with it. If so, return the
  314. // appropriate error code.
  315. if ( !pRequest->IsOk() )
  316. {
  317. return WBEM_E_OUT_OF_MEMORY;
  318. }
  319. // Create an event handle to signal when request is finished, if required
  320. // ======================================================================
  321. if(phWhenDone)
  322. {
  323. *phWhenDone = CreateEvent(NULL, FALSE, FALSE, NULL);
  324. if (NULL == *phWhenDone) return WBEM_E_OUT_OF_MEMORY;
  325. pRequest->SetWhenDoneHandle(*phWhenDone);
  326. }
  327. cs.Enter();
  328. // Search for a suitable thread
  329. // ============================
  330. for(int i = 0; i < m_aThreads.Size(); i++)
  331. {
  332. CThreadRecord* pRecord = (CThreadRecord*)m_aThreads[i];
  333. if(pRecord->m_bReady)
  334. {
  335. // Free. Check if suitable
  336. // =======================
  337. if(IsSuitableThread(pRecord, pRequest))
  338. {
  339. pRecord->m_pCurrentRequest = pRequest;
  340. pRecord->m_bReady = FALSE;
  341. pRecord->Signal();
  342. m_lNumIdle--;
  343. // Done!
  344. // =====
  345. cs.Leave();
  346. return WBEM_S_NO_ERROR;
  347. }
  348. }
  349. }
  350. // No suitable thread found. Add to the queue
  351. // ==========================================
  352. if(m_lNumRequests >= m_lAbsoluteLimitCount)
  353. {
  354. cs.Leave();
  355. return WBEM_E_FAILED;
  356. }
  357. // Search for insert position based on priority
  358. // ============================================
  359. AdjustInitialPriority(pRequest);
  360. CExecRequest* pCurrent = m_pHead;
  361. CExecRequest* pLast = NULL;
  362. while(pCurrent && pCurrent->GetPriority() <= pRequest->GetPriority())
  363. {
  364. pLast = pCurrent;
  365. pCurrent = pCurrent->GetNext();
  366. }
  367. // Insert
  368. // ======
  369. if(pCurrent)
  370. {
  371. pRequest->SetNext(pCurrent);
  372. }
  373. else
  374. {
  375. m_pTail = pRequest;
  376. }
  377. if(pLast)
  378. {
  379. pLast->SetNext(pRequest);
  380. }
  381. else
  382. {
  383. m_pHead= pRequest;
  384. }
  385. m_lNumRequests++;
  386. // Adjust priorities of the loosers
  387. // ================================
  388. while(pCurrent)
  389. {
  390. AdjustPriorityForPassing(pCurrent);
  391. pCurrent = pCurrent->GetNext();
  392. }
  393. // Create a new thread, if required
  394. // ================================
  395. if(DoesNeedNewThread(pRequest))
  396. CreateNewThread();
  397. long lIndex = m_lNumRequests;
  398. cs.Leave();
  399. return WBEM_S_NO_ERROR;
  400. }
  401. DWORD CExecQueue::CalcSitOutPenalty(long lRequestIndex)
  402. {
  403. if(lRequestIndex <= m_lStartSlowdownCount)
  404. return 0; // no penalty
  405. if(lRequestIndex > m_lAbsoluteLimitCount)
  406. lRequestIndex = ( m_lAbsoluteLimitCount -1 );
  407. // Calculate the timeout
  408. // =====================
  409. double dblTimeout =
  410. m_dblAlpha / (m_lAbsoluteLimitCount - lRequestIndex) +
  411. m_dblBeta;
  412. // Return penalty
  413. // ===========
  414. return ((DWORD) dblTimeout);
  415. }
  416. void CExecQueue::SitOutPenalty(long lRequestIndex)
  417. {
  418. DWORD dwSitOutPenalty = CalcSitOutPenalty( lRequestIndex );
  419. // Sleep on it
  420. // ===========
  421. if ( 0 != dwSitOutPenalty )
  422. {
  423. Sleep( dwSitOutPenalty );
  424. }
  425. }
  426. HRESULT CExecQueue::EnqueueAndWait(CExecRequest* pRequest)
  427. {
  428. if(IsAppropriateThread())
  429. {
  430. pRequest->Execute();
  431. delete pRequest;
  432. return WBEM_S_NO_ERROR;
  433. }
  434. HANDLE hWhenDone;
  435. HRESULT hr = Enqueue(pRequest, &hWhenDone);
  436. CCloseMe cmWhenDone( hWhenDone );
  437. if ( FAILED(hr) ) return hr;
  438. DWORD dwRes = WbemWaitForSingleObject(hWhenDone, INFINITE);
  439. return ( dwRes == WAIT_OBJECT_0 ? WBEM_S_NO_ERROR : WBEM_E_FAILED );
  440. }
  441. BOOL CExecQueue::DoesNeedNewThread(CExecRequest* pRequest)
  442. {
  443. if(m_lNumIdle > 0 || m_lNumRequests == 0)
  444. return FALSE;
  445. if(m_lNumThreads < m_lMaxThreads)
  446. return TRUE;
  447. else if(pRequest->GetPriority() <= m_lHiPriBound &&
  448. m_lNumThreads < m_lHiPriMaxThreads)
  449. return TRUE;
  450. else
  451. return FALSE;
  452. }
  453. //******************************************************************************
  454. //
  455. // See dbgalloc.h for documentation
  456. //
  457. //******************************************************************************
  458. BOOL CExecQueue::Execute(CThreadRecord* pRecord)
  459. {
  460. CExecRequest* pReq = pRecord->m_pCurrentRequest;
  461. HRESULT hres = pReq->Execute();
  462. if(hres == RPC_E_RETRY)
  463. {
  464. // The request has been postponed
  465. // ==============================
  466. DEBUGTRACE((LOG_WBEMCORE, "Thread %p postponed request %p\n",
  467. pRecord, pReq));
  468. }
  469. else
  470. {
  471. if(hres != WBEM_NO_ERROR)
  472. {
  473. LogError(pReq, hres);
  474. }
  475. HANDLE hWhenDone = pReq->GetWhenDoneHandle();
  476. if(hWhenDone != NULL)
  477. {
  478. SetEvent(hWhenDone);
  479. }
  480. THREADDEBUGTRACE((LOG_WBEMCORE, "Thread %p done with request %p\n",
  481. pRecord, pReq));
  482. delete pReq;
  483. }
  484. pRecord->m_pCurrentRequest = NULL;
  485. return TRUE;
  486. }
  487. //******************************************************************************
  488. //
  489. // See dbgalloc.h for documentation
  490. //
  491. //******************************************************************************
  492. void CExecQueue::LogError(CExecRequest* pRequest, int nRes)
  493. {
  494. DEBUGTRACE((LOG_WBEMCORE,
  495. "Error %X occured executing queued request\n", nRes));
  496. pRequest->DumpError();
  497. }
  498. HRESULT CExecQueue::InitializeThread()
  499. {
  500. return CoInitializeEx(0,COINIT_MULTITHREADED|COINIT_DISABLE_OLE1DDE);
  501. }
  502. void CExecQueue::UninitializeThread()
  503. {
  504. CoUninitialize();
  505. }
  506. CExecRequest* CExecQueue::SearchForSuitableRequest(CThreadRecord* pRecord)
  507. {
  508. // Assumes in critical section
  509. // ===========================
  510. CExecRequest* pCurrent = m_pHead;
  511. CExecRequest* pPrev = NULL;
  512. while(pCurrent)
  513. {
  514. if(IsSuitableThread(pRecord, pCurrent))
  515. {
  516. // Found one --- take it
  517. // =====================
  518. if(pPrev)
  519. pPrev->SetNext(pCurrent->GetNext());
  520. else
  521. m_pHead = pCurrent->GetNext();
  522. if(pCurrent == m_pTail)
  523. m_pTail = pPrev;
  524. m_lNumRequests--;
  525. break;
  526. }
  527. pPrev = pCurrent;
  528. pCurrent = pCurrent->GetNext();
  529. }
  530. return pCurrent;
  531. }
  532. //******************************************************************************
  533. //
  534. // See dbgalloc.h for documentation
  535. //
  536. //******************************************************************************
  537. void CExecQueue::ThreadMain(CThreadRecord* pRecord)
  538. {
  539. CCritSecWrapper cs(&m_cs);
  540. if (FAILED(InitializeThread())) return;
  541. // Register this queue with this thread, so any further wait would be
  542. // interruptable
  543. // ==================================================================
  544. Register(pRecord);
  545. while (1)
  546. {
  547. // Returning from work. At this point, our event is not signaled,
  548. // our m_pCurrentRequest is NULL and our m_bReady is FALSE
  549. // ====================================================================
  550. // Search for work in the queue
  551. // ============================
  552. cs.Enter();
  553. CExecRequest* pCurrent = SearchForSuitableRequest(pRecord);
  554. if(pCurrent)
  555. {
  556. // Found some. Take it
  557. // ===================
  558. pRecord->m_pCurrentRequest = pCurrent;
  559. }
  560. else
  561. {
  562. // No work in the queue. Wait
  563. // ==========================
  564. pRecord->m_bReady = TRUE;
  565. m_lNumIdle++;
  566. DWORD dwTimeout = GetIdleTimeout(pRecord);
  567. cs.Leave();
  568. DWORD dwRes = WbemWaitForSingleObject(pRecord->m_hAttention,
  569. dwTimeout);
  570. cs.Enter();
  571. if(dwRes != WAIT_OBJECT_0)
  572. {
  573. // Check if someone managed to place a request in our record
  574. // after the timeout.
  575. // =========================================================
  576. if(WbemWaitForSingleObject(pRecord->m_hAttention, 0) ==
  577. WAIT_OBJECT_0)
  578. {
  579. DEBUGTRACE((LOG_WBEMCORE, "AMAZING: Thread %p received "
  580. "request %p after timing out. Returning to the "
  581. "queue\n", pRecord, pRecord->m_pCurrentRequest));
  582. if(pRecord->m_bExitNow || pRecord->m_pCurrentRequest == NULL)
  583. {
  584. ShutdownThread(pRecord);
  585. cs.Leave();
  586. return;
  587. }
  588. pRecord->m_pQueue->Enqueue(pRecord->m_pCurrentRequest);
  589. pRecord->m_pCurrentRequest = NULL;
  590. }
  591. // Timeout. See if it is time to quit
  592. // ==================================
  593. pRecord->m_bReady = FALSE;
  594. if(IsIdleTooLong(pRecord, dwTimeout))
  595. {
  596. ShutdownThread(pRecord);
  597. cs.Leave();
  598. return;
  599. }
  600. // Go and wait a little more
  601. // =========================
  602. m_lNumIdle--;
  603. cs.Leave();
  604. continue;
  605. }
  606. else
  607. {
  608. // Check why we were awaken
  609. // ========================
  610. if(pRecord->m_bExitNow || pRecord->m_pCurrentRequest == NULL)
  611. {
  612. ShutdownThread(pRecord);
  613. cs.Leave();
  614. return;
  615. }
  616. // We have a request. Enqueue already adjusted lNumIdle and
  617. // our m_bReady;
  618. }
  619. }
  620. // Execute the request
  621. // ===================
  622. cs.Leave();
  623. Execute(pRecord);
  624. }
  625. }
  626. DWORD CExecQueue::GetIdleTimeout(CThreadRecord* pRecord)
  627. {
  628. if(m_lNumThreads > m_lMaxThreads)
  629. return m_dwOverflowTimeout;
  630. else
  631. return m_dwTimeout;
  632. }
  633. BOOL CExecQueue::IsIdleTooLong(CThreadRecord* pRecord, DWORD dwTimeout)
  634. {
  635. if(m_lNumThreads > m_lMaxThreads)
  636. return TRUE;
  637. else if(dwTimeout < m_dwTimeout)
  638. return FALSE;
  639. else
  640. return TRUE;
  641. }
  642. void CExecQueue::ShutdownThread(CThreadRecord* pRecord)
  643. {
  644. CCritSecWrapper cs(&m_cs);
  645. cs.Enter();
  646. TlsSetValue(mstatic_dwTlsIndex, NULL);
  647. for(int i = 0; i < m_aThreads.Size(); i++)
  648. {
  649. if(m_aThreads[i] == pRecord)
  650. {
  651. m_aThreads.RemoveAt(i);
  652. // Make sure we don't close the handle if the queue's Shutdown is
  653. // waiting on it
  654. // ==============================================================
  655. if(pRecord->m_bExitNow)
  656. pRecord->m_hThread = NULL;
  657. delete pRecord;
  658. m_lNumIdle--;
  659. m_lNumThreads--;
  660. break;
  661. }
  662. }
  663. UninitializeThread();
  664. cs.Leave();
  665. }
  666. //******************************************************************************
  667. //
  668. // See dbgalloc.h for documentation
  669. //
  670. //******************************************************************************
  671. // static
  672. DWORD WINAPI CExecQueue::_ThreadEntry(LPVOID pObj)
  673. {
  674. CThreadRecord* pRecord = (CThreadRecord*)pObj;
  675. pRecord->m_pQueue->ThreadMain(pRecord);
  676. return 0;
  677. }
  678. //******************************************************************************
  679. //
  680. // See dbgalloc.h for documentation
  681. //
  682. //******************************************************************************
  683. BOOL CExecQueue::CreateNewThread()
  684. {
  685. BOOL bRet = FALSE;
  686. try
  687. {
  688. CInCritSec ics(&m_cs);
  689. // Create new thread record
  690. // ========================
  691. wmilib::auto_ptr<CThreadRecord> pNewRecord( new CThreadRecord(this));
  692. if (NULL == pNewRecord.get()) return FALSE;
  693. if (CFlexArray::no_error != m_aThreads.Add(pNewRecord.get())) return FALSE;
  694. DWORD dwId;
  695. pNewRecord->m_hThread = CreateThread(0, 0, _ThreadEntry, pNewRecord.get(), 0,&dwId);
  696. if( NULL == pNewRecord->m_hThread )
  697. {
  698. m_aThreads.RemoveAt(m_aThreads.Size()-1);
  699. return FALSE;
  700. }
  701. pNewRecord.release(); // array took ownership
  702. m_lNumThreads++;
  703. bRet = TRUE;
  704. }
  705. catch (CX_Exception &)
  706. {
  707. bRet = FALSE;
  708. }
  709. return bRet;
  710. }
  711. DWORD CompensateForBug(DWORD dwOriginal, DWORD dwElapsed)
  712. {
  713. if(dwOriginal == 0xFFFFFFFF)
  714. return 0xFFFFFFFF;
  715. DWORD dwLeft = dwOriginal - dwElapsed;
  716. if(dwLeft > 0x7FFFFFFF)
  717. dwLeft = 0x7FFFFFFF;
  718. return dwLeft;
  719. }
  720. DWORD CExecQueue::WaitForSingleObjectWhileBusy(HANDLE hHandle, DWORD dwWait,
  721. CThreadRecord* pRecord)
  722. {
  723. CCritSecWrapper cs(&m_cs);
  724. CExecRequest* pOld = pRecord->m_pCurrentRequest;
  725. DWORD dwStart = GetTickCount();
  726. while (dwWait > GetTickCount() - dwStart)
  727. {
  728. // Search for work in the queue
  729. // ============================
  730. cs.Enter();
  731. CExecRequest* pCurrent = SearchForSuitableRequest(pRecord);
  732. if(pCurrent != NULL)
  733. {
  734. pRecord->m_pCurrentRequest = pCurrent;
  735. if(pRecord->m_pCurrentRequest == pOld)
  736. {
  737. // Something is very wrong
  738. // =======================
  739. }
  740. }
  741. else
  742. {
  743. // No work in the queue. Wait
  744. // ==========================
  745. pRecord->m_bReady = TRUE;
  746. // Block until a request comes through.
  747. // ====================================
  748. HANDLE ahSems[2];
  749. ahSems[0] = hHandle;
  750. ahSems[1] = pRecord->m_hAttention;
  751. cs.Leave();
  752. DWORD dwLeft = CompensateForBug(dwWait, (GetTickCount() - dwStart));
  753. DWORD dwRes = WbemWaitForMultipleObjects(2, ahSems, dwLeft);
  754. cs.Enter();
  755. pRecord->m_bReady = FALSE;
  756. if(dwRes != WAIT_OBJECT_0 + 1)
  757. {
  758. // Either our target handle is ready or we timed out
  759. // =================================================
  760. // Check if anyone placed a request in our record
  761. // ==============================================
  762. if(pRecord->m_pCurrentRequest != pOld)
  763. {
  764. // Re-issue it to the queue
  765. // ========================
  766. pRecord->m_pQueue->Enqueue(pRecord->m_pCurrentRequest);
  767. pRecord->m_pCurrentRequest = pOld;
  768. // Decrement our semaphore
  769. // =======================
  770. dwRes = WaitForSingleObject(pRecord->m_hAttention, 0);
  771. if(dwRes != WAIT_OBJECT_0)
  772. {
  773. // Internal error --- whoever placed the request had
  774. // to have upped the semaphore
  775. // =================================================
  776. ERRORTRACE((LOG_WBEMCORE, "Internal error: queue "
  777. "semaphore is too low\n"));
  778. }
  779. }
  780. cs.Leave();
  781. return dwRes;
  782. }
  783. else
  784. {
  785. // Check why we were awaken
  786. // ========================
  787. if(pRecord->m_bExitNow || pRecord->m_pCurrentRequest == NULL)
  788. {
  789. // Can't exit in the middle of a request. Leave it for later
  790. // =========================================================
  791. pRecord->Signal();
  792. cs.Leave();
  793. DWORD dwLeft2 = CompensateForBug(dwWait,
  794. (GetTickCount() - dwStart));
  795. return WbemWaitForSingleObject(hHandle, dwLeft2);
  796. }
  797. // We've got work to do
  798. // ====================
  799. if(pRecord->m_pCurrentRequest == pOld)
  800. {
  801. // Something is very wrong
  802. // =======================
  803. }
  804. }
  805. }
  806. // Execute the request
  807. // ===================
  808. cs.Leave();
  809. Execute(pRecord);
  810. pRecord->m_pCurrentRequest = pOld;
  811. }
  812. return WAIT_TIMEOUT;
  813. }
  814. DWORD CExecQueue::UnblockedWaitForSingleObject(HANDLE hHandle, DWORD dwWait,
  815. CThreadRecord* pRecord)
  816. {
  817. CCritSecWrapper cs(&m_cs);
  818. // Silently bump the max threads count. We will not allow the queue to reuse
  819. // this thread, so we need to account for this missing thread while we
  820. // are blocked. Essentially, we are hijacking the code that was hijacking
  821. // the thread
  822. cs.Enter();
  823. m_lMaxThreads++;
  824. m_lHiPriMaxThreads++;
  825. cs.Leave();
  826. DWORD dwRet = WbemWaitForSingleObject( hHandle, dwWait );
  827. // The thread is back, so bump down the max threads number. If extra threads were in
  828. // fact created, they should eventually peter out and go away.
  829. cs.Enter();
  830. m_lMaxThreads--;
  831. m_lHiPriMaxThreads--;
  832. cs.Leave();
  833. return dwRet;
  834. }
  835. //******************************************************************************
  836. //
  837. // See dbgalloc.h for documentation
  838. //
  839. //******************************************************************************
  840. // static
  841. DWORD CExecQueue::QueueWaitForSingleObject(HANDLE hHandle, DWORD dwWait)
  842. {
  843. InitTls();
  844. // Get the queue that is registered for this thread, if any
  845. // ========================================================
  846. CThreadRecord* pRecord = (CThreadRecord*)TlsGetValue(mstatic_dwTlsIndex);
  847. if(pRecord == NULL)
  848. {
  849. // No queue is registered with this thread. Just wait
  850. // ==================================================
  851. return WbemWaitForSingleObject(hHandle, dwWait);
  852. }
  853. CExecQueue* pQueue = pRecord->m_pQueue;
  854. return pQueue->WaitForSingleObjectWhileBusy(hHandle, dwWait, pRecord);
  855. }
  856. // static
  857. DWORD CExecQueue::QueueUnblockedWaitForSingleObject(HANDLE hHandle, DWORD dwWait)
  858. {
  859. InitTls();
  860. // Get the queue that is registered for this thread, if any
  861. // ========================================================
  862. CThreadRecord* pRecord = (CThreadRecord*)TlsGetValue(mstatic_dwTlsIndex);
  863. if(pRecord == NULL)
  864. {
  865. // No queue is registered with this thread. Just wait
  866. // ==================================================
  867. return WbemWaitForSingleObject(hHandle, dwWait);
  868. }
  869. CExecQueue* pQueue = pRecord->m_pQueue;
  870. return pQueue->UnblockedWaitForSingleObject(hHandle, dwWait, pRecord);
  871. }
  872. void CExecQueue::SetThreadLimits(long lMaxThreads, long lHiPriMaxThreads,
  873. long lHiPriBound)
  874. {
  875. m_lMaxThreads = lMaxThreads;
  876. if(lHiPriMaxThreads == -1)
  877. m_lHiPriMaxThreads = lMaxThreads * 1.1;
  878. else
  879. m_lHiPriMaxThreads = lHiPriMaxThreads;
  880. m_lHiPriBound = lHiPriBound;
  881. while(DoesNeedNewThread(NULL))
  882. CreateNewThread();
  883. }
  884. BOOL CExecQueue::IsAppropriateThread()
  885. {
  886. // Get the queue that is registered for this thread, if any
  887. // ========================================================
  888. CThreadRecord* pRecord = (CThreadRecord*)TlsGetValue(mstatic_dwTlsIndex);
  889. if(pRecord == NULL)
  890. return FALSE;
  891. CExecQueue* pQueue = pRecord->m_pQueue;
  892. if(pQueue != this)
  893. return FALSE;
  894. return TRUE;
  895. }
  896. BOOL CExecQueue::IsSTAThread()
  897. {
  898. // Get the queue that is registered for this thread, if any
  899. // ========================================================
  900. CThreadRecord* pRecord = (CThreadRecord*)TlsGetValue(mstatic_dwTlsIndex);
  901. if(pRecord == NULL) return FALSE;
  902. return pRecord->m_pQueue->IsSTA();
  903. }
  904. void CExecQueue::SetRequestLimits(long lAbsoluteLimitCount,
  905. long lStartSlowdownCount,
  906. long lOneSecondDelayCount)
  907. {
  908. CCritSecWrapper cs(&m_cs);
  909. cs.Enter();
  910. m_lAbsoluteLimitCount = lAbsoluteLimitCount;
  911. m_lStartSlowdownCount = lStartSlowdownCount;
  912. if(m_lStartSlowdownCount < 0)
  913. {
  914. m_lStartSlowdownCount = m_lAbsoluteLimitCount / 2;
  915. }
  916. m_lOneSecondDelayCount = lOneSecondDelayCount;
  917. if(m_lOneSecondDelayCount < 0)
  918. {
  919. m_lOneSecondDelayCount =
  920. m_lAbsoluteLimitCount * 0.2 + m_lStartSlowdownCount * 0.8;
  921. }
  922. // Calculate coefficients
  923. // ======================
  924. m_dblBeta =
  925. 1000 *
  926. ((double)m_lAbsoluteLimitCount - (double)m_lOneSecondDelayCount) /
  927. ((double)m_lStartSlowdownCount - (double)m_lOneSecondDelayCount);
  928. m_dblAlpha = m_dblBeta *
  929. ((double)m_lStartSlowdownCount - (double)m_lAbsoluteLimitCount);
  930. cs.Leave();
  931. }