//----------------------------------------------------------------------------- // // // File: asncwrkq.cpp // // Description: Implementation of CAsyncWorkQueue. // // Author: Mike Swafford (MikeSwa) // // History: // 3/8/99 - MikeSwa Created // // Copyright (C) 1999 Microsoft Corporation // //----------------------------------------------------------------------------- #include "aqprecmp.h" #include "asncwrkq.h" #include "asyncq.inl" CPool CAsyncWorkQueueItem::s_CAsyncWorkQueueItemPool; DWORD CAsyncWorkQueueItem::s_cCurrentHeapAllocations = 0; DWORD CAsyncWorkQueueItem::s_cTotalHeapAllocations = 0; //---[ CAsyncWorkQueueItem::new ]---------------------------------------------- // // // Description: // Wrapper for new that will use CPool or Exchmem to allocate... // whichever is appropriate. // Parameters: // size size of item to allocate (should always be // sizeof (CAsyncWorkQueueItem) // Returns: // Pointer to newly allocated CAsyncWorkQueueItem // History: // 7/8/99 - MikeSwa Created // //----------------------------------------------------------------------------- void * CAsyncWorkQueueItem::operator new(size_t size) { CAsyncWorkQueueItemAllocatorBlock *pcpaqwi = NULL; _ASSERT(sizeof(CAsyncWorkQueueItem) == size); pcpaqwi = (CAsyncWorkQueueItemAllocatorBlock *) s_CAsyncWorkQueueItemPool.Alloc(); if (pcpaqwi) { pcpaqwi->m_dwSignature = ASYNC_WORK_QUEUE_ENTRY_ALLOC_CPOOL_SIG; } else { //Fallback on Exchmem pcpaqwi = (CAsyncWorkQueueItemAllocatorBlock *) pvMalloc(sizeof(CAsyncWorkQueueItemAllocatorBlock)); if (pcpaqwi) { pcpaqwi->m_dwSignature = ASYNC_WORK_QUEUE_ENTRY_ALLOC_HEAP_SIG; DEBUG_DO_IT(InterlockedIncrement((PLONG) &s_cCurrentHeapAllocations)); DEBUG_DO_IT(InterlockedIncrement((PLONG) &s_cTotalHeapAllocations)); } } if (pcpaqwi) return ((void *) &(pcpaqwi->m_pawqi)); else return NULL; } //---[ CAsyncWorkQueueItem::delete ]------------------------------------------- // // // Description: // Delete operator that will handle deleting via CPool or exchmem // Parameters: // pv Object to delete // size Size of object // Returns: // - // History: // 7/8/99 - MikeSwa Created // //----------------------------------------------------------------------------- void CAsyncWorkQueueItem::operator delete(void *pv, size_t size) { _ASSERT(sizeof(CAsyncWorkQueueItem) == size); _ASSERT(pv); CAsyncWorkQueueItemAllocatorBlock *pcpaqwi = CONTAINING_RECORD(pv, CAsyncWorkQueueItemAllocatorBlock, m_pawqi); DWORD dwOldSignature = pcpaqwi->m_dwSignature; _ASSERT(ASYNC_WORK_QUEUE_ENTRY_ALLOC_INVALID_SIG != dwOldSignature); //Reset signature before we free it, in case memory allocators //do not overwrite it (we want our asserts to fire at the time //of the double-free). pcpaqwi->m_dwSignature = ASYNC_WORK_QUEUE_ENTRY_ALLOC_INVALID_SIG; switch(dwOldSignature) { case ASYNC_WORK_QUEUE_ENTRY_ALLOC_CPOOL_SIG: s_CAsyncWorkQueueItemPool.Free(pcpaqwi); break; case ASYNC_WORK_QUEUE_ENTRY_ALLOC_HEAP_SIG: DEBUG_DO_IT(InterlockedDecrement((PLONG) &s_cCurrentHeapAllocations)); FreePv(pcpaqwi); break; default: _ASSERT(0 && "Invalid signature when freeing CAsyncWorkQueueItem"); } } //---[ CAsyncWorkQueueItem::CAsyncWorkQueueItem ]------------------------------ // // // Description: // Default constructor for CAsyncWorkQueueItem // Parameters: // pvData Data to pass to completion function // pfnCompletion Completion function // Returns: // - // History: // 3/8/99 - MikeSwa Created // //----------------------------------------------------------------------------- CAsyncWorkQueueItem::CAsyncWorkQueueItem(PVOID pvData, PASYNC_WORK_QUEUE_FN pfnCompletion) { _ASSERT(pfnCompletion); m_dwSignature = ASYNC_WORK_QUEUE_ENTRY; m_pvData = pvData; m_pfnCompletion = pfnCompletion; } //---[ CAsyncWorkQueueItem::~CAsyncWorkQueueItem ]----------------------------- // // // Description: // Default destructor for CAsyncWorkQueueItem // Parameters: // - // Returns: // - // History: // 3/8/99 - MikeSwa Created // //----------------------------------------------------------------------------- CAsyncWorkQueueItem::~CAsyncWorkQueueItem() { m_dwSignature = ASYNC_WORK_QUEUE_ENTRY_FREE; } //---[ CAsyncWorkQueue::CAsyncWorkQueue ]-------------------------------------- // // // Description: // Default constructor for CAsyncWorkQueue // Parameters: // - // Returns: // - // History: // 3/8/99 - MikeSwa Created // //----------------------------------------------------------------------------- CAsyncWorkQueue::CAsyncWorkQueue() { m_dwSignature = ASYNC_WORK_QUEUE_SIG; m_cWorkQueueItems = 0; m_dwStateFlags = ASYNC_WORK_QUEUE_NORMAL; } //---[ CAsyncWorkQueue::~CAsyncWorkQueue ]------------------------------------- // // // Description: // Destructor for CAsyncWorkQueue // Parameters: // - // Returns: // - // History: // 3/8/99 - MikeSwa Created // //----------------------------------------------------------------------------- CAsyncWorkQueue::~CAsyncWorkQueue() { m_dwSignature = ASYNC_WORK_QUEUE_SIG_FREE; } //---[ CAsyncWorkQueue::HrInitialize ]----------------------------------------- // // // Description: // Initialization routing for CAsyncWorkQueue base. Initializes the // CAsyncQueue // Parameters: // cItemsPerThread The number of items to process per async thread // Returns: // S_OK on success // Failure code from CAsyncQueue::HrInitialize() // History: // 3/8/99 - MikeSwa Created // //----------------------------------------------------------------------------- HRESULT CAsyncWorkQueue::HrInitialize(DWORD cItemsPerThread) { HRESULT hr = S_OK; hr = m_asyncq.HrInitialize(0, //there can be *no* sync threads cItemsPerThread, 1,//init requires this value to be at least 1 this, CAsyncWorkQueue::fQueueCompletion, CAsyncWorkQueue::fQueueFailure, NULL); return hr; } //---[ CAsyncWorkQueue::HrDeinitialize ]--------------------------------------- // // // Description: // Signals shutdown for queue code // Parameters: // paqinst Pointer to AQ server instance object // Returns: // S_OK on success // History: // 3/8/99 - MikeSwa Created // 7/7/99 - MikeSwa Allow async threads to help process shutdown // //----------------------------------------------------------------------------- HRESULT CAsyncWorkQueue::HrDeinitialize(CAQSvrInst *paqinst) { const DWORD MAX_ITERATIONS_NO_PROGRESS = 1000; //iterations before assert HRESULT hr = S_OK; DWORD cLastCount = cGetWorkQueueItems(); DWORD cIterationsNoProgress = 0; _ASSERT(paqinst); //Start processing all items in "shutdown" mode m_dwStateFlags = ASYNC_WORK_QUEUE_SHUTDOWN; // // Make sure we have threads actively processing this queue before // we settle down and wait for them to stop. // _ASSERT(!cGetWorkQueueItems() || m_asyncq.dwGetTotalThreads()); m_asyncq.StartRetry(); //Let the worker threads have some fun before we stop and do the single //theaded initialization while (cLastCount && (cIterationsNoProgress < MAX_ITERATIONS_NO_PROGRESS)) { if (cLastCount <= cGetWorkQueueItems()) cIterationsNoProgress++; //I'd like to see this case _ASSERT(cIterationsNoProgress < MAX_ITERATIONS_NO_PROGRESS); cLastCount = cGetWorkQueueItems(); paqinst->ServerStopHintFunction(); //Since it may take longer than our stop hint to process a //single item in the queue, we need to sleep instead of //attempting to process an item (Bug #X5:118258). Sleep(10000); } hr = m_asyncq.HrDeinitialize(CAsyncWorkQueue::HrShutdownWalkFn, paqinst); return hr; } //---[ CAsyncWorkQueue::HrQueueWorkItem ]-------------------------------------- // // // Description: // Queues items to async work queue // Parameters: // pvData Data item to pass to completion function // pfCompletion Completion function // Returns: // S_OK on success // E_OUTOFMEMORY if queue item could not be allocated // History: // 3/8/99 - MikeSwa Created // //----------------------------------------------------------------------------- HRESULT CAsyncWorkQueue::HrQueueWorkItem(PVOID pvData, PASYNC_WORK_QUEUE_FN pfnCompletion) { HRESULT hr = S_OK; CAsyncWorkQueueItem *pawqi = NULL; _ASSERT(pvData); _ASSERT(pfnCompletion); if (!pfnCompletion) { hr = E_INVALIDARG; goto Exit; } //Create queue item, initialize it, and queue it pawqi = new CAsyncWorkQueueItem(pvData, pfnCompletion); if (!pawqi) { hr = E_OUTOFMEMORY; goto Exit; } hr = m_asyncq.HrQueueRequest(pawqi, FALSE); if (FAILED(hr)) goto Exit; InterlockedIncrement((PLONG) &m_cWorkQueueItems); Exit: if (FAILED(hr) && pfnCompletion) { //call completion function pfnCompletion(pvData, ASYNC_WORK_QUEUE_FAILURE | ASYNC_WORK_QUEUE_ENQUEUE_THREAD); } if (pawqi) pawqi->Release(); return hr; } //---[ CAsyncWorkQueue::fQueueCompletion ]------------------------------------- // // // Description: // Completion function called by CAsyncQueue // Parameters: // pawqi CAsyncWorkQueueItem to process // pvContext "this" pointer // Returns: // TRUE if item was process // FALSE otherwise // History: // 3/8/99 - MikeSwa Created // //----------------------------------------------------------------------------- BOOL CAsyncWorkQueue::fQueueCompletion(CAsyncWorkQueueItem *pawqi, PVOID pvContext) { BOOL fRet = TRUE; CAsyncWorkQueue *pawq = (CAsyncWorkQueue *) pvContext; _ASSERT(pawqi); _ASSERT(pawq); _ASSERT(ASYNC_WORK_QUEUE_ENTRY == pawqi->m_dwSignature); _ASSERT(ASYNC_WORK_QUEUE_SIG == pawq->m_dwSignature); fRet = pawqi->m_pfnCompletion(pawqi->m_pvData, pawq->m_dwStateFlags); if (fRet) InterlockedDecrement((PLONG) &(((CAsyncWorkQueue *)pawq)->m_cWorkQueueItems)); return fRet; } //---[ CAsyncWorkQueue::fQueueFailure ]---------------------------------------- // // // Description: // Function to handle internal failures in CAsyncQueue // Parameters: // pawq "this" pointer // pawqi CAsyncWorkQueueItem to process // Returns: // TRUE always // History: // 3/8/99 - MikeSwa Created // //----------------------------------------------------------------------------- BOOL CAsyncWorkQueue::fQueueFailure(CAsyncWorkQueueItem *pawqi, PVOID pawq) { _ASSERT(pawqi); _ASSERT(pawq); _ASSERT(ASYNC_WORK_QUEUE_ENTRY == pawqi->m_dwSignature); _ASSERT(ASYNC_WORK_QUEUE_SIG == ((CAsyncWorkQueue *)pawq)->m_dwSignature); pawqi->m_pfnCompletion(pawqi->m_pvData, ASYNC_WORK_QUEUE_FAILURE); InterlockedDecrement((PLONG) &(((CAsyncWorkQueue *)pawq)->m_cWorkQueueItems)); return TRUE; } //---[ CAsyncWorkQueue::HrShutdownWalkFn ]------------------------------------- // // // Description: // Function to walk an CAsyncWorkQueue queue at shutdown and clear out // all of the pending work items // Parameters: // IN CAsyncWorkQueueItem ptr to data on queue // IN PVOID pvContext AQ server intstance // OUT BOOL *pfContinue, TRUE if we should continue // OUT BOOL *pfDelete); TRUE if item should be deleted // Returns: // S_OK always // History: // 3/8/99 - MikeSwa Created // //----------------------------------------------------------------------------- HRESULT CAsyncWorkQueue::HrShutdownWalkFn( CAsyncWorkQueueItem *pawqi, PVOID pvContext, BOOL *pfContinue, BOOL *pfDelete) { CAQSvrInst *paqinst = (CAQSvrInst *) pvContext; _ASSERT(pfContinue); _ASSERT(pfDelete); _ASSERT(pawqi); _ASSERT(ASYNC_WORK_QUEUE_ENTRY == pawqi->m_dwSignature); *pfContinue = TRUE; *pfDelete = TRUE; //call server stop hint function paqinst->ServerStopHintFunction(); pawqi->m_pfnCompletion(pawqi->m_pvData, ASYNC_WORK_QUEUE_SHUTDOWN); return S_OK; }