/*++ Copyright (C) 1996-2001 Microsoft Corporation Module Name: EXECQ.CPP Abstract: Implements classes related to abstract execution queues. Classes implemeted: CExecRequest An abstract request. CExecQueue A queue of requests with an associated thread History: 23-Jul-96 raymcc Created. 3/10/97 levn Fully documented (heh, heh) 14-Aug-99 raymcc Changed timeouts 30-Oct-99 raymcc Critsec changes for NT Wksta Stress Oct 30 1999 --*/ #include "precomp.h" #include #include #include #include #include #include "genutils.h" #define IDLE_THREAD_TIMEOUT 12000 #define OVERLFLOW_TIMEOUT 5000 //*************************************************************************** // Local wrapper class. Does not initialize or clean up the critsec. Simply // used as a wrapper for scoping so that AV and exception stack unwinding will // cause the critsec to be exited properly once it is entered. class CCritSecWrapper { BOOL m_bIn; CRITICAL_SECTION *m_pcs; public: CCritSecWrapper(CRITICAL_SECTION *pcs) { m_pcs = pcs; m_bIn = FALSE; } ~CCritSecWrapper() { if (m_bIn) Leave(); } void Enter() { EnterCriticalSection(m_pcs); m_bIn = TRUE; } void Leave() { LeaveCriticalSection(m_pcs); m_bIn = FALSE; } }; //*************************************************************************** long CExecQueue::mstatic_lNumInits = -1; POLARITY DWORD mstatic_dwTlsIndex = 0xFFFFFFFF; class CTlsStaticCleanUp { public: CTlsStaticCleanUp() {} ~CTlsStaticCleanUp() { if (mstatic_dwTlsIndex != 0xFFFFFFFF) TlsFree(mstatic_dwTlsIndex); } }; CTlsStaticCleanUp g_tlsStaticCleanup; #ifdef WINMGMT_THREAD_DEBUG CCritSec CExecRequest::mstatic_cs; CPointerArray CExecRequest::mstatic_apOut; #define THREADDEBUGTRACE DEBUGTRACE #else #define THREADDEBUGTRACE(X) #endif CExecRequest::CExecRequest() : m_hWhenDone(NULL), m_pNext(NULL), m_lPriority(0), m_fOk( true ) { #ifdef WINMGMT_THREAD_DEBUG CInCritSec ics(&mstatic_cs); mstatic_apOut.Add(this); #endif } CExecRequest::~CExecRequest() { #ifdef WINMGMT_THREAD_DEBUG CInCritSec ics(&mstatic_cs); for(int i = 0; i < mstatic_apOut.GetSize(); i++) { if(mstatic_apOut[i] == this) { mstatic_apOut.RemoveAt(i); break; } } #endif } DWORD CExecQueue::GetTlsIndex() { return mstatic_dwTlsIndex; } CExecQueue::CThreadRecord::CThreadRecord(CExecQueue* pQueue) : m_pQueue(pQueue), m_pCurrentRequest(NULL), m_bReady(FALSE), m_bExitNow(FALSE) { m_hAttention = CreateEvent(NULL, FALSE, FALSE, NULL); } CExecQueue::CThreadRecord::~CThreadRecord() { CloseHandle(m_hAttention); CloseHandle(m_hThread); } void CExecQueue::CThreadRecord::Signal() { SetEvent(m_hAttention); } //****************************************************************************** // // See execq.h for documentation // //****************************************************************************** CExecQueue::CExecQueue() : m_lNumThreads(0), m_lMaxThreads(1), m_lNumIdle(0), m_lNumRequests(0), m_pHead(NULL), m_pTail(NULL), m_dwTimeout(IDLE_THREAD_TIMEOUT), m_dwOverflowTimeout(OVERLFLOW_TIMEOUT), m_lHiPriBound(-1), m_lHiPriMaxThreads(1), m_lRef(0) { InitTls(); InitializeCriticalSection(&m_cs); SetRequestLimits(4000); } //****************************************************************************** // // See execq.h for documentation // //****************************************************************************** CExecQueue::~CExecQueue() { Shutdown(); DeleteCriticalSection(&m_cs); } void CExecQueue::Shutdown() { CCritSecWrapper cs(&m_cs); // Get all member thread handles // ============================= cs.Enter(); int nNumHandles = m_aThreads.Size(); HANDLE* ah = new HANDLE[nNumHandles]; DEBUGTRACE((LOG_WBEMCORE, "Queue is shutting down!\n")); int i, j=0; for(i = 0; i < nNumHandles; i++) { CThreadRecord* pRecord = (CThreadRecord*)m_aThreads[i]; if ( pRecord->m_hThread ) { ah[j++] = pRecord->m_hThread; } // Inform the thread it should go away when ready // ============================================== pRecord->m_bExitNow = TRUE; // Wake it up if necessary // ======================= pRecord->Signal(); } cs.Leave(); // Make sure all our threads are gone // ================================== if(j > 0) { DWORD dwRet = WaitForMultipleObjects(j, ah, TRUE, INFINITE); _DBG_ASSERT( dwRet != WAIT_FAILED ); } for(i = 0; i < j; i++) CloseHandle(ah[i]); delete [] ah; // Remove all outstanding requests // =============================== while(m_pHead) { CExecRequest* pReq = m_pHead; m_pHead = m_pHead->GetNext(); delete pReq; } } //****************************************************************************** // // See execq.h for documentation // //****************************************************************************** // static void CExecQueue::InitTls() { if(InterlockedIncrement(&mstatic_lNumInits) == 0) { mstatic_dwTlsIndex = TlsAlloc(); } } void CExecQueue::Enter() { EnterCriticalSection(&m_cs); } void CExecQueue::Leave() { LeaveCriticalSection(&m_cs); } //****************************************************************************** // // See dbgalloc.h for documentation // //****************************************************************************** void CExecQueue::Register(CThreadRecord* pRecord) { TlsSetValue(mstatic_dwTlsIndex, (void*)pRecord); } BOOL CExecQueue::IsSuitableThread(CThreadRecord* pRecord, CExecRequest* pReq) { if(pRecord->m_pCurrentRequest == NULL) return TRUE; // This thread is in the middle of something. By default, ignore it // ================================================================ return FALSE; } //****************************************************************************** // // See dbgalloc.h for documentation // //****************************************************************************** HRESULT CExecQueue::Enqueue(CExecRequest* pRequest, HANDLE* phWhenDone) { CCritSecWrapper cs(&m_cs); // Check if the request has a problem with it. If so, return the // appropriate error code. if ( !pRequest->IsOk() ) { return WBEM_E_OUT_OF_MEMORY; } #ifdef __COLLECT_ALLOC_STAT pRequest->m_Stack.Create(0, FALSE); #endif // Create an event handle to signal when request is finished, if required // ====================================================================== if(phWhenDone) { *phWhenDone = CreateEvent(NULL, FALSE, FALSE, NULL); pRequest->SetWhenDoneHandle(*phWhenDone); } cs.Enter(); // Search for a suitable thread // ============================ for(int i = 0; i < m_aThreads.Size(); i++) { CThreadRecord* pRecord = (CThreadRecord*)m_aThreads[i]; if(pRecord->m_bReady) { // Free. Check if suitable // ======================= if(IsSuitableThread(pRecord, pRequest)) { THREADDEBUGTRACE((LOG_WBEMCORE, "Enqueueing request %p onto a " "thread record %p. Previous request: %p\n", pRequest, pRecord, pRecord->m_pCurrentRequest)); pRecord->m_pCurrentRequest = pRequest; THREADDEBUGTRACE((LOG_WBEMCORE, "In Enqueue, changing m_bReady " "for thread %p to 0. Was: %d\n", pRecord, pRecord->m_bReady)); pRecord->m_bReady = FALSE; pRecord->Signal(); m_lNumIdle--; // Done! // ===== cs.Leave(); return WBEM_S_NO_ERROR; } } } THREADDEBUGTRACE((LOG_WBEMCORE, "Enqueueing request %p onto the queue\n", pRequest)); // No suitable thread found. Add to the queue // ========================================== if(m_lNumRequests >= m_lAbsoluteLimitCount) { cs.Leave(); return WBEM_E_FAILED; } // Search for insert position based on priority // ============================================ AdjustInitialPriority(pRequest); CExecRequest* pCurrent = m_pHead; CExecRequest* pLast = NULL; while(pCurrent && pCurrent->GetPriority() <= pRequest->GetPriority()) { pLast = pCurrent; pCurrent = pCurrent->GetNext(); } // Insert // ====== if(pCurrent) { pRequest->SetNext(pCurrent); } else { m_pTail = pRequest; } if(pLast) { pLast->SetNext(pRequest); } else { m_pHead= pRequest; } m_lNumRequests++; // Adjust priorities of the loosers // ================================ while(pCurrent) { AdjustPriorityForPassing(pCurrent); pCurrent = pCurrent->GetNext(); } // Create a new thread, if required // ================================ if(DoesNeedNewThread(pRequest)) CreateNewThread(); long lIndex = m_lNumRequests; cs.Leave(); // Sit out whatever penalty is imposed // =================================== SitOutPenalty(lIndex); return WBEM_S_NO_ERROR; } //****************************************************************************** // // See dbgalloc.h for documentation // //****************************************************************************** HRESULT CExecQueue::EnqueueWithoutSleep(CExecRequest* pRequest, HANDLE* phWhenDone) { CCritSecWrapper cs(&m_cs); // Check if the request has a problem with it. If so, return the // appropriate error code. if ( !pRequest->IsOk() ) { return WBEM_E_OUT_OF_MEMORY; } #ifdef __COLLECT_ALLOC_STAT pRequest->m_Stack.Create(0, FALSE); #endif // Create an event handle to signal when request is finished, if required // ====================================================================== if(phWhenDone) { *phWhenDone = CreateEvent(NULL, FALSE, FALSE, NULL); pRequest->SetWhenDoneHandle(*phWhenDone); } cs.Enter(); // Search for a suitable thread // ============================ for(int i = 0; i < m_aThreads.Size(); i++) { CThreadRecord* pRecord = (CThreadRecord*)m_aThreads[i]; if(pRecord->m_bReady) { // Free. Check if suitable // ======================= if(IsSuitableThread(pRecord, pRequest)) { THREADDEBUGTRACE((LOG_WBEMCORE, "Enqueueing request %p onto a " "thread record %p. Previous request: %p\n", pRequest, pRecord, pRecord->m_pCurrentRequest)); pRecord->m_pCurrentRequest = pRequest; THREADDEBUGTRACE((LOG_WBEMCORE, "In Enqueue, changing m_bReady " "for thread %p to 0. Was: %d\n", pRecord, pRecord->m_bReady)); pRecord->m_bReady = FALSE; pRecord->Signal(); m_lNumIdle--; // Done! // ===== cs.Leave(); return WBEM_S_NO_ERROR; } } } THREADDEBUGTRACE((LOG_WBEMCORE, "Enqueueing request %p onto the queue\n", pRequest)); // No suitable thread found. Add to the queue // ========================================== if(m_lNumRequests >= m_lAbsoluteLimitCount) { cs.Leave(); return WBEM_E_FAILED; } // Search for insert position based on priority // ============================================ AdjustInitialPriority(pRequest); CExecRequest* pCurrent = m_pHead; CExecRequest* pLast = NULL; while(pCurrent && pCurrent->GetPriority() <= pRequest->GetPriority()) { pLast = pCurrent; pCurrent = pCurrent->GetNext(); } // Insert // ====== if(pCurrent) { pRequest->SetNext(pCurrent); } else { m_pTail = pRequest; } if(pLast) { pLast->SetNext(pRequest); } else { m_pHead= pRequest; } m_lNumRequests++; // Adjust priorities of the loosers // ================================ while(pCurrent) { AdjustPriorityForPassing(pCurrent); pCurrent = pCurrent->GetNext(); } // Create a new thread, if required // ================================ if(DoesNeedNewThread(pRequest)) CreateNewThread(); long lIndex = m_lNumRequests; cs.Leave(); // Sit out whatever penalty is imposed // =================================== // DWORD dwSleep = CalcSitOutPenalty(lIndex); return WBEM_S_NO_ERROR; } DWORD CExecQueue::CalcSitOutPenalty(long lRequestIndex) { if(lRequestIndex <= m_lStartSlowdownCount) return 0; // no penalty if(lRequestIndex >= m_lAbsoluteLimitCount) lRequestIndex = m_lAbsoluteLimitCount; // Calculate the timeout // ===================== double dblTimeout = m_dblAlpha / (m_lAbsoluteLimitCount - lRequestIndex) + m_dblBeta; // Return penalty // =========== return ((DWORD) dblTimeout); } void CExecQueue::SitOutPenalty(long lRequestIndex) { DWORD dwSitOutPenalty = CalcSitOutPenalty( lRequestIndex ); // Sleep on it // =========== if ( 0 != dwSitOutPenalty ) { Sleep( dwSitOutPenalty ); } } HRESULT CExecQueue::EnqueueAndWait(CExecRequest* pRequest) { if(IsAppropriateThread()) { pRequest->Execute(); delete pRequest; return WBEM_S_NO_ERROR; } HANDLE hWhenDone; HRESULT hr = Enqueue(pRequest, &hWhenDone); if ( FAILED(hr) ) { return hr; } DWORD dwRes = WbemWaitForSingleObject(hWhenDone, INFINITE); CloseHandle(hWhenDone); return ( dwRes == WAIT_OBJECT_0 ? WBEM_S_NO_ERROR : WBEM_E_FAILED ); } BOOL CExecQueue::DoesNeedNewThread(CExecRequest* pRequest) { if(m_lNumIdle > 0 || m_lNumRequests == 0) return FALSE; if(m_lNumThreads < m_lMaxThreads) return TRUE; else if(pRequest->GetPriority() <= m_lHiPriBound && m_lNumThreads < m_lHiPriMaxThreads) return TRUE; else return FALSE; } //****************************************************************************** // // See dbgalloc.h for documentation // //****************************************************************************** BOOL CExecQueue::Execute(CThreadRecord* pRecord) { CExecRequest* pReq = pRecord->m_pCurrentRequest; #ifdef __COLLECT_ALLOC_STAT CStackContinuation Cont; Cont.m_pPrevStack = &pReq->m_Stack; CStackContinuation* pPrev = CStackContinuation::Set(&Cont); #endif HRESULT hres = pReq->Execute(); #ifdef __COLLECT_ALLOC_STAT CStackContinuation::Set(pPrev); #endif if(hres == RPC_E_RETRY) { // The request has been postponed // ============================== THREADDEBUGTRACE((LOG_WBEMCORE, "Thread %p postponed request %p\n", pRecord, pReq)); } else { if(hres != WBEM_NO_ERROR) { LogError(pReq, hres); } HANDLE hWhenDone = pReq->GetWhenDoneHandle(); if(hWhenDone != NULL) { SetEvent(hWhenDone); } THREADDEBUGTRACE((LOG_WBEMCORE, "Thread %p done with request %p\n", pRecord, pReq)); delete pReq; } pRecord->m_pCurrentRequest = NULL; return TRUE; } //****************************************************************************** // // See dbgalloc.h for documentation // //****************************************************************************** void CExecQueue::LogError(CExecRequest* pRequest, int nRes) { DEBUGTRACE((LOG_WBEMCORE, "Error %X occured executing queued request\n", nRes)); pRequest->DumpError(); } void CExecQueue::InitializeThread() { InitializeCom(); } void CExecQueue::UninitializeThread() { if(IsDcomEnabled() || IsNT()) CoUninitialize(); } CExecRequest* CExecQueue::SearchForSuitableRequest(CThreadRecord* pRecord) { // Assumes in critical section // =========================== CExecRequest* pCurrent = m_pHead; CExecRequest* pPrev = NULL; while(pCurrent) { if(IsSuitableThread(pRecord, pCurrent)) { // Found one --- take it // ===================== if(pPrev) pPrev->SetNext(pCurrent->GetNext()); else m_pHead = pCurrent->GetNext(); if(pCurrent == m_pTail) m_pTail = pPrev; m_lNumRequests--; break; } pPrev = pCurrent; pCurrent = pCurrent->GetNext(); } return pCurrent; } //****************************************************************************** // // See dbgalloc.h for documentation // //****************************************************************************** void CExecQueue::ThreadMain(CThreadRecord* pRecord) { CCritSecWrapper cs(&m_cs); InitializeThread(); // Register this queue with this thread, so any further wait would be // interruptable // ================================================================== Register(pRecord); while (1) { // Returning from work. At this point, our event is not signaled, // our m_pCurrentRequest is NULL and our m_bReady is FALSE // ==================================================================== // Search for work in the queue // ============================ cs.Enter(); CExecRequest* pCurrent = SearchForSuitableRequest(pRecord); if(pCurrent) { // Found some. Take it // =================== pRecord->m_pCurrentRequest = pCurrent; } else { // No work in the queue. Wait // ========================== THREADDEBUGTRACE((LOG_WBEMCORE, "ThreadMain of %p is setting Ready to " "TRUE. Was: %d\n", pRecord, pRecord->m_bReady)); pRecord->m_bReady = TRUE; m_lNumIdle++; DWORD dwTimeout = GetIdleTimeout(pRecord); cs.Leave(); DWORD dwRes = WbemWaitForSingleObject(pRecord->m_hAttention, dwTimeout); cs.Enter(); if(dwRes != WAIT_OBJECT_0) { // Check if someone managed to place a request in our record // after the timeout. // ========================================================= if(WbemWaitForSingleObject(pRecord->m_hAttention, 0) == WAIT_OBJECT_0) { DEBUGTRACE((LOG_WBEMCORE, "AMAZING: Thread %p received " "request %p after timing out. Returning to the " "queue\n", pRecord, pRecord->m_pCurrentRequest)); if(pRecord->m_bExitNow || pRecord->m_pCurrentRequest == NULL) { THREADDEBUGTRACE((LOG_WBEMCORE, "Thread %p being stopped --- %d " "requests in the queue (head = %p)\n", pRecord, pRecord->m_pQueue->m_lNumRequests, pRecord->m_pQueue->m_pHead)); ShutdownThread(pRecord); cs.Leave(); return; } pRecord->m_pQueue->Enqueue(pRecord->m_pCurrentRequest); pRecord->m_pCurrentRequest = NULL; } // Timeout. See if it is time to quit // ================================== THREADDEBUGTRACE((LOG_WBEMCORE, "ThreadMain of %p is setting Ready " " to FALSE on timeout. Was: %d\n", pRecord, pRecord->m_bReady)); pRecord->m_bReady = FALSE; if(IsIdleTooLong(pRecord, dwTimeout)) { THREADDEBUGTRACE((LOG_WBEMCORE, "Thread %p timing out --- %d " "requests in the queue (head = %p)\n", pRecord, pRecord->m_pQueue->m_lNumRequests, pRecord->m_pQueue->m_pHead)); ShutdownThread(pRecord); cs.Leave(); return; } // Go and wait a little more // ========================= m_lNumIdle--; cs.Leave(); continue; } else { // Check why we were awaken // ======================== if(pRecord->m_bExitNow || pRecord->m_pCurrentRequest == NULL) { THREADDEBUGTRACE((LOG_WBEMCORE, "Thread %p being stopped --- %d " "requests in the queue (head = %p)\n", pRecord, pRecord->m_pQueue->m_lNumRequests, pRecord->m_pQueue->m_pHead)); ShutdownThread(pRecord); cs.Leave(); return; } // We have a request. Enqueue already adjusted lNumIdle and // our m_bReady; } } // Execute the request // =================== #ifdef WINMGMT_THREAD_DEBUG if(pRecord->m_bReady) DebugBreak(); #endif THREADDEBUGTRACE((LOG_WBEMCORE, "Thread %p picking up normal request %p\n", pRecord, pRecord->m_pCurrentRequest)); cs.Leave(); Execute(pRecord); #ifdef WINMGMT_THREAD_DEBUG if(pRecord->m_bReady) DebugBreak(); #endif } } DWORD CExecQueue::GetIdleTimeout(CThreadRecord* pRecord) { if(m_lNumThreads > m_lMaxThreads) return m_dwOverflowTimeout; else return m_dwTimeout; } BOOL CExecQueue::IsIdleTooLong(CThreadRecord* pRecord, DWORD dwTimeout) { if(m_lNumThreads > m_lMaxThreads) return TRUE; else if(dwTimeout < m_dwTimeout) return FALSE; else return TRUE; } void CExecQueue::ShutdownThread(CThreadRecord* pRecord) { CCritSecWrapper cs(&m_cs); cs.Enter(); TlsSetValue(mstatic_dwTlsIndex, NULL); for(int i = 0; i < m_aThreads.Size(); i++) { if(m_aThreads[i] == pRecord) { m_aThreads.RemoveAt(i); // Make sure we don't close the handle if the queue's Shutdown is // waiting on it // ============================================================== if(pRecord->m_bExitNow) pRecord->m_hThread = NULL; delete pRecord; m_lNumIdle--; m_lNumThreads--; break; } } UninitializeThread(); cs.Leave(); } //****************************************************************************** // // See dbgalloc.h for documentation // //****************************************************************************** // static DWORD WINAPI CExecQueue::_ThreadEntry(LPVOID pObj) { CThreadRecord* pRecord = (CThreadRecord*)pObj; pRecord->m_pQueue->ThreadMain(pRecord); return 0; } //****************************************************************************** // // See dbgalloc.h for documentation // //****************************************************************************** BOOL CExecQueue::CreateNewThread() { CCritSecWrapper cs(&m_cs); BOOL bRet; cs.Enter(); // Create new thread record // ======================== CThreadRecord* pNewRecord = new CThreadRecord(this); if (pNewRecord) { m_aThreads.Add(pNewRecord); DWORD dwId; pNewRecord->m_hThread = CreateThread(0, 0, _ThreadEntry, pNewRecord, 0, &dwId); if(pNewRecord->m_hThread == NULL) { m_aThreads.RemoveAt(m_aThreads.Size()-1); delete pNewRecord; bRet = FALSE; } else { m_lNumThreads++; bRet = TRUE; } } else bRet = FALSE; cs.Leave(); return bRet; } DWORD CompensateForBug(DWORD dwOriginal, DWORD dwElapsed) { if(dwOriginal == 0xFFFFFFFF) return 0xFFFFFFFF; DWORD dwLeft = dwOriginal - dwElapsed; if(dwLeft > 0x7FFFFFFF) dwLeft = 0x7FFFFFFF; return dwLeft; } DWORD CExecQueue::WaitForSingleObjectWhileBusy(HANDLE hHandle, DWORD dwWait, CThreadRecord* pRecord) { CCritSecWrapper cs(&m_cs); CExecRequest* pOld = pRecord->m_pCurrentRequest; DWORD dwStart = GetTickCount(); while (dwWait > GetTickCount() - dwStart) { // Search for work in the queue // ============================ cs.Enter(); CExecRequest* pCurrent = SearchForSuitableRequest(pRecord); if(pCurrent != NULL) { THREADDEBUGTRACE((LOG_WBEMCORE, "QUEUE: While busy, found work in the " "queue: thread %p, request %p, old request %p\n", pRecord, pCurrent, pRecord->m_pCurrentRequest)); pRecord->m_pCurrentRequest = pCurrent; if(pRecord->m_pCurrentRequest == pOld) { // Something is very wrong // ======================= #ifdef WINMGMT_THREAD_DEBUG DebugBreak(); #endif } } else { // No work in the queue. Wait // ========================== THREADDEBUGTRACE((LOG_WBEMCORE, "While Busy thread %p is setting Ready " "to TRUE. Was: %d\n", pRecord, pRecord->m_bReady)); pRecord->m_bReady = TRUE; // Block until a request comes through. // ==================================== HANDLE ahSems[2]; ahSems[0] = hHandle; ahSems[1] = pRecord->m_hAttention; cs.Leave(); DWORD dwLeft = CompensateForBug(dwWait, (GetTickCount() - dwStart)); DWORD dwRes = WbemWaitForMultipleObjects(2, ahSems, dwLeft); cs.Enter(); THREADDEBUGTRACE((LOG_WBEMCORE, "While Busy thread %p is setting Ready " "to FALSE. Was: %d\n", pRecord, pRecord->m_bReady)); pRecord->m_bReady = FALSE; if(dwRes != WAIT_OBJECT_0 + 1) { // Either our target handle is ready or we timed out // ================================================= // Check if anyone placed a request in our record // ============================================== if(pRecord->m_pCurrentRequest != pOld) { // Re-issue it to the queue // ======================== DEBUGTRACE((LOG_WBEMCORE, "SURPRIZE: Somebody placed " "request %p into thread record %p while it was getting " "ready to continue. Reissuing\n", pRecord->m_pCurrentRequest, pRecord)); pRecord->m_pQueue->Enqueue(pRecord->m_pCurrentRequest); pRecord->m_pCurrentRequest = pOld; // Decrement our semaphore // ======================= dwRes = WaitForSingleObject(pRecord->m_hAttention, 0); if(dwRes != WAIT_OBJECT_0) { // Internal error --- whoever placed the request had // to have upped the semaphore // ================================================= ERRORTRACE((LOG_WBEMCORE, "Internal error: queue " "semaphore is too low\n")); } } cs.Leave(); return dwRes; } else { // Check why we were awaken // ======================== if(pRecord->m_bExitNow || pRecord->m_pCurrentRequest == NULL) { // Can't exit in the middle of a request. Leave it for later // ========================================================= pRecord->Signal(); cs.Leave(); DWORD dwLeft2 = CompensateForBug(dwWait, (GetTickCount() - dwStart)); return WbemWaitForSingleObject(hHandle, dwLeft2); } // We've got work to do // ==================== if(pRecord->m_pCurrentRequest == pOld) { // Something is very wrong // ======================= #ifdef WINMGMT_THREAD_DEBUG DebugBreak(); #endif } } } // Execute the request // =================== THREADDEBUGTRACE((LOG_WBEMCORE, "Switching to a dependent request %p. " "Previous was %p\n", pRecord->m_pCurrentRequest, pOld)); cs.Leave(); Execute(pRecord); pRecord->m_pCurrentRequest = pOld; THREADDEBUGTRACE((LOG_WBEMCORE, "Going back to request %p\n", pOld)); } return WAIT_TIMEOUT; } DWORD CExecQueue::UnblockedWaitForSingleObject(HANDLE hHandle, DWORD dwWait, CThreadRecord* pRecord) { CCritSecWrapper cs(&m_cs); // Silently bump the max threads count. We will not allow the queue to reuse // this thread, so we need to account for this missing thread while we // are blocked. Essentially, we are hijacking the code that was hijacking // the thread cs.Enter(); m_lMaxThreads++; m_lHiPriMaxThreads++; cs.Leave(); DWORD dwRet = WbemWaitForSingleObject( hHandle, dwWait ); // The thread is back, so bump down the max threads number. If extra threads were in // fact created, they should eventually peter out and go away. cs.Enter(); m_lMaxThreads--; m_lHiPriMaxThreads--; cs.Leave(); return dwRet; } //****************************************************************************** // // See dbgalloc.h for documentation // //****************************************************************************** // static DWORD CExecQueue::QueueWaitForSingleObject(HANDLE hHandle, DWORD dwWait) { InitTls(); // Get the queue that is registered for this thread, if any // ======================================================== CThreadRecord* pRecord = (CThreadRecord*)TlsGetValue(mstatic_dwTlsIndex); if(pRecord == NULL) { // No queue is registered with this thread. Just wait // ================================================== return WbemWaitForSingleObject(hHandle, dwWait); } CExecQueue* pQueue = pRecord->m_pQueue; return pQueue->WaitForSingleObjectWhileBusy(hHandle, dwWait, pRecord); } // static DWORD CExecQueue::QueueUnblockedWaitForSingleObject(HANDLE hHandle, DWORD dwWait) { InitTls(); // Get the queue that is registered for this thread, if any // ======================================================== CThreadRecord* pRecord = (CThreadRecord*)TlsGetValue(mstatic_dwTlsIndex); if(pRecord == NULL) { // No queue is registered with this thread. Just wait // ================================================== return WbemWaitForSingleObject(hHandle, dwWait); } CExecQueue* pQueue = pRecord->m_pQueue; return pQueue->UnblockedWaitForSingleObject(hHandle, dwWait, pRecord); } void CExecQueue::SetThreadLimits(long lMaxThreads, long lHiPriMaxThreads, long lHiPriBound) { m_lMaxThreads = lMaxThreads; if(lHiPriMaxThreads == -1) m_lHiPriMaxThreads = lMaxThreads * 1.1; else m_lHiPriMaxThreads = lHiPriMaxThreads; m_lHiPriBound = lHiPriBound; while(DoesNeedNewThread(NULL)) CreateNewThread(); } BOOL CExecQueue::IsAppropriateThread() { // Get the queue that is registered for this thread, if any // ======================================================== CThreadRecord* pRecord = (CThreadRecord*)TlsGetValue(mstatic_dwTlsIndex); if(pRecord == NULL) return FALSE; CExecQueue* pQueue = pRecord->m_pQueue; if(pQueue != this) return FALSE; return TRUE; } BOOL CExecQueue::IsSTAThread() { // Get the queue that is registered for this thread, if any // ======================================================== CThreadRecord* pRecord = (CThreadRecord*)TlsGetValue(mstatic_dwTlsIndex); if(pRecord == NULL) return FALSE; return pRecord->m_pQueue->IsSTA(); } void CExecQueue::SetRequestLimits(long lAbsoluteLimitCount, long lStartSlowdownCount, long lOneSecondDelayCount) { CCritSecWrapper cs(&m_cs); cs.Enter(); m_lAbsoluteLimitCount = lAbsoluteLimitCount; m_lStartSlowdownCount = lStartSlowdownCount; if(m_lStartSlowdownCount < 0) { m_lStartSlowdownCount = m_lAbsoluteLimitCount / 2; } m_lOneSecondDelayCount = lOneSecondDelayCount; if(m_lOneSecondDelayCount < 0) { m_lOneSecondDelayCount = m_lAbsoluteLimitCount * 0.2 + m_lStartSlowdownCount * 0.8; } // Calculate coefficients // ====================== m_dblBeta = 1000 * ((double)m_lAbsoluteLimitCount - (double)m_lOneSecondDelayCount) / ((double)m_lStartSlowdownCount - (double)m_lOneSecondDelayCount); m_dblAlpha = m_dblBeta * ((double)m_lStartSlowdownCount - (double)m_lAbsoluteLimitCount); cs.Leave(); }