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.
380 lines
14 KiB
380 lines
14 KiB
//-----------------------------------------------------------------------------
|
|
//
|
|
//
|
|
// 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 <fifoq.h>
|
|
#include <intrnlqa.h>
|
|
#include <baseobj.h>
|
|
#include <aqstats.h>
|
|
#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 PQDATA, DWORD TEMPLATE_SIG>
|
|
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<PQDATA>::MAPFNAPI pfnQueueFailure,
|
|
DWORD cMaxPendingAsyncCompletions = 0);
|
|
|
|
HRESULT HrDeinitialize(typename CFifoQueue<PQDATA>::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<PQDATA>::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<PQDATA> 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<PQDATA>::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 PQDATA, DWORD TEMPLATE_SIG>
|
|
class CAsyncRetryQueue : public CAsyncQueue<PQDATA, TEMPLATE_SIG>
|
|
{
|
|
public:
|
|
CAsyncRetryQueue();
|
|
~CAsyncRetryQueue();
|
|
|
|
HRESULT HrDeinitialize(typename CFifoQueue<PQDATA>::MAPFNAPI pfnQueueShutdown,
|
|
CAQSvrInst *paqinst);
|
|
void StartRetry()
|
|
{
|
|
MergeRetryQueue();
|
|
CAsyncQueue<PQDATA, TEMPLATE_SIG>::StartRetry();
|
|
};
|
|
HRESULT HrQueueRequest(PQDATA pqdata, BOOL fRetry = FALSE); //Queue request for processing
|
|
virtual BOOL fHandleCompletionFailure(PQDATA pqdata);
|
|
virtual HRESULT HrMapFn(typename CFifoQueue<PQDATA>::MAPFNAPI pfnQueueFn, PVOID pvContext);
|
|
virtual HRESULT HrMapFnBaseQueue(typename CFifoQueue<PQDATA>::MAPFNAPI pfnQueueFn, PVOID pvContext);
|
|
virtual HRESULT HrMapFnRetryQueue(typename CFifoQueue<PQDATA>::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<PQDATA> m_fqRetryQueue; //queue for items
|
|
|
|
void MergeRetryQueue();
|
|
};
|
|
|
|
//Define typical asyncq type for casting
|
|
typedef CAsyncQueue<CMsgRef *, ASYNC_QUEUE_MSGREF_SIG> 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__
|