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.
441 lines
13 KiB
441 lines
13 KiB
//-----------------------------------------------------------------------------
|
|
//
|
|
//
|
|
// 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;
|
|
}
|