windows-nt/Source/XPSP1/NT/admin/wmi/wbem/winmgmt/wbemcomn/execq.h
2020-09-26 16:20:57 +08:00

395 lines
12 KiB
C++

/*++
Copyright (C) 1996-2001 Microsoft Corporation
Module Name:
EXECQ.H
Abstract:
Defines classes related to execution queues.
Classes defined:
CExecRequest An abstract request.
CExecQueue A queue of requests with an associated thread
History:
23-Jul-96 a-raymcc Created.
3/10/97 a-levn Fully documented
9/6/97 a-levn Rewrote for thread pool
--*/
#ifndef __EXECQUEUE__H_
#define __EXECQUEUE__H_
#include "sync.h"
#include "wbemutil.h"
#ifdef __COLLECT_ALLOC_STAT
#include "stackcom.h"
#endif
//******************************************************************************
//******************************************************************************
//
// class CExecRequest
//
// Abstract base class for any schedulable request
//
//******************************************************************************
//
// Execute
//
// Primary method. Executes the request, whatever that means.
//
// Returns:
//
// int: return code. 0 means success, everything else --- failure.
// Exact error codes are request-specific.
//
//******************************************************************************
class POLARITY CExecRequest
{
protected:
#ifdef WINMGMT_THREAD_DEBUG
static CCritSec mstatic_cs;
static CPointerArray<CExecRequest> mstatic_apOut;
#endif
#ifdef __COLLECT_ALLOC_STAT
public:
CStackRecord m_Stack;
protected:
#endif
HANDLE m_hWhenDone;
CExecRequest* m_pNext;
long m_lPriority;
bool m_fOk;
public:
void SetWhenDoneHandle(HANDLE h) {m_hWhenDone = h;}
HANDLE GetWhenDoneHandle() {return m_hWhenDone;}
void SetNext(CExecRequest* pNext) {m_pNext = pNext;}
CExecRequest* GetNext() {return m_pNext;}
void SetPriority(long lPriority) {m_lPriority = lPriority;}
long GetPriority() {return m_lPriority;}
virtual void DumpError(){ DEBUGTRACE((LOG_WBEMCORE,
"No additional info\n"));};
bool IsOk( void ) { return m_fOk; }
public:
CExecRequest();
virtual ~CExecRequest();
virtual HRESULT Execute() = 0;
};
class CDavidsRequest
{
protected:
LPTHREAD_START_ROUTINE m_pfn;
void* m_pParam;
public:
CDavidsRequest(LPTHREAD_START_ROUTINE pFunctionToExecute, void* pParam)
: m_pfn(pFunctionToExecute), m_pParam(pParam)
{}
HRESULT Execute()
{
return (HRESULT)m_pfn(m_pParam);
}
};
//******************************************************************************
//******************************************************************************
//
// class CExecQueue
//
// CExecQueue represents the concept of a queue of requests with an associated
// thread to execute those requests. In a lot of respects, it is similar to
// a message queue. Requests are added to the queue (which is represented by
// an array) and the thread (created by the Run function) picks them up one
// by one and executes them.
//
// The trick is what to do if while processing one request, another one
// is generated and needs to be processed before the first one succeeds. This
// is similar to a SendMessage, but trickier: the thread generating the new
// request may not be the thread attached to the queue!
//
// To overcome this problem, we make all our waits interruptible in the
// following sense. Whenever the thread attached to the queue needs to block
// waiting for something to happen (which is when another thread may post a
// new request and deadlock the system), it uses QueueWaitForSingleObject
// instead. This function will wait for the object that the thread wanted to
// wait for but it will also wake up if a new Critical request is added to
// the queue and process any such request while waiting.
//
// See QueueWaitForSingleObject for details.
//
// Operations of CExecQueue are protected by a critical section, so multiple
// threads can add requests simultaneously.
//
//******************************************************************************
//
// Constructor
//
// Creates and initializes all the synchronization objects, as well as the
// thread local storage required by QueueWaitForSingleObject.
//
//******************************************************************************
//
// Destructor
//
// Deletes synchronization objects.
//
//******************************************************************************
//
// virtual Enqueue
//
// Adds a request to the queue. The acction depends on whether the request is
// critical or not. If not, it is added to the queue and the semaphor of
// non-critical requests is incremented. The processing thread will pick it up
// in FIFO order. If critical, request is added to the front of the queye and
// the semaphor of critical requests is incremented. This will cause the
// processing thread to take this request the next time it enters into a
// waiting state (see QueueWaitForSingleObject).
//
//******************************************************************************
//
// QueueWaitForSingleObject
//
// The core of the trick. In WINMGMT, whenever a thread needs to wait for an
// object, it calls this function instead. This function checks if the calling
// thread is the registered processing thread for any CExecQueue object (by
// looking up the m_dwTlsIndex thread local variable for the thread). If it
// is not, the function simply calls WaitForSingleObject.
//
// If it is, the function queries the queue for the semaphore indicating the
// number of critical requests on the queue. It then calls
// WaitForMultipleObjects with the original handle and the semaphore. If the
// semaphore is signaled during the wait (or was singlaled when we came in),
// this function picks up the first requests on the queue and executes it;
// once that request is complete, it resumes the wait (with adjusted timeout).
//
// Parameters:
//
// HANDLE hHandle The handle of synchronization object to wait for.
// DWORD dwTimeout Timeout in milliseconds.
//
// Returns:
//
// Same values as WaitForSingleObject:
// WAIT_OBJECT_0 hHandle became signaled
// WAIT_TIMEOUT Timed out.
//
//******************************************************************************
//**************************** protected ***************************************
//
// Register
//
// Registers the calling thread as the processing thread of this queue by
// storing the pointer to the queue in the m_dwTlsIndex thread local storage
// variable. QueueWaitForSingleObject reads this index to interrupt waits
// when needed (see QueueWaitForSingleObject).
//
// Returns:
//
// CExecQueue*: the previous CExecQueue this thread was registered for,
// or NULL if none. The caller MUST not delete this object.
//
//******************************************************************************
//
// ThreadMain
//
// This is the function that the thread created by Run executes. It sits in
// an infinite loop, retrieving requests and executing them one by one.
// This function never returns.
//
//******************************************************************************
//
// Dequeue
//
// Retrieves the request at the head of the queue and removes it from the
// queue.
//
// Returns:
//
// CExecRequest*: the request that was at the head of the queue, or NULL
// if the queue was empty. The caller must delete this
// object when no longer needed.
//
//******************************************************************************
//
// static _ThreadEntry
//
// Stub function used to create the tread. Calls ThreadEntry on the real
// CExecQueue.
//
// Parameters:
//
// LPVOID pObj Actually CExecQueue* to the queue this thread is
// supposed to serve.
//
// Returns:
//
// never.
//
//******************************************************************************
//
// static InitTls
//
// Invoked only once during the life of the system (not the life of a queue),
// creates a thread local storage location where the pointer to the queue is
// stored for the attached threads (see Register and QueueWaitForSingleObject)
//
//******************************************************************************
//
// GetNormalReadyHandle
//
// Returns the handle to the semaphore which contains the number of
// non-critical requests currently on the queue.
//
// Returns:
//
// HANDLE: the the semaphore
//
//******************************************************************************
//
// GetCriticalReadyHandle
//
// Returns the handle to the semaphore which contains the number of
// critical requests currently on the queue.
//
// Returns:
//
// HANDLE: the the semaphore
//
//******************************************************************************
//
// Execute
//
// Dequeues and executes a single request.
//
//******************************************************************************
class POLARITY CExecQueue
{
protected:
class CThreadRecord
{
public:
CExecQueue* m_pQueue;
CExecRequest* m_pCurrentRequest;
BOOL m_bReady;
BOOL m_bExitNow;
HANDLE m_hThread;
HANDLE m_hAttention;
public:
CThreadRecord(CExecQueue* pQueue);
~CThreadRecord();
void Signal();
};
protected:
static long mstatic_lNumInits;
long m_lRef;
CRITICAL_SECTION m_cs;
CFlexArray m_aThreads;
CExecRequest* m_pHead;
CExecRequest* m_pTail;
long m_lNumThreads;
long m_lNumIdle;
long m_lNumRequests;
long m_lMaxThreads;
long m_lHiPriBound;
long m_lHiPriMaxThreads;
long m_lStartSlowdownCount;
long m_lAbsoluteLimitCount;
long m_lOneSecondDelayCount;
double m_dblAlpha;
double m_dblBeta;
DWORD m_dwTimeout;
DWORD m_dwOverflowTimeout;
protected:
virtual void ThreadMain(CThreadRecord* pRecord);
virtual void LogError(CExecRequest* pRequest, int nRes);
static DWORD WINAPI _ThreadEntry(LPVOID pObj);
static void InitTls();
virtual void InitializeThread();
virtual void UninitializeThread();
virtual BOOL CreateNewThread();
static void Register(CThreadRecord* pRecord);
virtual void ShutdownThread(CThreadRecord* pRecord);
virtual BOOL IsSuitableThread(CThreadRecord* pRecord, CExecRequest* pReq);
virtual BOOL DoesNeedNewThread(CExecRequest* pReq);
virtual BOOL IsIdleTooLong(CThreadRecord* pRecord, DWORD dwIdle);
virtual DWORD GetIdleTimeout(CThreadRecord* pRecord);
virtual BOOL IsAppropriateThread();
virtual DWORD WaitForSingleObjectWhileBusy(HANDLE hHandle, DWORD dwWait,
CThreadRecord* pRecord);
virtual DWORD UnblockedWaitForSingleObject(HANDLE hHandle, DWORD dwWait,
CThreadRecord* pRecord);
virtual BOOL Execute(CThreadRecord* pRecord);
virtual BOOL IsSTA() {return FALSE;}
virtual CExecRequest* SearchForSuitableRequest(CThreadRecord* pRecord);
virtual void SitOutPenalty(long lRequestIndex);
virtual DWORD CalcSitOutPenalty(long lRequestIndex);
virtual void AdjustInitialPriority(CExecRequest* pRequest){}
virtual void AdjustPriorityForPassing(CExecRequest* pRequest){}
public:
CExecQueue();
~CExecQueue();
void AddRef() {InterlockedIncrement(&m_lRef);}
void Release() {if(InterlockedDecrement(&m_lRef) == 0) delete this;}
static DWORD GetTlsIndex();
void Enter();
void Leave();
virtual HRESULT Enqueue(CExecRequest* pRequest, HANDLE* phWhenDone = NULL);
HRESULT EnqueueWithoutSleep(CExecRequest* pRequest, HANDLE* phWhenDone = NULL );
HRESULT EnqueueAndWait(CExecRequest* pRequest);
virtual LPCWSTR GetType() {return L"";}
void SetThreadLimits(long lMaxThreads, long lHiPriMaxThreads = -1,
long lHiPriBound = 0);
void SetIdleTimeout(DWORD dwTimeout) {m_dwTimeout = dwTimeout;}
void SetOverflowIdleTimeout(DWORD dwTimeout)
{m_dwOverflowTimeout = dwTimeout;}
void SetRequestLimits(long lAbsoluteLimitCount,
long lStartSlowdownCount = -1, long lOneSecondDelayCount = -1);
void Shutdown();
DWORD GetSitoutPenalty( void ) { return CalcSitOutPenalty( m_lNumRequests ); }
static DWORD QueueWaitForSingleObject(HANDLE hHandle, DWORD dwWait);
static DWORD QueueUnblockedWaitForSingleObject(HANDLE hHandle, DWORD dwWait);
static BOOL IsSTAThread();
};
#endif