Leaked source code of windows server 2003
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
12 KiB

/*++
Copyright (C) 1996-2001 Microsoft Corporation
Module Name:
MERGERTHROTTLING.CPP
Abstract:
CMergerThrottling clas
History:
30-Nov-00 sanjes Created.
--*/
#include "precomp.h"
#pragma warning (disable : 4786)
#include <wbemcore.h>
#include <map>
#include <vector>
#include <genutils.h>
#include <oahelp.inl>
#include <wqllex.h>
#include "wmimerger.h"
#include "mergerthrottling.h"
static long g_lNumMergers = 0L;
//***************************************************************************
//
//***************************************************************************
//
CMergerThrottling::CMergerThrottling( void )
: m_hParentThrottlingEvent( NULL ), m_hChildThrottlingEvent( NULL ), m_dwNumChildObjects( 0 ),
m_dwNumParentObjects( 0 ), m_dwNumThrottledThreads( 0 ), m_bParentThrottled( false ),
m_bChildThrottled( true ), m_bChildDone( false ), m_bParentDone( false ),
m_dwThrottlingThreshold( 0 ), m_dwReleaseThreshold( 0 ), m_dwLastParentPing( 0 ),
m_dwLastChildPing( 0 ), m_dwProviderDeliveryTimeout( 0xFFFFFFFF ), m_dwBatchingThreshold( 0 ),
m_cs()
{
}
//***************************************************************************
//
//***************************************************************************
//
CMergerThrottling::~CMergerThrottling( void )
{
_DBG_ASSERT( m_dwNumChildObjects == 0 && m_dwNumParentObjects == 0 );
if ( NULL != m_hParentThrottlingEvent )
{
CloseHandle( m_hParentThrottlingEvent );
}
if ( NULL != m_hChildThrottlingEvent )
{
CloseHandle( m_hChildThrottlingEvent );
}
}
// Two step initialization. This retrieves values from registry to configure the
// behavior of our throttling mechanisms
HRESULT CMergerThrottling::Initialize( void )
{
HRESULT hr = WBEM_S_NO_ERROR;
ConfigMgr::GetMergerThresholdValues( &m_dwThrottlingThreshold, &m_dwReleaseThreshold,
&m_dwBatchingThreshold );
// Hold off on this until we work our way through
// m_dwProviderDeliveryTimeout = ConfigMgr::GetProviderDeliveryTimeout();
return hr;
}
// Call this function to perform proper throttling based on our registry
// configured values.
HRESULT CMergerThrottling::Throttle( bool bParent, CWmiMergerRecord* pMergerRecord )
{
bool bContinue = true;
bool bTimedOut = false;
HRESULT hr = WBEM_S_NO_ERROR;
while ( bContinue && SUCCEEDED( hr ) )
{
// Scoped for proper cleanup if anything bad happens
CCheckedInCritSec ics( &m_cs ); // SEC:REVIEWED 2002-03-22 : Assumes entry
DWORD dwAdjust = 0L;
DWORD dwWait = 0L;
// If the timed out flag is set, we need to check if we are
// really timed out
if ( bTimedOut )
{
bTimedOut = VerifyTimeout( pMergerRecord->GetWmiMerger()->GetLastDeliveryTime(),
pMergerRecord->GetWmiMerger()->NumArbitratorThrottling(), &dwAdjust );
}
if ( bTimedOut )
{
hr = WBEM_E_PROVIDER_TIMED_OUT;
continue;
}
HANDLE hEvent = ( bParent ? m_hParentThrottlingEvent : m_hChildThrottlingEvent );
bool bThrottle = ShouldThrottle( bParent );
// These should NEVER both be TRUE
_DBG_ASSERT( !( m_bParentThrottled && m_bChildThrottled ) );
if ( m_bParentThrottled && m_bChildThrottled )
{
hr = WBEM_E_FAILED;
continue;
}
if ( bThrottle )
{
hr = PrepareThrottle( bParent, &hEvent );
if (FAILED(hr)) continue;
dwWait = m_dwProviderDeliveryTimeout - dwAdjust;
}
// Since we will wait if we choose to throttle, we should do
// this OUTSIDE of our critical section
ics.Leave();
// Throttle only if appropriate
if ( !bThrottle )
{
bContinue = false;
continue;
}
// If we are about to throttle a parent, then we need to ensure a
// child delivery request is scheduled
if ( bParent )
{
hr = pMergerRecord->GetWmiMerger()->ScheduleMergerChildRequest( pMergerRecord );
if (FAILED(hr)) continue;
}
InterlockedIncrement( (long*) &m_dwNumThrottledThreads );
#ifdef __DEBUG_MERGER_THROTTLING
DbgPrintfA(0,"Thread 0x%x throttled in merger %p for 0x%x ms.\nParent Objects: %d, Child Objects: %d, Num Throttled Threads: %d\n",
GetCurrentThreadId(),
pMergerRecord->GetWmiMerger(),
dwWait,
m_dwNumParentObjects,
m_dwNumChildObjects,
m_dwNumThrottledThreads );
#endif
DEBUGTRACE((LOG_WBEMCORE,
"Thread 0x%x throttled in merger for 0x%x ms.\n"
"Parent Objects: %d, Child Objects: %d, Num Throttled Threads: %d\n",
GetCurrentThreadId(), dwWait,
m_dwNumParentObjects, m_dwNumChildObjects, m_dwNumThrottledThreads));
DWORD dwRet = CCoreQueue::QueueWaitForSingleObject( hEvent, dwWait );
DEBUGTRACE((LOG_WBEMCORE, "Thread 0x%x woken up in merger.\n",
GetCurrentThreadId() ) );
#ifdef __DEBUG_MERGER_THROTTLING
DbgPrintfA(0, L"Thread 0x%x woken up in merger %p.\n",
GetCurrentThreadId(),
pMergerRecord->GetWmiMerger());
#endif
InterlockedDecrement( (long*) &m_dwNumThrottledThreads );
// Check for error return codes.
if ( dwRet == WAIT_OBJECT_0 ) break;
if ( dwRet == WAIT_TIMEOUT )
{
bTimedOut = true;
}
else
{
hr = WBEM_E_FAILED;
}
} // WHILE check for throttling
return hr;
}
// Call this to release any actual throttled threads.
bool CMergerThrottling::ReleaseThrottle( bool bForce /* = false */ )
{
bool bRelease = bForce;
// Scoped for proper cleanup if anything bad happens
CCheckedInCritSec ics( &m_cs ); // SEC:REVIEWED 2002-03-22 : Assumes entry
if ( !bForce && ( m_bParentThrottled || m_bChildThrottled ) )
{
// These should NEVER both be TRUE
_DBG_ASSERT( !( m_bParentThrottled && m_bChildThrottled ) );
if ( !( m_bParentThrottled && m_bChildThrottled ) )
{
if ( m_bParentThrottled )
{
// We only release if we have exceeded the threshold.
if ( m_dwNumParentObjects > m_dwNumChildObjects )
{
DWORD dwDiff = m_dwNumParentObjects - m_dwNumChildObjects;
bRelease = ( dwDiff < m_dwReleaseThreshold );
}
else
{
// Always release if we are not greater than number of
// child objects
bRelease = true;
}
}
else if ( m_bChildThrottled )
{
// We only release if we have exceeded the threshold.
if ( m_dwNumChildObjects > m_dwNumParentObjects )
{
DWORD dwDiff = m_dwNumChildObjects - m_dwNumParentObjects;
bRelease = ( dwDiff < m_dwReleaseThreshold );
}
else
{
// Always release if we are not greater than number of
// child objects
bRelease = true;
}
}
} // Only if NOT both
else
{
// looks like both are throttled - we shouldn't be here, but go ahead and
// release anyway
bRelease = true;
}
} // IF not bForce and something is throttled
if ( bRelease )
{
m_bParentThrottled = false;
m_bChildThrottled = false;
// Should release everyone
if ( NULL != m_hParentThrottlingEvent )
{
SetEvent( m_hParentThrottlingEvent );
}
// Should release everyone
if ( NULL != m_hChildThrottlingEvent )
{
SetEvent( m_hChildThrottlingEvent );
}
} // IF bRelease
return bRelease;
}
// Called to log the fact that children instances are done
void CMergerThrottling::SetChildrenDone( void )
{
// Scoped for proper cleanup if anything bad happens
CCheckedInCritSec ics( &m_cs ); // SEC:REVIEWED 2002-03-22 : Assumes entry
// Child is done - we should release throttling as well
m_bChildDone = true;
ReleaseThrottle( true );
}
// Called to log the fact that parent instances are done
void CMergerThrottling::SetParentDone( void )
{
// Scoped for proper cleanup if anything bad happens
CCheckedInCritSec ics( &m_cs ); // SEC:REVIEWED 2002-03-22 : Assumes entry
// Parent is done - we should release throttling as well
m_bParentDone = true;
ReleaseThrottle( true );
}
// Causes us to clear any throttling we are doing
void CMergerThrottling::Cancel( void )
{
// Scoped for proper cleanup if anything bad happens
CCheckedInCritSec ics( &m_cs ); // SEC:REVIEWED 2002-03-22 : Assumes entry
// Everything is just over with - release the throttle as well
m_bChildDone = true;
m_bParentDone = true;
// No point in tracking these anymore.
m_dwNumChildObjects = 0;
m_dwNumParentObjects = 0;
ReleaseThrottle( true );
}
// Helper function to check if we should throttle
bool CMergerThrottling::ShouldThrottle( bool bParent )
{
bool bThrottle = false;
if ( bParent )
{
// If the child is done, no point in throttling
if ( !m_bChildDone )
{
// If for some reason parent objects are coming in on multiple threads,
// we *could* theoretically have to throttle multiple threads. If we're
// not already throttling, we should check if we need to.
if ( !m_bParentThrottled )
{
// We only throttle if we have exceeded the threshold.
if ( m_dwNumParentObjects > m_dwNumChildObjects )
{
DWORD dwDiff = m_dwNumParentObjects - m_dwNumChildObjects;
bThrottle = ( dwDiff > m_dwThrottlingThreshold );
m_bParentThrottled = bThrottle;
}
}
else
{
bThrottle = true;;
}
} // IF !m_bChildDone
}
else
{
// No point in continuing if the parent is done
if ( !m_bParentDone )
{
// More likely that multiple child threads could be coming in (e.g. multiple
// classes inheriting from a base class).
if ( !m_bChildThrottled )
{
// We only throttle if we have exceeded the threshold.
if ( m_dwNumChildObjects > m_dwNumParentObjects )
{
DWORD dwDiff = m_dwNumChildObjects - m_dwNumParentObjects;
bThrottle = ( dwDiff > m_dwThrottlingThreshold );
m_bChildThrottled = bThrottle;
}
}
else
{
bThrottle = true;
}
} // IF !m_bParentDone
}
return bThrottle;
}
// Helper function to prepare us for throttling
HRESULT CMergerThrottling::PrepareThrottle( bool bParent, HANDLE* phEvent )
{
HRESULT hr = WBEM_S_NO_ERROR;
// Create the event if we have to, otherwise reset it
if ( NULL == *phEvent )
{
// Creates in an unsignalled state
*phEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); // SEC:REVIEWED 2002-03-22 : OK
if ( NULL == *phEvent )
{
hr = WBEM_E_OUT_OF_MEMORY;
}
else if ( bParent )
{
m_hParentThrottlingEvent = *phEvent;
}
else
{
m_hChildThrottlingEvent = *phEvent;
}
}
else
{
// Make sure the event is reset
BOOL fSuccess = ResetEvent( *phEvent );
// What to do here?
_DBG_ASSERT( fSuccess );
if ( !fSuccess )
{
hr = WBEM_E_FAILED;
}
}
return hr;
}
// Helper function to verify if we timed out. For example, we may have timed out on throttling
// but, actually be receiving (albeit slowly) objects from a child or parent. Since we are getting
// stuff, we aren't really timed out, but we should adjust our wait time based on the difference
// since the last ping.
bool CMergerThrottling::VerifyTimeout( DWORD dwLastTick, long lNumArbThrottledThreads, DWORD* pdwAdjust )
{
// We only do this of no threads are throttled in the arbitrator - since we may actually
// just be slow. So if there are throttled threads, we just return that we are not timed
//out
_DBG_ASSERT( lNumArbThrottledThreads >= 0 );
if ( lNumArbThrottledThreads > 0 )
{
return false;
}
DWORD dwCurrent = GetTickCount();
// We must deal with the fact that a rollover *can* occur
if ( dwCurrent >= dwLastTick )
{
*pdwAdjust = dwCurrent - dwLastTick;
}
else
{
// Accounts for rollover - 0xFFFFFFFF minus the last tick, plus the current
// plus 1 will give us the number of elapsed ticks
*pdwAdjust = dwCurrent + ( 0xFFFFFFFF - dwLastTick );
}
// If the difference is greater
return ( *pdwAdjust > m_dwProviderDeliveryTimeout );
}
// Sets the proper ping variable and sends it to the main merger
DWORD CMergerThrottling::Ping( bool bParent, CWmiMerger* pWmiMerger )
{
DWORD dwTick = GetTickCount();
if ( bParent )
{
m_dwLastParentPing = dwTick;
}
else
{
m_dwLastChildPing = dwTick;
}
// Sets the ping delivery
pWmiMerger->PingDelivery( dwTick );
return dwTick;
}