//----------------------------------------------------------------------------- // // // File: asyncq.h // // Description: Header file for CAsyncQueue class, which provides the // underlying implementation of pre-local delivery and pre-categorization // queue. // // Author: Mike Swafford (MikeSwa) // // History: // 7/16/98 - MikeSwa Created // 2/2/99 - MikeSwa Added CAsyncRetryQueue // 2/22/99 - MikeSwa Added CAsyncRetryAdminMsgRefQueue // // Copyright (C) 1998 Microsoft Corporation // //----------------------------------------------------------------------------- #ifndef __ASYNCQ_H__ #define __ASYNCQ_H__ #include #include #include #include #include "statemachinebase.h" _declspec(selectany) BOOL g_fRetryAtFrontOfAsyncQueue = FALSE; // global count of total number of async threads needed _declspec(selectany) DWORD g_cTotalThreadsNeeded = 0; class CAQSvrInst; class CAsyncWorkQueueItem; // sig for state machine #define ASYNC_QUEUE_STATE_MACHINE_SIG 'MSQA' #define ASYNC_QUEUE_SIG 'QnsA' #define ASYNC_RETRY_QUEUE_SIG ' QRA' //Add new template signatures here #define ASYNC_QUEUE_MAILMSG_SIG 'MMIt' #define ASYNC_QUEUE_MSGREF_SIG 'frMt' #define ASYNC_QUEUE_WORK_SIG 'krWt' //---[ CAsyncQueueBase ]------------------------------------------------------- // // // Description: // Base class for CAsyncQueue. This is a separate class for 2 reasons. // The most important reason to to allow access to standard member data // with out knowing the template type of the class (for ATQ completion // functions). The 2nd reason is to make it easier to write a debugger // extension to dump this information. // // This class should only be used as a baseclass for CAsyncQueue... it // is not designed to be used by itself. // Hungarian: // asyncqb, pasyncqb // //----------------------------------------------------------------------------- class CAsyncQueueBase : public CStateMachineBase { protected: DWORD m_dwSignature; DWORD m_dwTemplateSignature; //signature that defines type of PQDATA (for ATQ) DWORD m_cMaxSyncThreads; //max threads that can complete sync DWORD m_cCurrentSyncThreads; //current sync threads DWORD m_cCurrentAsyncThreads; //current number of async threads DWORD m_cItemsPending; //# of items pending in the queue LONG m_cItemsPerATQThread; //max # of items an atq thread will process LONG m_cItemsPerSyncThread; //max # of items a pilfered thread will process DWORD m_cScheduledWorkItems; // Number of items that a thread has been allocated for DWORD m_cCurrentCompletionThreads;//# of threads processing end of queue DWORD m_cTotalAsyncCompletionThreads;//Total # of async completion threads DWORD m_cTotalSyncCompletionThreads; //Total # of sync completion threads DWORD m_cTotalShortCircuitThreads; //Total # of threads that proccess data without queue DWORD m_cCompletionThreadsRequested; //# of threads requested to process queue DWORD m_cPendingAsyncCompletions; //# of async completions that we know about DWORD m_cMaxPendingAsyncCompletions; PVOID m_pvContext; //Context that is passed to completion function PATQ_CONTEXT m_pAtqContext; //ATQ Context for this object SOCKET m_hAtqHandle; //Handle used for atq stuff DWORD m_cThreadsNeeded; // Number of threads we need currently to ideally // process the queue - used for thread management friend VOID AsyncQueueAtqCompletion(PVOID pvContext, DWORD vbBytesWritten, DWORD dwStatus, OVERLAPPED *pOverLapped); inline CAsyncQueueBase(DWORD dwTemplateSignature); // possible states enum { ASYNC_QUEUE_STATUS_NORMAL = 0x00000000, ASYNC_QUEUE_STATUS_PAUSED = 0x00000001, ASYNC_QUEUE_STATUS_FROZEN = 0x00000002, ASYNC_QUEUE_STATUS_FROZENPAUSED = 0x00000003, ASYNC_QUEUE_STATUS_SHUTDOWN = 0x00000004, }; // possible internal queue actions enum { ASYNC_QUEUE_ACTION_KICK = 0x00000000, ASYNC_QUEUE_ACTION_FREEZE = 0x00000001, ASYNC_QUEUE_ACTION_THAW = 0x00000002, ASYNC_QUEUE_ACTION_PAUSE = 0x00000003, ASYNC_QUEUE_ACTION_UNPAUSE = 0x00000004, ASYNC_QUEUE_ACTION_SHUTDOWN = 0x00000005, }; // // Statics used for ATQ stuff. // static DWORD s_cAsyncQueueStaticInitRefCount; static DWORD s_cMaxPerProcATQThreadAdjustment; static DWORD s_cDefaultMaxAsyncThreads; // // Statics uses for debugging thread management // static DWORD s_cThreadCompletion_QueueEmpty; // Completed because the queue was empty static DWORD s_cThreadCompletion_CompletedScheduledItems; // Completed because we processed all items we were scheduled for static DWORD s_cThreadCompletion_UnacceptableThreadCount; // Completed because our queue was consuming more threads than allowed static DWORD s_cThreadCompletion_Timeout; // Completed because the thread was taking too long to process static DWORD s_cThreadCompletion_Failure; // Completed because an item failed static DWORD s_cThreadCompletion_Paused; // Completed because the queue was paused void ThreadPoolInitialize(); void ThreadPoolDeinitialize(); // used for state machine stuff static STATE_TRANSITION s_rgTransitionTable[]; virtual void getTransitionTable(const STATE_TRANSITION** ppTransitionTable, DWORD* pdwNumTransitions); public: DWORD dwGetTotalThreads() { return ( m_cCurrentSyncThreads + m_cCurrentAsyncThreads + m_cCompletionThreadsRequested); } // // Start point for worker threads // virtual void StartThreadCompletionRoutine(BOOL fSync) = 0; }; //---[ CAsyncQueue ]----------------------------------------------------------- // // // Description: // FIFO queue that allows thread-throttling and async completion. // Inherits from CAsyncQueueBase. // Hungarian: // asyncq, pasyncq // //----------------------------------------------------------------------------- template class CAsyncQueue : public CAsyncQueueBase { public: typedef BOOL (*QCOMPFN)(PQDATA pqdItem, PVOID pvContext); //function type for Queue completion CAsyncQueue(); ~CAsyncQueue(); HRESULT HrInitialize( DWORD cMaxSyncThreads, DWORD cItemsPerATQThread, DWORD cItemsPerSyncThread, PVOID pvContext, QCOMPFN pfnQueueCompletion, QCOMPFN pfnFailedItem, typename CFifoQueue::MAPFNAPI pfnQueueFailure, DWORD cMaxPendingAsyncCompletions = 0); HRESULT HrDeinitialize(typename CFifoQueue::MAPFNAPI pfnQueueShutdown, CAQSvrInst *paqinst); HRESULT HrQueueRequest(PQDATA pqdata, BOOL fRetry = FALSE); //Queue request for processing void StartThreadCompletionRoutine(BOOL fSync); //Start point for worker threads void RequestCompletionThreadIfNeeded(); BOOL fThreadNeededAndMarkWorkPending(BOOL fSync); virtual BOOL fHandleCompletionFailure(PQDATA pqdata); void StartRetry() {UnpauseQueue();RequestCompletionThreadIfNeeded();}; virtual HRESULT HrMapFn(typename CFifoQueue::MAPFNAPI pfnQueueFn, PVOID pvContext); DWORD cGetItemsPending() {return m_cItemsPending;}; // // "Pause" API - This is used to throttle async completions // void PauseQueue() { dwGetNextState(ASYNC_QUEUE_ACTION_PAUSE); UpdateThreadsNeeded();}; void UnpauseQueue(); inline BOOL fIsPaused() {return (ASYNC_QUEUE_STATUS_PAUSED == dwGetCurrentState());}; // // "Freeze" API - This is used to allow the QAPI to freeze/thaw a queue // void FreezeQueue() { dwGetNextState(ASYNC_QUEUE_ACTION_FREEZE); UpdateThreadsNeeded();}; void ThawQueue(); inline BOOL fIsFrozen() {return (ASYNC_QUEUE_STATUS_FROZEN == dwGetCurrentState());}; // "Kick" API // kicking the queue overrides freezing, so if it is frozen it should be thawed. void KickQueue() { ThawQueue(); StartRetry(); }; // chefking for shutdown inline BOOL fInShutdown() {return (ASYNC_QUEUE_STATUS_SHUTDOWN == dwGetCurrentState());}; // Denotes whether threads should stop processing. (replaces fIsPaused) inline BOOL fShouldStopProcessing() { return (fIsFrozen() || fIsPaused());}; // // Tells the queue about pending async completions, so it can be // intelligent about throttling. As we hit the limit, we will // pause/unpause the queue // void IncPendingAsyncCompletions(); void DecPendingAsyncCompletions(); BOOL fNoPendingAsyncCompletions(); // // Basic QAPI functionality // DWORD cQueueAdminGetNumItems() {return m_cItemsPending;}; DWORD dwQueueAdminLinkGetLinkState(); protected: CFifoQueue m_fqQueue; //queue for items //Function called to handle item pulled off of queue QCOMPFN m_pfnQueueCompletion; //Function called to handle items that could not be called due to resource //failures (for example during MergeRetryQueue). QCOMPFN m_pfnFailedItem; //Function called to walk the queues when the completion function fails typename CFifoQueue::MAPFNAPI m_pfnQueueFailure; //Process the item at the head of the queue HRESULT HrProcessSingleQueueItem(); //Handles callback for dropped data void HandleDroppedItem(PQDATA pqdItem); VOID IncrementPendingCount(LONG lCount=1) { if (!lCount) return; _ASSERT(lCount > 0); //should call decrement InterlockedExchangeAdd((PLONG) &m_cItemsPending, lCount); // Update threads needed UpdateThreadsNeeded(); }; VOID DecrementPendingCount(LONG lCount=-1) { if (!lCount) return; _ASSERT(lCount < 0); //should call increment instead InterlockedExchangeAdd((PLONG) &m_cItemsPending, lCount); // Update threads needed UpdateThreadsNeeded(); }; //Update the local and global threads needed counters void UpdateThreadsNeeded(); //Used to decide if we should add or remove threads from this queue BOOL fIsThreadCountAcceptable(); }; //---[ CAsyncRetryQueue ]------------------------------------------------------ // // // Description: // Derived class of CAsyncQueue adds an additional queue to gracefully // handle retry scenarios. // // Messages are first placed in the normal retry queue, If they fail, // they are placed in a secondary retry queue, which will not be retried // until this queue is kicked by an external retry timer. // Hungarian: // asyncrq, pasyncrq // //----------------------------------------------------------------------------- template class CAsyncRetryQueue : public CAsyncQueue { public: CAsyncRetryQueue(); ~CAsyncRetryQueue(); HRESULT HrDeinitialize(typename CFifoQueue::MAPFNAPI pfnQueueShutdown, CAQSvrInst *paqinst); void StartRetry() { MergeRetryQueue(); CAsyncQueue::StartRetry(); }; HRESULT HrQueueRequest(PQDATA pqdata, BOOL fRetry = FALSE); //Queue request for processing virtual BOOL fHandleCompletionFailure(PQDATA pqdata); virtual HRESULT HrMapFn(typename CFifoQueue::MAPFNAPI pfnQueueFn, PVOID pvContext); virtual HRESULT HrMapFnBaseQueue(typename CFifoQueue::MAPFNAPI pfnQueueFn, PVOID pvContext); virtual HRESULT HrMapFnRetryQueue(typename CFifoQueue::MAPFNAPI pfnQueueFn, PVOID pvContext); DWORD cGetItemsPendingRetry() {return m_cRetryItems;}; // // Basic QAPI functionality // DWORD cQueueAdminGetNumItems() {return (m_cItemsPending+m_cRetryItems);}; DWORD dwQueueAdminLinkGetLinkState(); protected: DWORD m_dwRetrySignature; DWORD m_cRetryItems; CFifoQueue m_fqRetryQueue; //queue for items void MergeRetryQueue(); }; //Define typical asyncq type for casting typedef CAsyncQueue ASYNCQ_TYPE; typedef ASYNCQ_TYPE *PASYNCQ_TYPE; //---[ AsyncQueueAtqCompletion ]----------------------------------------------- // // // Description: // Atq completion routine. This is slightly tricky since we cannot pass // a templated function to the ATQ context. This is the one place that // templating breaks down, and we actually need to list all of the // supported PQDATA types. // Parameters: // pvContext - ptr fo CAsyncQueue class // Returns: // - // History: // 7/17/98 - MikeSwa Created // 3/8/99 - MikeSwa Added ASYNC_QUEUE_WORK_SIG // 12/11/2000 - MikeSwa Added t-toddc's virtual code // //---------------------------------------------------------------------------- inline VOID AsyncQueueAtqCompletion(PVOID pvContext, DWORD vbBytesWritten, DWORD dwStatus, OVERLAPPED *pOverLapped) { CAsyncQueueBase *pasyncqb = (PASYNCQ_TYPE) pvContext; DWORD dwTemplateSig = pasyncqb->m_dwTemplateSignature; DWORD dwCurrentQueueState = pasyncqb->dwGetCurrentState(); _ASSERT(ASYNC_QUEUE_SIG == pasyncqb->m_dwSignature); //Up total async thread count (only async threads visit this function) InterlockedIncrement((PLONG) &(pasyncqb->m_cTotalAsyncCompletionThreads)); InterlockedDecrement((PLONG) &(pasyncqb->m_cCompletionThreadsRequested)); InterlockedIncrement((PLONG) &(pasyncqb->m_cCurrentAsyncThreads)); // // Call completion routing if we are not shutting down // if (CAsyncQueueBase::ASYNC_QUEUE_STATUS_SHUTDOWN != dwCurrentQueueState) { pasyncqb->StartThreadCompletionRoutine(FALSE); } InterlockedDecrement((PLONG) &(pasyncqb->m_cCurrentAsyncThreads)); } #endif //__ASYNCQ_H__