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
1179 lines
31 KiB
/*++
|
|
|
|
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 <stdio.h>
|
|
#include <wbemcomn.h>
|
|
#include <execq.h>
|
|
#include <cominit.h>
|
|
#include <sync.h>
|
|
#include "genutils.h"
|
|
|
|
#define IDLE_THREAD_TIMEOUT 12000
|
|
#define OVERLFLOW_TIMEOUT 5000
|
|
|
|
|
|
//***************************************************************************
|
|
|
|
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> 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_hThread(NULL)
|
|
{
|
|
m_hAttention = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (NULL == m_hAttention) throw CX_MemoryException();
|
|
}
|
|
|
|
CExecQueue::CThreadRecord::~CThreadRecord()
|
|
{
|
|
CloseHandle(m_hAttention);
|
|
if (m_hThread) 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),
|
|
m_bShutDonwCalled(FALSE)
|
|
{
|
|
InitTls();
|
|
SetRequestLimits(4000);
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// See execq.h for documentation
|
|
//
|
|
//******************************************************************************
|
|
CExecQueue::~CExecQueue()
|
|
{
|
|
Shutdown();
|
|
}
|
|
|
|
void CExecQueue::Shutdown()
|
|
{
|
|
CCritSecWrapper cs(&m_cs);
|
|
|
|
// Get all member thread handles
|
|
// =============================
|
|
|
|
if (m_bShutDonwCalled) return;
|
|
cs.Enter();
|
|
if (m_bShutDonwCalled) return;
|
|
m_bShutDonwCalled = TRUE;
|
|
|
|
int nNumHandles = m_aThreads.Size();
|
|
int i, j=0;
|
|
HANDLE* ah = NULL;
|
|
if (nNumHandles)
|
|
{
|
|
ah = new HANDLE[nNumHandles];
|
|
DEBUGTRACE((LOG_WBEMCORE, "Queue is shutting down!\n"));
|
|
|
|
for(i = 0; i < nNumHandles; i++)
|
|
{
|
|
CThreadRecord* pRecord = (CThreadRecord*)m_aThreads[i];
|
|
|
|
if ( pRecord->m_hThread && ah)
|
|
{
|
|
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
|
|
// ==================================
|
|
|
|
for( i=0; i < j && ah; i++ )
|
|
{
|
|
DWORD dwRet = WaitForSingleObject( ah[i], INFINITE );
|
|
_DBG_ASSERT( dwRet != WAIT_FAILED );
|
|
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()
|
|
{
|
|
m_cs.Enter();
|
|
}
|
|
|
|
void CExecQueue::Leave()
|
|
{
|
|
m_cs.Leave();
|
|
}
|
|
|
|
//******************************************************************************
|
|
//
|
|
// 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)
|
|
{
|
|
if (m_bShutDonwCalled) return WBEM_E_FAILED;
|
|
CCritSecWrapper cs(&m_cs);
|
|
if (m_bShutDonwCalled) return WBEM_E_FAILED;
|
|
|
|
// 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;
|
|
}
|
|
|
|
// Create an event handle to signal when request is finished, if required
|
|
// ======================================================================
|
|
if(phWhenDone)
|
|
{
|
|
*phWhenDone = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (NULL == *phWhenDone) return WBEM_E_OUT_OF_MEMORY;
|
|
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))
|
|
{
|
|
pRecord->m_pCurrentRequest = pRequest;
|
|
pRecord->m_bReady = FALSE;
|
|
pRecord->Signal();
|
|
m_lNumIdle--;
|
|
|
|
// Done!
|
|
// =====
|
|
|
|
cs.Leave();
|
|
return WBEM_S_NO_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
if (m_bShutDonwCalled) return WBEM_E_FAILED;
|
|
CCritSecWrapper cs(&m_cs);
|
|
if (m_bShutDonwCalled) return WBEM_E_FAILED;
|
|
|
|
// 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;
|
|
}
|
|
|
|
// Create an event handle to signal when request is finished, if required
|
|
// ======================================================================
|
|
|
|
if(phWhenDone)
|
|
{
|
|
*phWhenDone = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (NULL == *phWhenDone) return WBEM_E_OUT_OF_MEMORY;
|
|
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))
|
|
{
|
|
pRecord->m_pCurrentRequest = pRequest;
|
|
pRecord->m_bReady = FALSE;
|
|
pRecord->Signal();
|
|
m_lNumIdle--;
|
|
|
|
// Done!
|
|
// =====
|
|
|
|
cs.Leave();
|
|
return WBEM_S_NO_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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();
|
|
|
|
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 -1 );
|
|
|
|
// 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);
|
|
CCloseMe cmWhenDone( hWhenDone );
|
|
|
|
if ( FAILED(hr) ) return hr;
|
|
|
|
DWORD dwRes = WbemWaitForSingleObject(hWhenDone, INFINITE);
|
|
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;
|
|
|
|
HRESULT hres = pReq->Execute();
|
|
|
|
|
|
if(hres == RPC_E_RETRY)
|
|
{
|
|
// The request has been postponed
|
|
// ==============================
|
|
|
|
DEBUGTRACE((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();
|
|
}
|
|
|
|
HRESULT CExecQueue::InitializeThread()
|
|
{
|
|
return CoInitializeEx(0,COINIT_MULTITHREADED|COINIT_DISABLE_OLE1DDE);
|
|
}
|
|
|
|
void CExecQueue::UninitializeThread()
|
|
{
|
|
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);
|
|
|
|
if (FAILED(InitializeThread())) return;
|
|
|
|
// 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
|
|
// ==========================
|
|
|
|
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)
|
|
{
|
|
ShutdownThread(pRecord);
|
|
cs.Leave();
|
|
return;
|
|
}
|
|
pRecord->m_pQueue->Enqueue(pRecord->m_pCurrentRequest);
|
|
pRecord->m_pCurrentRequest = NULL;
|
|
}
|
|
|
|
// Timeout. See if it is time to quit
|
|
// ==================================
|
|
|
|
|
|
pRecord->m_bReady = FALSE;
|
|
if(IsIdleTooLong(pRecord, dwTimeout))
|
|
{
|
|
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)
|
|
{
|
|
ShutdownThread(pRecord);
|
|
cs.Leave();
|
|
return;
|
|
}
|
|
|
|
// We have a request. Enqueue already adjusted lNumIdle and
|
|
// our m_bReady;
|
|
}
|
|
}
|
|
|
|
// Execute the request
|
|
// ===================
|
|
cs.Leave();
|
|
Execute(pRecord);
|
|
|
|
}
|
|
}
|
|
|
|
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()
|
|
{
|
|
BOOL bRet = FALSE;
|
|
try
|
|
{
|
|
CInCritSec ics(&m_cs);
|
|
|
|
// Create new thread record
|
|
// ========================
|
|
|
|
wmilib::auto_ptr<CThreadRecord> pNewRecord( new CThreadRecord(this));
|
|
if (NULL == pNewRecord.get()) return FALSE;
|
|
|
|
if (CFlexArray::no_error != m_aThreads.Add(pNewRecord.get())) return FALSE;
|
|
|
|
DWORD dwId;
|
|
pNewRecord->m_hThread = CreateThread(0, 0, _ThreadEntry, pNewRecord.get(), 0,&dwId);
|
|
|
|
if( NULL == pNewRecord->m_hThread )
|
|
{
|
|
m_aThreads.RemoveAt(m_aThreads.Size()-1);
|
|
return FALSE;
|
|
}
|
|
|
|
pNewRecord.release(); // array took ownership
|
|
m_lNumThreads++;
|
|
bRet = TRUE;
|
|
}
|
|
catch (CX_Exception &)
|
|
{
|
|
bRet = FALSE;
|
|
}
|
|
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)
|
|
{
|
|
pRecord->m_pCurrentRequest = pCurrent;
|
|
|
|
if(pRecord->m_pCurrentRequest == pOld)
|
|
{
|
|
// Something is very wrong
|
|
// =======================
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No work in the queue. Wait
|
|
// ==========================
|
|
|
|
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();
|
|
|
|
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
|
|
// ========================
|
|
|
|
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
|
|
// =======================
|
|
}
|
|
}
|
|
}
|
|
|
|
// Execute the request
|
|
// ===================
|
|
|
|
cs.Leave();
|
|
Execute(pRecord);
|
|
pRecord->m_pCurrentRequest = 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();
|
|
}
|