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.
482 lines
16 KiB
482 lines
16 KiB
//-----------------------------------------------------------------------------
|
|
//
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
|
|
|