|
|
//-----------------------------------------------------------------------------
//
//
// File: Hndlmgmr.cpp
//
// Description:
// Contains implementation of the CQueueHandleManager class
//
// Author: mikeswa
//
// Copyright (C) 2001 Microsoft Corporation
//
//-----------------------------------------------------------------------------
#include "aqprecmp.h"
#include "hndlmgr.h"
//
// Initialize statics
//
DWORD CQueueHandleManager::s_cNumQueueInstances = 0; DWORD CQueueHandleManager::s_cNumQueueInstancesWithLowBackLog = 0; DWORD CQueueHandleManager::s_cReservedHandles = 0; DWORD CQueueHandleManager::s_cMaxSharedConcurrentItems = 0;
//---[ CQueueHandleManager::CQueueHandleManager ]------------------------------
//
//
// Description:
// Constructor for CQueueHandleManger
// Parameters:
// -
// Returns:
// -
// History:
// 05/12/2001 - MikeSwa Created
//
//-----------------------------------------------------------------------------
CQueueHandleManager::CQueueHandleManager() { m_dwSignature = CQueueHandleManager_Sig;
//
// Users must call into ReportMaxConcurrentItems first
//
m_dwCurrentState = QUEUE_STATE_UNITIALIZED; m_cMaxPrivateConcurrentItems = 0; m_cMaxSharedConcurrentItems = 0; m_cDbgStateTransitions = 0; m_cDbgCallsToUpdateStateIfNecessary = 0; }
//---[ CQueueHandleManager::~CQueueHandleManager ]-----------------------------
//
//
// Description:
// Destructor for CQueueHandleManger
// Parameters:
// -
// Returns:
// -
// History:
// 05/12/2001 - MikeSwa Created
//
//-----------------------------------------------------------------------------
CQueueHandleManager::~CQueueHandleManager() { DeinitializeStaticsAndStateIfNecessary(); m_dwSignature = CQueueHandleManager_Sig; m_dwCurrentState = QUEUE_STATE_UNITIALIZED; };
//---[ CQueueHandleManager::DeinitializeStaticsAndStateIfNecessary ]-----------
//
//
// Description:
// Update statics based on this queue instance's state. This is used
// in the desctructor and when the config is updated.
// Parameters:
// -
// Returns:
// -
// History:
// 05/12/2001 - MikeSwa Created
//
//-----------------------------------------------------------------------------
void CQueueHandleManager::DeinitializeStaticsAndStateIfNecessary() { DWORD dwOldState = QUEUE_STATE_UNITIALIZED;
if (fIsInitialized()) { if (m_cMaxPrivateConcurrentItems) { dwInterlockedAddSubtractDWORD(&s_cReservedHandles, m_cMaxPrivateConcurrentItems, FALSE); }
dwOldState = InterlockedExchange((PLONG) &m_dwCurrentState, QUEUE_STATE_UNITIALIZED);
//
// Update statics based on previous states
//
if ((QUEUE_STATE_LOW_BACKLOG == dwOldState) || (QUEUE_STATE_ASYNC_BACKLOG == dwOldState)) InterlockedDecrement((PLONG) &s_cNumQueueInstancesWithLowBackLog);
//
// The last one here gets to updated the shared count
//
if (0 == InterlockedDecrement((PLONG) &s_cNumQueueInstances) && s_cMaxSharedConcurrentItems) { dwInterlockedAddSubtractDWORD(&s_cReservedHandles, s_cMaxSharedConcurrentItems, FALSE); }
} }
//---[ CQueueHandleManager::SetMaxConcurrentItems ]-------------------------
//
//
// Description:
// Sets appropriate settings for this queue instance
// Parameters:
// -
// Returns:
// -
// History:
// 05/12/2001 - MikeSwa Created
//
//-----------------------------------------------------------------------------
void CQueueHandleManager::SetMaxConcurrentItems( DWORD cMaxSharedConcurrentItems, DWORD cMaxPrivateConcurrentItems) { DWORD dwRefCount = 0; DWORD dwOldState = QUEUE_STATE_UNITIALIZED; //
// Odd things will happen if this is called multiple times... since
// part of the initialization is undone before it is finalized
//
_ASSERT(!fIsInitialized() && "Already initialized");
//
// I don't think this is possible (hence above assert), but this
// will at least prevent the static data from becoming invald and
// will only lead to transient oddities.
//
DeinitializeStaticsAndStateIfNecessary(); dwRefCount = InterlockedIncrement((PLONG) &s_cNumQueueInstances);
dwOldState = InterlockedExchange((PLONG) &m_dwCurrentState, QUEUE_STATE_NO_BACKLOG);
_ASSERT(QUEUE_STATE_UNITIALIZED == dwOldState); //
// Update statics based on previous states - again this
// should not be necessary - firewall anyway
//
if ((QUEUE_STATE_LOW_BACKLOG == dwOldState) || (QUEUE_STATE_ASYNC_BACKLOG == dwOldState)) InterlockedDecrement((PLONG) &s_cNumQueueInstancesWithLowBackLog);
//
// Calculate the appropriate reserve handle count. Eaxh queue
// can handle a certain number of items concurrently. Some of these
// are constrained by process-wide resources (such as a thread pool),
// but others are things like async completions and
//
m_cMaxPrivateConcurrentItems = cMaxPrivateConcurrentItems; m_cMaxSharedConcurrentItems = cMaxSharedConcurrentItems; if (m_cMaxPrivateConcurrentItems) { dwInterlockedAddSubtractDWORD(&s_cReservedHandles, m_cMaxPrivateConcurrentItems, TRUE); } if (m_cMaxSharedConcurrentItems && (1 == dwRefCount)) { //
// The expectation is that there will not be multiple threads
// bouncing the refcount off zero, since VSI start/stop is
// single threaded, and an instance has at least one static
// instance.
//
_ASSERT(s_cNumQueueInstances && "threading violation"); s_cMaxSharedConcurrentItems = m_cMaxSharedConcurrentItems; dwInterlockedAddSubtractDWORD(&s_cReservedHandles, s_cMaxSharedConcurrentItems, TRUE); }
}
//---[ CQueueHandleManager::fShouldCloseHandle ]-------------------------------
//
//
// Description:
// Called by queue instances to determine if they should close handles.
// Must be preceeded by a call to SetMaxConcurrentItems to initialize
// configuration.
// Parameters:
// IN cItemsPending Number of items currently waiting to be
// processed on this queue.
// IN cItemsPendingAsyncCompletions - Items that are actually opened
// and currently being processed
// IN cCurrentMsgsOpenInSystem - The current number of messages
// open in the process (ie - how much resources
// are being consumed).
// Returns:
// TRUE - Caller should close messages now
// FALSE - Caller should *not* close messages now
// History:
// 05/12/2001 - MikeSwa Created
//
//-----------------------------------------------------------------------------
BOOL CQueueHandleManager::fShouldCloseHandle( DWORD cItemsPending, DWORD cItemsPendingAsyncCompletions, DWORD cCurrentMsgsOpenInSystem) { TraceFunctEnterEx((LPARAM) this, "CQueueHandleManager::fShouldCloseHandle"); _ASSERT(fIsInitialized()); DWORD dwState = m_dwCurrentState; DWORD cHandleLimit = 0; BOOL fShouldClose = TRUE;
if (QUEUE_STATE_UNITIALIZED != dwState) { //
// See if state needs to be updated
//
dwState = dwUpdateCurrentStateIfNeccessary(cItemsPending, cItemsPendingAsyncCompletions); _ASSERT(QUEUE_STATE_UNITIALIZED != dwState); }
//
// Code defensively - Assume worst case
//
if (QUEUE_STATE_UNITIALIZED == dwState) { ErrorTrace((LPARAM) this, "Queue state is unitialized"); dwState = QUEUE_STATE_BACKLOG; //defensive code
}
cHandleLimit = cGetHandleLimitForState(dwState);
//
// Now that we have the limit on the number of handles, the math is easy
//
if (cHandleLimit > cCurrentMsgsOpenInSystem) fShouldClose = FALSE;
DebugTrace((LPARAM) this, "%s Handle - %d pending, %d pending async, 0x%X state, %d open msgs, %d handle limit", (fShouldClose ? "Closing" : "Not closing"), cItemsPending, cItemsPendingAsyncCompletions, dwState, cCurrentMsgsOpenInSystem, cHandleLimit);
TraceFunctLeave(); return fShouldClose; }
//---[ CQueueHandleManager::cGetHandleLimitForState ]--------------------------
//
//
// Description:
// Called by queue instances to determine if they should close handles.
// Must be preceeded by a call to SetMaxConcurrentItems to initialize
// configuration. - Static method
// Parameters:
// IN dwState The state to calculate the limit for
// Returns:
// The handle limti for the given state
// History:
// 05/17/2001 - MikeSwa Created
//
//-----------------------------------------------------------------------------
DWORD CQueueHandleManager::cGetHandleLimitForState(DWORD dwState) { DWORD cHandleLimit = g_cMaxIMsgHandlesThreshold; DWORD cReserve = s_cReservedHandles;
//
// Allow registry configurable limit
//
if (s_cReservedHandles > g_cMaxHandleReserve) cReserve = g_cMaxHandleReserve;
//
// Our logic only makes sense if this is true
//
_ASSERT(g_cMaxIMsgHandlesThreshold >= g_cMaxIMsgHandlesLowThreshold);
//
// If the handle limit is actually... zero, then close without
// regard for the queue state.
//
if (!cHandleLimit) goto Exit;
switch(dwState) { //
// The number of messages pending is equal to the number
// of messages that can be concurrently processed. In this case,
// we should try hard not to close handles. To accomplish this,
// we will dip into our reserve.
//
// Async backlog is a similar case...we have no backlog of items
// pending, but we have a large number of pending completions
//
case QUEUE_STATE_NO_BACKLOG: case QUEUE_STATE_ASYNC_BACKLOG: cHandleLimit += cReserve; break;
//
// In the case where there are some of messages queued up (to
// a configured percentage of the max handle limit), we will
// continue closing handles normally
//
case QUEUE_STATE_LOW_BACKLOG: break; //use handle limit as-is
//
// In the case where there is a significant backlog, we
// would like to use handles if available... but not to the
// determent of shorter queues. If there are other queues
// have a low backlog... we will defer to them. Otherwise,
// we will use as many handles as we can.
//
case QUEUE_STATE_BACKLOG: if (s_cNumQueueInstancesWithLowBackLog) cHandleLimit = g_cMaxIMsgHandlesLowThreshold; break;
//
// Queue is either non initialized or in an invalid state.
// We will err on the side of caution and treat this as
//
default: _ASSERT(0 && "Invalid Queue State"); cHandleLimit = 0; } Exit: return cHandleLimit; }
//---[ CQueueHandleManager::dwUpdateCurrentStateIfNeccessary ]----------------
//
//
// Description:
// Will update this queues state if necessary and return the resulting
// state.
// Parameters:
// IN cItemsPending Number of items currently waiting to be
// processed on this queue.
// IN cItemsPendingAsyncCompletions - Items that are actually opened
// and currently being processed
// Returns:
// The state for given lengths
// History:
// 05/17/2001 - MikeSwa Created
//
//-----------------------------------------------------------------------------
DWORD CQueueHandleManager::dwUpdateCurrentStateIfNeccessary( DWORD cItemsPending, DWORD cItemsPendingAsyncCompletions) { TraceFunctEnterEx((LPARAM) this, "CQueueHandleManager::dwUpdateCurrentStateIfNeccessary"); DWORD dwOldState = m_dwCurrentState; DWORD dwNewState = dwOldState; DWORD dwCompare = dwOldState; _ASSERT(fIsInitialized());
if (!fIsInitialized()) { ErrorTrace((LPARAM) this, "Queue is not initialized"); goto Exit; }
m_cDbgCallsToUpdateStateIfNecessary++; dwNewState = dwGetStateForLengths(cItemsPending, cItemsPendingAsyncCompletions);
//
// We need to update the state... do this in a thread-safe manner
//
do { dwOldState = m_dwCurrentState; if (dwNewState == dwOldState) goto Exit; dwCompare = InterlockedCompareExchange((PLONG) &m_dwCurrentState, dwNewState, dwOldState); } while (dwCompare != dwOldState);
//
// Now that we have changed state, we are responsible for updating
// the static counters for the old and new states
//
if ((QUEUE_STATE_LOW_BACKLOG == dwNewState) || (QUEUE_STATE_ASYNC_BACKLOG == dwNewState)) InterlockedIncrement((PLONG) &s_cNumQueueInstancesWithLowBackLog);
if ((QUEUE_STATE_LOW_BACKLOG == dwOldState) || (QUEUE_STATE_ASYNC_BACKLOG == dwOldState)) InterlockedDecrement((PLONG) &s_cNumQueueInstancesWithLowBackLog);
m_cDbgStateTransitions++; Exit: TraceFunctLeave(); return dwNewState; }
//---[ CQueueHandleManager::dwGetStateForLengths ]-----------------------------
//
//
// Description:
// Static method to determine the appropriate state for a given
// set of lenghts
// Parameters:
// IN cItemsPending Number of items currently waiting to be
// processed on this queue.
// IN cItemsPendingAsyncCompletions - Items that are actually opened
// and currently being processed
// Returns:
// The state for given lengths
// History:
// 05/17/2001 - MikeSwa Created
//
//-----------------------------------------------------------------------------
DWORD CQueueHandleManager::dwGetStateForLengths( DWORD cItemsPending, DWORD cItemsPendingAsyncCompletions) { DWORD dwState = QUEUE_STATE_BACKLOG;
//
// If we are at or less than our max number of concurrent items
//
if (cItemsPending <= s_cReservedHandles) dwState = QUEUE_STATE_NO_BACKLOG; else if (cItemsPending <= g_cMaxIMsgHandlesLowThreshold) dwState = QUEUE_STATE_LOW_BACKLOG;
//
// Async completions are slightly tricky. We don't want to bounce
// a handle simply because we have a large number of async completions.
// We also want to identify ourselves as a potential user of handles.
//
// We have a state (QUEUE_STATE_ASYNC_BACKLOG) that is used to
// indicate that while there is no backlog of items pending, there
// may be a large number of items owned by this queue (with open
// handles). This state has the effect of:
// - Flagging this queue has one with a low backlog
// - Managing handles for *this* queue as if there was no backlog
//
// If there is any backlog of any kind for cItemsPending, we will
// treat the queue based on those results
//
if ((QUEUE_STATE_NO_BACKLOG == dwState) && (cItemsPendingAsyncCompletions >= s_cReservedHandles)) dwState = QUEUE_STATE_ASYNC_BACKLOG; return dwState; }
|