|
|
/*++
Copyright (C) 1996-2001 Microsoft Corporation
Module Name:
INTERNALMERGER.CPP
Abstract:
CInternalMerger class.
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 "internalmerger.h"
static long g_lNumMergers = 0L;
//***************************************************************************
//
// class CInternalMerger
//
// This class is a 'reverse fork'. It consumes two sinks and outputs
// one. Its purpose is to merge instances of the same key in a given
// dynasty. Each CInternalMerger has two inputs, (a) instances of the class
// in question, (b) instances of from another Merger representing
// instances of subclasses. Given classes A,B:A,C:B, for example,
// where "<--" is a sink:
//
// | own:Instances of A
// <---| | own:Instances of B
// | child: <--------|
// | child:Instances of C
//
//
// The two input sinks for CInternalMerger are <m_pOwnSink> which receives
// instances from the provider for "A", for example, and the <m_pChildSink>
// which receives instances from the underyling Merger.
//
// The mergers operate asynchronously to each other. Therefore,
// the instances for A may arrive in its CInternalMerger sink before instances
// of the child classes have arrived in theirs.
//
// As objects arrive in the owning CInternalMerger for a class, AddOwnObject()
// is called. As objects arrive from a child sink, AddChildObject()
// is called. In either case, if the object with a given key
// arrives for the first time, it is simply added to the map. If
// it is already there (via a key lookup), then a merge is performed
// via CWbemInstance::AsymmetricMerge. Immediately after this merge,
// the object is dispatched up to the next parent sink via the parent's
// AddChildObject and removed from the map.
//
// Note that in a class hierarchy {A,B:A,C:B} an enumeration/query is
// performed only against the classes in the CDynasty referenced in
// the query. This logic occurs in CQueryEngine::EvaluateSubQuery.
// For example, if "select * from B" is the query, only queries
// for B and C are performed. The CInternalMerger logic will do individual
// 'get object' calls for any instances needed in A to complete
// the merged B/C instances while merging is taking place.
//
//***************************************************************************
#pragma warning(disable:4355)
static long g_lNumInternalMergers = 0L;
CInternalMerger::CInternalMerger( CWmiMergerRecord* pWmiMergerRecord, CMergerSink* pDest, CWbemClass* pOwnClass, CWbemNamespace* pNamespace, IWbemContext* pContext ) : m_pDest(pDest), m_bOwnDone(FALSE), m_bChildrenDone(FALSE), m_pNamespace(pNamespace), m_pContext(pContext), m_pOwnClass(pOwnClass), m_bDerivedFromTarget(TRUE), m_lRef(0), m_pSecurity(NULL), m_pWmiMergerRecord( pWmiMergerRecord ), m_pOwnSink( NULL ), m_pChildSink( NULL ), m_hErrorRes( WBEM_S_NO_ERROR ), m_lTotalObjectData( 0L ), m_Throttler() { // We do want to AddRef() in this case, since we will potentially be the only ones holding
// onto the destination sink. In this case, our child and owner sink will AddRef() us. When
// they perform a final release on us, we will release the destination sink. If, on the other
// hand we are outright deleted if this value is non-NULL we will clean up there as well
m_pDest->AddRef();
if(m_pContext) m_pContext->AddRef(); if(m_pNamespace) m_pNamespace->AddRef();
if(m_pOwnClass) { m_pOwnClass->AddRef(); CVar v; if (SUCCEEDED(pOwnClass->GetClassName(&v))) m_wsClass = v.GetLPWSTR(); // delegate Initialzie to check
}
// Retrieve call security. Need to create a copy for use on another thread
// =======================================================================
m_pSecurity = CWbemCallSecurity::MakeInternalCopyOfThread();
// Keep the count up to date
InterlockedIncrement( &g_lNumInternalMergers ); }
//***************************************************************************
//
//***************************************************************************
CInternalMerger::~CInternalMerger() { if ( NULL != m_pDest ) { m_pDest->Release(); m_pDest = NULL; }
// Map should be empty whenever we destruct
_DBG_ASSERT( m_map.size() == 0 ); _DBG_ASSERT( m_lTotalObjectData == 0L );
if(m_pNamespace) m_pNamespace->Release(); if(m_pContext) m_pContext->Release(); if(m_pOwnClass) m_pOwnClass->Release();
if(m_pSecurity) m_pSecurity->Release();
// Keep the count up to date
InterlockedDecrement( &g_lNumInternalMergers ); } //***************************************************************************
//
//***************************************************************************
long CInternalMerger::AddRef() { return InterlockedIncrement(&m_lRef); }
//***************************************************************************
//
//***************************************************************************
long CInternalMerger::Release() { long lRef = InterlockedDecrement(&m_lRef);
// On Final Release, we will clear up the actual destination sink
if(lRef == 0) { // Enter the critical section, save off the sink pointer in a
// temporary variable, set the member to NULL and then release
// the sink. This will prevent reentrancy issues with the merger
// (e.g. during a Cancel).
Enter();
CMergerSink* pSink = m_pDest; m_pDest = NULL;
Leave();
pSink->Release(); } return lRef; }
HRESULT CInternalMerger::Initialize( void ) { if (m_pOwnClass) if (NULL == (WCHAR *)m_wsClass) return WBEM_E_OUT_OF_MEMORY;
HRESULT hr = m_Throttler.Initialize(); if ( SUCCEEDED( hr ) ) { hr = m_pWmiMergerRecord->GetWmiMerger()->CreateMergingSink( eMergerOwnSink, NULL, this, (CMergerSink**) &m_pOwnSink );
if ( SUCCEEDED( hr ) ) { hr = m_pWmiMergerRecord->GetWmiMerger()->CreateMergingSink( eMergerChildSink, NULL, this, (CMergerSink**) &m_pChildSink ); }
} // IF throttler initialized
return hr; }
//***************************************************************************
//
//***************************************************************************
void CInternalMerger::GetKey(IWbemClassObject* pObj, WString& wsKey) { LPWSTR wszRelPath = ((CWbemInstance*)pObj)->GetRelPath(); if (wszRelPath == NULL) { ERRORTRACE((LOG_WBEMCORE, "Object with no path submitted to a merge\n")); wsKey.Empty(); return; }
WCHAR* pwcDot = wcschr(wszRelPath, L'.'); if (pwcDot == NULL) { ERRORTRACE((LOG_WBEMCORE, "Object with invalid path %S submitted to a merge\n", wszRelPath)); wsKey.Empty();
// Clean up the path
delete [] wszRelPath;
return; }
wsKey = pwcDot+1; delete [] wszRelPath; }
//***************************************************************************
//
//***************************************************************************
void CInternalMerger::SetIsDerivedFromTarget(BOOL bIs) { m_bDerivedFromTarget = bIs;
if (!bIs) { // We will need our OwnSink for GetObject calls
// ============================================
m_pOwnSink->AddRef(); } }
//
// The algorithm for reporting memory usage is as follows:
//
// Lowest level indicate (i.e. the one coming in from a provider), will iterate
// all objects sent down by the provider and report them to the arbitrator. At
// the end of processing we will report the negative of this value. Reason for
// this is that we will be holding onto all of these objects for the length of
// the function, and may get throttled at any time.
//
// During processing, we will account for objects added to the map, and removed
// from the map. When we remove objects from the map, we add them to an array
// which we indicate. Usually we merge objects, sometimes we pass the objects
// straight down. We need to account for these objects during the call to
// indicate, so we will total these and report usage BEFORE calling Indicate.
// After Indicate returns we will remove their usage, since we will be releasing
// them and hence no longer care about them.
//
// Except for the case of the lowest level indicate, we will not account for
// pass-through objects - those that are sent in and sent out. It is assumed
// that the calling function has accounted for these objects.
//
// There will be small windows where a single object may get reported multiple
// times. This would occur if we reported a new object prior to indicate,
// then in the call to indicate the merger added to the map, or the finalizer
// added to its list. When the call returns, we will report removal. The object
// may actually get removed on another thread, but if we get throttled, we
// still need to account for it. In tight memory conditions if multiple threads
// cause addition/removal at jus tthe right times and are then throttled, we will
// get stuck sleeping and each could report an object multiple times. However, this
// should only occur in relatively stressful conditions, and should be rare.
//
//***************************************************************************
//
//***************************************************************************
HRESULT CInternalMerger::IndicateArrayAndThrottle( long lObjectCount, CRefedPointerArray<IWbemClassObject>* pObjArray, CWStringArray* pwsKeyArray, long lMapAdjustmentSize, long lNewObjectSize, bool bThrottle, bool bParent, long* plNumIndicated ) { // In this case, we report the size of the objects as they were adjusted in the map
// in addition to new objects we created. The new objects will be released after
// we indicate, so we will account for them post-indicate, since we will no longer
// be holding onto them
HRESULT hRes = ReportMemoryUsage( lMapAdjustmentSize);
// Use scoped memory cleanup to handle the new objects
// Note that in the event of an exception this will cleanup properly
CScopedMemoryUsage scopedMemUsage( this );
if ( SUCCEEDED( hRes ) ) { hRes = scopedMemUsage.ReportMemoryUsage( lNewObjectSize ); }
// If the value is > 0L, and we succeeded, we can go ahead indicate the objects now.
// The refed pointer array should properly clean up.
if ( SUCCEEDED( hRes ) ) { // If we have "own instances" in the array, we need to retrieve those objects.
// Each is retrieved individually
if ( NULL != pwsKeyArray && pwsKeyArray->Size() > 0 ) {
for ( int x = 0; SUCCEEDED( hRes ) && x < pwsKeyArray->Size(); x++ ) { IWbemClassObject* pMergedInstance = NULL; hRes = GetOwnInstance( pwsKeyArray->GetAt( x ), &pMergedInstance ); CReleaseMe rm( pMergedInstance );
// If we retrieved a merged instance at this time, we should place it in
// the array for indicating
if ( SUCCEEDED( hRes ) && NULL != pMergedInstance ) { // Handle object size here. This is a merged object, so we must
// account for it in the size variable
long lObjSize = 0L; hRes = GetObjectLength( pMergedInstance, &lObjSize );
if ( SUCCEEDED( hRes ) ) { if ( pObjArray->Add( pMergedInstance ) < 0L ) { hRes = WBEM_E_OUT_OF_MEMORY; ERRORTRACE((LOG_WBEMCORE, "Add to array failed in IndicateArrayAndThrottle, hresult is 0x%x", hRes)); continue; }
lNewObjectSize += lObjSize;
lObjectCount++;
// Report size now, since each call to GetOwnInstance() may take some time
hRes = scopedMemUsage.ReportMemoryUsage( lObjSize );
} // IF SUCCEEDED( hRes )
} // IF we retrieved an object
} // FOR enum WStringArray
} // IF need to retrieve OWN instances
if ( SUCCEEDED( hRes ) ) { // If we have stuff to indicate, we will trust that indicate to do
// proper throttling. Otherwise, the buck stops here, so we will
// request throttling
if ( lObjectCount > 0L ) { // Not a lowest level indicate
hRes = m_pDest->Indicate( lObjectCount, pObjArray->GetArrayPtr(), false, plNumIndicated ); }
} // IF success after retrieving parent instances
} // IF SUCCESS after reporting memory usage
// Release all Indicated objects here in order to reduce memory overhead in case
// we sleep.
pObjArray->RemoveAll();
// Finally, since we are no longer really responsible for new objects, we
// will report removal to the arbitrator now if appropriate, and catch any
// errors as they come up. We do this manually since we may end up
// throttling for awhile
HRESULT hrTemp = scopedMemUsage.Cleanup();
// If this failed and we previously had a success code, record the
// failure
if ( SUCCEEDED( hRes ) && FAILED( hrTemp ) ) { hRes = hrTemp; }
// Now, if we're *still* successful, and it is appropriate to
// throttle, we should do merger specific throttling now if it
// is enabled.
if ( SUCCEEDED( hRes ) && bThrottle && m_pWmiMergerRecord->GetWmiMerger()->MergerThrottlingEnabled() ) { hRes = m_Throttler.Throttle( bParent, m_pWmiMergerRecord ); }
return hRes; }
//***************************************************************************
//
//***************************************************************************
HRESULT CInternalMerger::AddOwnObjects(long lObjectCount, IWbemClassObject** pObjArray, bool bLowestLevel, long* plNumIndicated ) {
// Ping the throttler
m_Throttler.Ping( true, m_pWmiMergerRecord->GetWmiMerger() );
// On the lowest level indicate, we will walk all of the objects and log them to the arbitrator
// since we will effectively be holding them for the duration of this operation
long lIndicateSize = 0L;
// Use scoped memory cleanup to handle the new objects
// Note that in the event of an exception this will cleanup properly
CScopedMemoryUsage scopedMemUsage( this );
HRESULT hRes = S_OK ; if ( bLowestLevel ) { for ( long lCtr = 0L; lCtr < lObjectCount; lCtr++ ) { lIndicateSize += ((CWbemObject*) pObjArray[lCtr])->GetBlockLength(); }
// If we're going further, we also report the total size of the indicate, since we
// may be sitting on the memory for awhile what with throttling and all.
hRes = scopedMemUsage.ReportMemoryUsage( lIndicateSize ); }
// Used to track dispersion of objects so we can keep the throttler adjusted
// properly
long lNumChildObjAdjust = 0L; long lNumOwnObjAdjust = 0L;
CRefedPointerArray<IWbemClassObject> objArray; long lNumToIndicate = 0L;
// Following variables track the memory size adjustments for the arbitrator and
// batching
long lMapAdjustmentSize = 0L; long lSizeMergedObjects = 0L; long lBatchSize = 0L;
// Scoped for proper cleanup if anything bad happens
CCheckedInCritSec ics( m_Throttler.GetCritSec() ); // SEC:REVIEWED 2002-03-22 : Assumes entry
// If we've been cancelled, then we should bail out.
if ( SUCCEEDED( hRes ) ) { if ( FAILED ( m_hErrorRes ) ) { hRes = m_hErrorRes; } else { // We shouldn't be here if m_bOwnDone is set!
_DBG_ASSERT( !m_bOwnDone ); if ( m_bOwnDone ) { hRes = WBEM_E_INVALID_OPERATION; }
} } // IF still in a success State
try {
for ( long x = 0; SUCCEEDED( hRes ) && x < lObjectCount; x++ ) { // If we've been cancelled, then we should bail out.
// We need to do the check in here, since this loop can
// exit and reeenter the critical section in the middle
// of processing.
if ( FAILED( m_hErrorRes ) ) { hRes = m_hErrorRes; continue; }
IWbemClassObject* pObj = pObjArray[x]; WString wsKey;
// Track the size for batching
lBatchSize += ((CWbemObject*) pObj)->GetBlockLength();
GetKey(pObj, wsKey);
MRGRKEYTOINSTMAPITER it = m_map.find(wsKey); if (it == m_map.end()) { // Not there. Check if there is any hope for children
// ==================================================
if (m_bChildrenDone) { if (m_bDerivedFromTarget) { // We queue up all results for the ensuing indicate into a single batch we will
// send down the line after we exit our critical section. This is especially
// important since we may get blocked during the call to indicate by the
// finalizer.
if ( objArray.Add( pObj ) < 0L ) // SEC:REVIEWED 2002-03-22 : Needs EH
{ hRes = WBEM_E_OUT_OF_MEMORY; ERRORTRACE((LOG_WBEMCORE, "Add to array failed in AddOwnObject, hresult is 0x%x", hRes)); continue; } lNumToIndicate++;
} else { // ignore
} } else { // Insert
CInternalMergerRecord& rRecord = m_map[wsKey]; rRecord.m_pData = (CWbemInstance*) pObj; pObj->AddRef(); rRecord.m_bOwn = TRUE; rRecord.m_dwObjSize = ((CWbemObject*)pObj)->GetBlockLength();
// We just added a parent object to the map, so reflect that in the totals
lNumOwnObjAdjust++;
// Add since we are adding to the map
lMapAdjustmentSize += rRecord.m_dwObjSize;
} } else if(it->second.m_bOwn) { ERRORTRACE((LOG_WBEMCORE, "Provider supplied duplicate instances for key %S\n", wsKey)); } else { // Attempt to merge
// ================
hRes = CWbemInstance::AsymmetricMerge( (CWbemInstance*)pObj, (CWbemInstance*)it->second.m_pData); if(FAILED(hRes)) { ERRORTRACE((LOG_WBEMCORE, "Merge conflict for instances with key %S\n", wsKey)); continue; }
// We queue up all results for the ensuing indicate into a single batch we will
// send down the line after we exit our critical section. This is especially
// important since we may get blocked during the call to indicate by the
// finalizer.
if ( objArray.Add( (IWbemClassObject*) it->second.m_pData ) < 0L ) // SEC:REVIEWED 2002-03-22 : Needs EH
{ hRes = WBEM_E_OUT_OF_MEMORY; ERRORTRACE((LOG_WBEMCORE, "Add to array failed in AddOwnObject, hresult is 0x%x", hRes)); continue; }
// Account for objects we have created/modified on the fly
lSizeMergedObjects += ((CWbemObject*)it->second.m_pData)->GetBlockLength(); lNumToIndicate++;
// Subtract since we are removing from the map
lMapAdjustmentSize -= it->second.m_dwObjSize;
// Tricky
// If Children are done and the DispatchOwnIter happens to be pointing
// at the object we are about to erase, we should point it to the result
// of the call to erase so we won't potentially access released memory
// when DispatchOwn reenters its critical section
bool bSaveErase = false;
if ( m_bChildrenDone ) { bSaveErase = ( m_DispatchOwnIter == it ); }
it->second.m_pData->Release();
if ( bSaveErase ) { m_DispatchOwnIter = m_map.erase(it); } else { m_map.erase(it); }
// We just removed a child object from the map, so reflect that in the totals
lNumChildObjAdjust--;
}
if ( SUCCEEDED( hRes ) ) { // If we have reached a complete batch, or reached the last object we need to
// send stuff down the wire.
if ( m_Throttler.IsCompleteBatch( lBatchSize ) || x == ( lObjectCount - 1 ) ) { // Adjust total object size now. Actual Arbitrator adjustment should occur outside a critical
// section. Note that we may not be in a critical section here, but that would be if something
// happened when trying to retrieve an instance. In that case, we'll be returning an error, so
// the adjustment should be 0L anyway.
_DBG_ASSERT( SUCCEEDED( hRes ) || lMapAdjustmentSize == 0L ); AdjustLocalObjectSize( lMapAdjustmentSize );
if ( SUCCEEDED( hRes ) ) { // Adjust the throttler now.
AdjustThrottle( lNumOwnObjAdjust, lNumChildObjAdjust ); }
// This object is smart enough to recognize if we've already left and not do
// so again, if we have.
ics.Leave();
// Now go ahead and perform the indicate we've been leading ourselves up to
if ( SUCCEEDED( hRes ) ) { hRes = IndicateArrayAndThrottle( lNumToIndicate, &objArray, NULL, lMapAdjustmentSize, lSizeMergedObjects, true, true, // Child
plNumIndicated );
// If we are in a success state and we have not enumerated all objects
// we should reset the size counters and reenter the critical section
if ( SUCCEEDED( hRes ) && x < ( lObjectCount ) - 1 ) { lMapAdjustmentSize = 0L; lSizeMergedObjects = 0L; lBatchSize = 0L; lNumToIndicate = 0L; lNumOwnObjAdjust = 0L; lNumChildObjAdjust = 0L;
ics.Enter(); } }
} // IF we should send the objects out
} // IF we are in a success state
} // FOR enum objects
} catch( CX_MemoryException ) { hRes = WBEM_E_OUT_OF_MEMORY; } catch(...) { ExceptionCounter c; hRes = WBEM_E_CRITICAL_ERROR; }
// Check error one last time
if ( FAILED( m_hErrorRes ) ) { hRes = m_hErrorRes; }
// We may have looped, entered the critical section and exited, so make
// sure we force our way out in case we're about to cancel
ics.Leave();
// If we are the lowest level and no objects made it out of the merger
// we will ask the arbitrator to throttle this call
if ( SUCCEEDED( hRes ) && bLowestLevel && *plNumIndicated == 0L ) { // Since we don't want the fact we're sleeping in the arbitrator to
// cause the merger to cancel operations, we'll increase the count
// of threads throttling, and decrement when we return
m_pWmiMergerRecord->GetWmiMerger()->IncrementArbitratorThrottling();
// If we get an error indicating we were throttled, that is okay
hRes = m_pWmiMergerRecord->GetWmiMerger()->Throttle();
m_pWmiMergerRecord->GetWmiMerger()->DecrementArbitratorThrottling();
if ( WBEM_E_ARB_THROTTLE == hRes || WBEM_S_ARB_NOTHROTTLING == hRes ) { hRes = WBEM_S_NO_ERROR; } }
if ( hRes == WBEM_E_ARB_CANCEL ) { hRes = WBEM_E_CALL_CANCELLED ; } // If we are in a failed state, nothing is going to matter from this point on,
// so tell the merger to cancel all underlying sinks.
if ( FAILED( hRes ) ) { m_pWmiMergerRecord->GetWmiMerger()->Cancel( hRes ); }
return hRes; }
//***************************************************************************
//
//***************************************************************************
HRESULT CInternalMerger::AddChildObjects(long lObjectCount, IWbemClassObject** pObjArray, bool bLowestLevel, long* plNumIndicated ) { HRESULT hRes = S_OK ;
// Ping the throttler
m_Throttler.Ping( false, m_pWmiMergerRecord->GetWmiMerger() );
// On the lowest level indicate, we will walk all of the objects and log them to the arbitrator
// since we will effectively be holding them for the duration of this operation
long lIndicateSize = 0L; long lTotalIndicated = 0L;
// Use scoped memory cleanup to handle the new objects
// Note that in the event of an exception this will cleanup properly
CScopedMemoryUsage scopedMemUsage( this );
if ( bLowestLevel ) { for ( long lCtr = 0L; lCtr < lObjectCount; lCtr++ ) { lIndicateSize += ((CWbemObject*) pObjArray[lCtr])->GetBlockLength(); }
// If we're going further, we also report the total size of the indicate, since we
// may be sitting on the memory for awhile what with throttling and all.
hRes = scopedMemUsage.ReportMemoryUsage( lIndicateSize ); }
// Used to track dispersion of objects so we can keep the throttler adjusted
// properly
long lNumChildObjAdjust = 0L; long lNumOwnObjAdjust = 0L;
CRefedPointerArray<IWbemClassObject> objArray; long lNumToIndicate = 0L;
// Following variables track the memory size adjustments for the arbitrator and
// batching
long lMapAdjustmentSize = 0L; long lSizeMergedObjects = 0L; long lBatchSize = 0L;
// Used to keep track of instance keys we need to retrieve using
// GetOwnInstance
CWStringArray wsOwnInstanceKeyArray; // SEC:REVIEWED 2002-03-22 : Needs EH
// Scoped for proper cleanup if anything bad happens
CCheckedInCritSec ics( m_Throttler.GetCritSec() ); // SEC:REVIEWED 2002-03-22 : Assumes entry
// If we've been cancelled, then we should bail out.
if ( SUCCEEDED( hRes ) ) { if ( FAILED ( m_hErrorRes ) ) { hRes = m_hErrorRes; } else { // We shouldn't be here if m_bChildrenDone is set!
_DBG_ASSERT( !m_bChildrenDone ); if ( m_bChildrenDone ) { hRes = WBEM_E_INVALID_OPERATION; }
} } // IF still in a success State
try { for ( long x = 0; SUCCEEDED( hRes ) && x < lObjectCount; x++ ) {
// If we've been cancelled, then we should bail out.
// We need to do the check in here, since this loop can
// exit and reeenter the critical section in the middle
// of processing.
if ( FAILED( m_hErrorRes ) ) { hRes = m_hErrorRes; continue; }
IWbemClassObject* pObj = pObjArray[x]; // Track the size for batching
lBatchSize += ((CWbemObject*) pObj)->GetBlockLength();
WString wsKey;
GetKey(pObj, wsKey);
MRGRKEYTOINSTMAPITER it = m_map.find(wsKey);
if (it == m_map.end()) { // Check if there is any hope for parent
// =====================================
if(m_bOwnDone) { // BSTR str = NULL;
// pObj->GetObjectText(0, &str);
// The following was commented out because it actually incorrectly logs
// an error if the child provider enumerates when the parent provider
// interprets a query and returns fewer instances. Neither provider is wrong,
// but this error message causes needless worry. In Quasar, we have to fix
// this whole merger thing to be smarter anyway.
//
// ERRORTRACE((LOG_WBEMCORE, "[Chkpt_1] [%S] Orphaned object %S returned by "
// "provider\n", LPWSTR(m_wsClass), str));
// SysFreeString(str);
// m_pDest->Add(pObj);
} else { // insert
CInternalMergerRecord& rRecord = m_map[wsKey]; rRecord.m_pData = (CWbemInstance*)pObj; pObj->AddRef(); rRecord.m_bOwn = FALSE; rRecord.m_dwObjSize = ((CWbemObject*)pObj)->GetBlockLength();
// We just added a child object to the map, so reflect that in the totals
lNumChildObjAdjust++;
// Add since we are adding to the map
lMapAdjustmentSize += rRecord.m_dwObjSize;
// Check if parent's retrieval is needed
// =====================================
if (!m_bDerivedFromTarget) {
// Add the instance name to the key array. We will perform retrieval
// of these parent instances *outside* of our critical section
if ( wsOwnInstanceKeyArray.Add( wsKey ) != CFlexArray::no_error ) // SEC:REVIEWED 2002-03-22 : Needs EH
{ hRes = WBEM_E_OUT_OF_MEMORY; }
} // IF !m_bDerivedFromTarget
} } else if(!it->second.m_bOwn) { ERRORTRACE((LOG_WBEMCORE, "Two providers supplied conflicting " "instances for key %S\n", wsKey)); } else { // Attempt to merge
// ================
hRes = CWbemInstance::AsymmetricMerge( (CWbemInstance*)it->second.m_pData, (CWbemInstance*)pObj ); if (FAILED(hRes)) { ERRORTRACE((LOG_WBEMCORE, "Merge conflict for instances with " "key %S\n", wsKey)); continue; }
// We queue up all results for the ensuing indicate into a single batch we will
// send down the line after we exit our critical section. This is especially
// important since we may get blocked during the call to indicate by the
// finalizer.
if ( objArray.Add( pObj ) < 0L ) // SEC:REVIEWED 2002-03-22 : Needs EH
{ hRes = WBEM_E_OUT_OF_MEMORY; ERRORTRACE((LOG_WBEMCORE, "Add to array failed in AddChildObject, hresult is 0x%x", hRes)); continue; }
// Account for objects we have created on the fly
lSizeMergedObjects += ((CWbemObject*) pObj)->GetBlockLength(); lNumToIndicate++;
// Subtract since we are removing from the map
lMapAdjustmentSize -= it->second.m_dwObjSize;
it->second.m_pData->Release(); m_map.erase(it);
// We just removed a parent object from the map, so reflect that in the totals
lNumOwnObjAdjust--;
}
if ( SUCCEEDED( hRes ) ) { // If we have reached a complete batch, or reached the last object we need to
// send stuff down the wire.
if ( m_Throttler.IsCompleteBatch( lBatchSize ) || x == ( lObjectCount - 1 ) ) { // Adjust total object size now. Actual Arbitrator adjustment should occur outside a critical
// section. Note that we may not be in a critical section here, but that would be if something
// happened when trying to retrieve an instance. In that case, we'll be returning an error, so
// the adjustment should be 0L anyway.
_DBG_ASSERT( SUCCEEDED( hRes ) || lMapAdjustmentSize == 0L ); AdjustLocalObjectSize( lMapAdjustmentSize );
if ( SUCCEEDED( hRes ) ) { // Adjust the throttler now.
AdjustThrottle( lNumOwnObjAdjust, lNumChildObjAdjust ); }
// This object is smart enough to recognize if we've already left and not do
// so again, if we have.
ics.Leave();
// Now go ahead and perform the indicate we've been leading ourselves up to
if ( SUCCEEDED( hRes ) ) { hRes = IndicateArrayAndThrottle( lNumToIndicate, &objArray, &wsOwnInstanceKeyArray, lMapAdjustmentSize, lSizeMergedObjects, true, false, // Child
plNumIndicated );
// If we are in a success state and we have not enumerated all objects
// we should reset the size counters and reenter the critical section
if ( SUCCEEDED( hRes ) && x < ( lObjectCount ) - 1 ) { lMapAdjustmentSize = 0L; lSizeMergedObjects = 0L; lBatchSize = 0L; lNumToIndicate = 0L; lNumOwnObjAdjust = 0L; lNumChildObjAdjust = 0L;
// Clear out the array
wsOwnInstanceKeyArray.Empty();
ics.Enter(); } }
} // IF we should send the objects out
} // IF we are in a success state
} // FOR Enum Objects
} catch( CX_MemoryException ) { hRes = WBEM_E_OUT_OF_MEMORY; } catch(...) { ExceptionCounter c; hRes = WBEM_E_CRITICAL_ERROR; }
// Check error one last time
if ( FAILED( m_hErrorRes ) ) { hRes = m_hErrorRes; }
// We may have looped, entered the critical section and exited, so make
// sure we force our way out in case we're about to cancel
ics.Leave();
// If we are the lowest level and no objects made it out of the merger
// we will ask the arbitrator to throttle this call
if ( SUCCEEDED( hRes ) && bLowestLevel && *plNumIndicated == 0L ) { // Since we don't want the fact we're sleeping in the arbitrator to
// cause the merger to cancel operations, we'll increase the count
// of threads throttling, and decrement when we return
m_pWmiMergerRecord->GetWmiMerger()->IncrementArbitratorThrottling();
// If we get an error indicating we were throttled, that is okay
hRes = m_pWmiMergerRecord->GetWmiMerger()->Throttle();
m_pWmiMergerRecord->GetWmiMerger()->DecrementArbitratorThrottling();
if ( WBEM_E_ARB_THROTTLE == hRes || WBEM_S_ARB_NOTHROTTLING == hRes ) { hRes = WBEM_S_NO_ERROR; } }
if ( hRes == WBEM_E_ARB_CANCEL ) { hRes = WBEM_E_CALL_CANCELLED ; }
// If we are in a failed state, nothing is going to matter from this point on,
// so tell the merger to cancel all underlying sinks.
if ( FAILED( hRes ) ) { m_pWmiMergerRecord->GetWmiMerger()->Cancel( hRes ); }
return hRes; }
//***************************************************************************
//
//***************************************************************************
HRESULT CInternalMerger::AddOwnInstance( IWbemClassObject* pObj, LPCWSTR pwszTargetPath, IWbemClassObject** ppMergedInstance) { HRESULT hRes = S_OK ; WString wsKey;
// Ping the throttler
m_Throttler.Ping( true, m_pWmiMergerRecord->GetWmiMerger() );
// Scoped for proper cleanup if anything bad happens
CCheckedInCritSec ics( m_Throttler.GetCritSec() ); // SEC:REVIEWED 2002-03-22 : Assumes entry
// If we've been cancelled, then we should bail out.
if ( FAILED( m_hErrorRes ) ) { hRes = m_hErrorRes; }
GetKey(pObj, wsKey);
// Find the instance - it should already be in the map. If not, we shouldn't be
// here
long lArbitratorAdjust = 0L; long lNumChildObjAdjust = 0L;
MRGRKEYTOINSTMAPITER it = m_map.find(wsKey); if (it != m_map.end()) { // Attempt to merge
// ================
hRes = CWbemInstance::AsymmetricMerge( (CWbemInstance*)pObj, (CWbemInstance*)it->second.m_pData);
if ( SUCCEEDED( hRes ) ) { *ppMergedInstance = (IWbemClassObject*) it->second.m_pData; (*ppMergedInstance)->AddRef();
// Subtract since we are removing from the map
lArbitratorAdjust -= it->second.m_dwObjSize;
it->second.m_pData->Release(); m_map.erase(it);
// We just removed a child object from the map, so reflect that in the totals
lNumChildObjAdjust--; } else { ERRORTRACE((LOG_WBEMCORE, "Merge conflict for instances with " "key %S\n", wsKey)); }
} else { BSTR str = NULL; pObj->GetObjectText(0, &str); CSysFreeMe sfm( str );
// The provider has indicated an improper instance to an OwnInstance request.
// We should always be able to find an instance in here. We'll toss the instance
// but we should output something to the error log since it sounds like
// we have a broken provider
//
ERRORTRACE((LOG_WBEMCORE, "Provider responded to request for instance %S, with object %S not in map\n", pwszTargetPath, str )); }
// Adjust total object size now. Actual Arbitrator adjustment should occur outside a critical
// section
AdjustLocalObjectSize( lArbitratorAdjust );
if ( SUCCEEDED( hRes ) ) { // Adjust the throttler now.
AdjustThrottle( 0L, lNumChildObjAdjust ); }
// This object is smart enough to recognize if we've already left and not do
// so again, if we have.
ics.Leave();
// Always report adjustments
hRes = ReportMemoryUsage( lArbitratorAdjust );
// If we are in a failed state, nothing is going to matter from this point on,
// so tell the merger to cancel all underlying sinks.
if ( FAILED( hRes ) ) { m_pWmiMergerRecord->GetWmiMerger()->Cancel( hRes ); }
return hRes; }
//***************************************************************************
//
//***************************************************************************
HRESULT CInternalMerger::RemoveInstance( LPCWSTR pwszTargetPath ) { HRESULT hRes = S_OK ; WString wsKey;
// Track what we clean up
long lNumChildObjAdjust = 0L; long lArbitratorAdjust = 0L;
// Scoped for proper cleanup if anything bad happens
CCheckedInCritSec ics( m_Throttler.GetCritSec() ); // SEC:REVIEWED 2002-03-22 : Assumes entry
// If we've been cancelled, then we should bail out.
if ( FAILED( m_hErrorRes ) ) { hRes = m_hErrorRes; }
// If the instance path is in our map, we should remove it
MRGRKEYTOINSTMAPITER it = m_map.find( pwszTargetPath ); if (it != m_map.end()) { // Subtract since we are removing from the map
lArbitratorAdjust -= it->second.m_dwObjSize;
it->second.m_pData->Release(); m_map.erase(it);
// We just removed a child object from the map, so reflect that in the totals
lNumChildObjAdjust--; } // Adjust total object size now. Actual Arbitrator adjustment should occur outside a critical
// section
AdjustLocalObjectSize( lArbitratorAdjust );
if ( SUCCEEDED( hRes ) ) { // Adjust the throttler now.
AdjustThrottle( 0L, lNumChildObjAdjust ); }
// This object is smart enough to recognize if we've already left and not do
// so again, if we have.
ics.Leave();
// Always report adjustments (this should be negative).
hRes = ReportMemoryUsage( lArbitratorAdjust );
// If we are in a failed state, nothing is going to matter from this point on,
// so tell the merger to cancel all underlying sinks.
if ( FAILED( hRes ) ) { m_pWmiMergerRecord->GetWmiMerger()->Cancel( hRes ); }
return hRes; }
//***************************************************************************
//
//***************************************************************************
void CInternalMerger::DispatchChildren() { long lNumChildObjAdjust = 0L; long lArbitratorAdjust = 0L;
// Scoped for proper cleanup if anything bad happens
CCheckedInCritSec ics( m_Throttler.GetCritSec() ); // SEC:REVIEWED 2002-03-22 : Assumes entry
MRGRKEYTOINSTMAPITER it = m_map.begin();
while (it != m_map.end()) { if (!it->second.m_bOwn) { // BSTR str = NULL;
// it->second.m_pData->GetObjectText(0, &str);
// The following was commented out because it actually incorrectly logs
// an error if the child provider enumerates when the parent provider
// interprets a query and returns fewer instances. Neither provider is wrong,
// but this error message causes needless worry. In Quasar, we have to fix
// this whole merger thing to be smarter anyway.
//
// ERRORTRACE((LOG_WBEMCORE, "Chkpt2 [%S] Orphaned object %S returned by "
// "provider\n", LPWSTR(m_wsClass), str));
// SysFreeString(str);
// m_pDest->Add(it->second.m_pData);
// Subtract since we are removing from the map
lArbitratorAdjust -= it->second.m_dwObjSize;
// Tricky
// If Children are done and the DispatchOwnIter happens to be pointing
// at the object we are about to erase, we should point it to the result
// of the call to erase so we won't potentially access released memory
// when DispatchOwn reenters its critical section
bool bSaveErase = false;
if ( m_bChildrenDone ) { bSaveErase = ( m_DispatchOwnIter == it ); }
it->second.m_pData->Release(); it = m_map.erase(it);
if ( bSaveErase ) { m_DispatchOwnIter = it; }
// We are removing child objects, so we need to adjust the throttling
// totals appropriately
lNumChildObjAdjust--; } else it++; }
// Adjust total object size now. Actual Arbitrator adjustment should occur outside a critical
// section
AdjustLocalObjectSize( lArbitratorAdjust );
// Apply the adjustment now.
m_Throttler.AdjustNumChildObjects( lNumChildObjAdjust );
// Mark the appropriate flags now this will also release throttling
OwnIsDone();
ics.Leave();
// Always report adjustments
HRESULT hrArbitrate = ReportMemoryUsage( lArbitratorAdjust );
// If we get a failure we're over, the function will filter out noise, such as
// requests to throttle (we're should actually *always* decrease the value here)
if ( FAILED( hrArbitrate ) ) { m_pWmiMergerRecord->GetWmiMerger()->Cancel( hrArbitrate ); }
}
//***************************************************************************
//
//***************************************************************************
void CInternalMerger::DispatchOwn() {
HRESULT hRes = S_OK; long lNumOwnObjAdjust = 0L;
// Temporary object storage
CRefedPointerArray<IWbemClassObject> objArray;
// Used for tracking object sizes
long lTotalMapAdjust = 0L;
// Scoped for proper cleanup if anything bad happens
CCheckedInCritSec ics( m_Throttler.GetCritSec() ); // SEC:REVIEWED 2002-03-22 : Assumes entry
// Mark the appropriate flags now this will also release throttling
ChildrenAreDone();
try {
// Walk the map, and for all own objects, store the pointer and record the size, then
// clear the element from the map. None of these objects should be merged, but we want
// them out of the map before we start batch processing them
// We use a member variable since we may leave a critical section while iterating
// and it is possible for AddOwnObject or Cancel to cause the iterator to be cleared
// before we reenter the critical section - This is the only please we have code
// like this
m_DispatchOwnIter = m_map.begin(); int x = 0; int y = 0;
while ( SUCCEEDED( hRes ) && m_DispatchOwnIter != m_map.end()) { // If we are not derived from the target class, this more or less
// just cleans up the array. Otherwise, instances left in the map
// are instances provided at this level, but not by children, so we
// need to send them up the line with an Indicate
if(m_DispatchOwnIter->second.m_bOwn) { IWbemClassObject* pObjToIndicate = NULL; long lMapAdjust = 0L;
// If we are not derived from the target class, this more or less
// just cleans up the array. Otherwise, instances left in the map
// are instances provided at this level, but not by children, so we
// need to send them up the line with an Indicate
if ( m_bDerivedFromTarget ) { // We will actually just go one object at a time.
if ( objArray.Add( m_DispatchOwnIter->second.m_pData ) < 0L ) // SEC:REVIEWED 2002-03-22 : Needs EH
{ hRes = WBEM_E_OUT_OF_MEMORY; ERRORTRACE((LOG_WBEMCORE, "Add to array failed in AddChildObject, hresult is 0x%x", hRes)); continue; }
lMapAdjust -= m_DispatchOwnIter->second.m_dwObjSize;
// Store the actual adjustment size
} // IF m_bDerivedFromTarget
// Adjust the total map size (this is in case we are not derived from
// target and are just removing objects).
lTotalMapAdjust -= m_DispatchOwnIter->second.m_dwObjSize;
m_DispatchOwnIter->second.m_pData->Release(); m_DispatchOwnIter = m_map.erase(m_DispatchOwnIter);
// Apply the object adjustment now.
m_Throttler.AdjustNumParentObjects( -1 );
if ( objArray.GetSize() > 0L ) { // Adjust total object size now. Actual Arbitrator adjustment should occur outside a critical
// section
AdjustLocalObjectSize( lMapAdjust );
// This object is smart enough to recognize if we've already left and not do
// so again, if we have.
ics.Leave();
// Now go ahead and perform the indicate we've been leading ourselves up to, adjustment
// will be negative, but conversely, we'll be holding onto that size of objects if anything
// is indicated, so call as follows...oh yeah, and don't throttle explicitly
if ( SUCCEEDED( hRes ) ) { hRes = IndicateArrayAndThrottle( 1, &objArray, NULL, lMapAdjust, -lMapAdjust, false, false, // no throttling here
NULL );
// If we are in a success state we should reenter the critical section
if ( SUCCEEDED( hRes ) ) { ics.Enter(); } }
} // IF NULL != pObjToIndicate
} // IF !m_bOwn
else { m_DispatchOwnIter++; }
} // WHILE enuming objects
} // try
catch(...) { ExceptionCounter c; }
if ( !m_bDerivedFromTarget ) { // Adjust total object size now. Actual Arbitrator adjustment should occur outside a critical
// section.
AdjustLocalObjectSize( lTotalMapAdjust ); }
// Any further code MUST be executed outside of a critical
// section
ics.Leave();
if ( !m_bDerivedFromTarget ) { // Report to the arbitrator now.
if ( 0L != lTotalMapAdjust ) { ReportMemoryUsage( lTotalMapAdjust ); } }
// If something went wrong, we need to cancel at this point.
if ( FAILED( hRes ) ) { // If we are in a failed state, nothing is going to matter from this point on,
// so tell the merger to cancel all underlying sinks.
m_pWmiMergerRecord->GetWmiMerger()->Cancel( hRes );
}
}
//***************************************************************************
//
//***************************************************************************
HRESULT CInternalMerger::GetOwnInstance(LPCWSTR wszKey, IWbemClassObject** ppMergedInstance) { HRESULT hRes = WBEM_S_NO_ERROR;
if (NULL == wszKey) return WBEM_E_OUT_OF_MEMORY;
size_t tmpLength = wcslen(wszKey) + m_wsClass.Length() + 2; // SEC:REVIEWED 2002-03-22 : OK ; precondition that these are valid
WCHAR * wszPath = new WCHAR[tmpLength]; if (NULL == wszPath) return WBEM_E_OUT_OF_MEMORY; CVectorDeleteMe<WCHAR> dm(wszPath);
if (wcslen(wszKey)) // SEC:REVIEWED 2002-03-22 : OK; precondition that this is valid
{ StringCchPrintf(wszPath, tmpLength, L"%s.%s", (LPCWSTR)m_wsClass, wszKey);
IServerSecurity * pSec = NULL; hRes = CoGetCallContext(IID_IServerSecurity,(void **)&pSec); CReleaseMe rmSec(pSec); if (RPC_E_CALL_COMPLETE == hRes ) hRes = S_OK; // no call context
if (FAILED(hRes)) return hRes; BOOL bImper = (pSec)?pSec->IsImpersonating():FALSE; if (pSec && bImper && FAILED(hRes = pSec->RevertToSelf())) return hRes; COwnInstanceSink* pOwnInstanceSink = NULL;
hRes = m_pWmiMergerRecord->GetWmiMerger()->CreateMergingSink( eMergerOwnInstanceSink, NULL, this, (CMergerSink**) &pOwnInstanceSink );
if ( SUCCEEDED( hRes ) ) { // Scoped release
pOwnInstanceSink->AddRef(); CReleaseMe rm( pOwnInstanceSink );
hRes = pOwnInstanceSink->SetInstancePath( wszKey );
if ( SUCCEEDED( hRes ) ) { // Impersonate original client
IUnknown* pOld; hRes = CoSwitchCallContext(m_pSecurity, &pOld); if (SUCCEEDED(hRes)) { { // Revert to self this will succeed is the first has succeeded
IUnknown* pThis; OnDelete2<IUnknown *,IUnknown **,HRESULT(*)(IUnknown *,IUnknown **),CoSwitchCallContext> SwitchBack(pOld, &pThis); hRes = m_pNamespace->DynAux_GetSingleInstance(m_pOwnClass, 0, wszPath, m_pContext, pOwnInstanceSink); // throw
}
if ( SUCCEEDED( hRes ) ) { hRes = pOwnInstanceSink->GetObject( ppMergedInstance );
// Means there was no object to retrieve
if ( WBEM_S_FALSE == hRes ) { hRes = WBEM_S_NO_ERROR; } } else if ( WBEM_E_NOT_FOUND == hRes ) { // In this case, this is really not an error
hRes = WBEM_S_NO_ERROR; } } } } // If created sink
if (bImper && pSec) { HRESULT hrInner = pSec->ImpersonateClient(); if (FAILED(hrInner)) return hrInner; } }
return hRes; }
//***************************************************************************
//
//***************************************************************************
void CInternalMerger::OwnIsDone() { // Let the throttler know what's up
m_Throttler.SetParentDone();
m_bOwnDone = TRUE; m_pOwnSink = NULL; }
//***************************************************************************
//
//***************************************************************************
void CInternalMerger::ChildrenAreDone() { // Let the throttler know what's up
m_Throttler.SetChildrenDone();
m_bChildrenDone = TRUE; m_pChildSink = NULL;
if(!m_bDerivedFromTarget) { // Don't need that ref count on pOwnSink anymore
// =============================================
m_pOwnSink->Release(); } }
//***************************************************************************
//
//***************************************************************************
HRESULT CInternalMerger::GetObjectLength( IWbemClassObject* pObj, long* plObjectSize ) { _IWmiObject* pWmiObject = NULL;
HRESULT hr = pObj->QueryInterface( IID__IWmiObject, (void**) &pWmiObject );
if ( SUCCEEDED( hr ) ) { CReleaseMe rm1( pWmiObject ); CWbemObject* pWbemObj = NULL;
hr = pWmiObject->_GetCoreInfo( 0L, (void**) &pWbemObj );
if ( SUCCEEDED( hr ) ) { CReleaseMe rm2( (IWbemClassObject*) pWbemObj ); *plObjectSize = pWbemObj->GetBlockLength(); } }
return hr; }
//***************************************************************************
//
//***************************************************************************
//
ULONG CInternalMerger::CMemberSink::AddRef() { // We keep an internal ref count and also pass up to the
// merger
// On first reference we AddRef() the internal merger as well
// Note that our internal ref count is really a bookkeeping count on
// the sink. The actual ref count that controls the destruction is
// that on the merger. When the merger hits zero the sink will be deleted
if ( InterlockedIncrement( &m_lRefCount ) == 1 ) { m_pInternalMerger->AddRef(); }
return m_pMerger->AddRef(); }
//***************************************************************************
//
//***************************************************************************
STDMETHODIMP CInternalMerger::CMemberSink:: SetStatus(long lFlags, long lParam, BSTR strParam, IWbemClassObject* pObjParam) {
if(lFlags == 0 && lParam == WBEM_E_NOT_FOUND) lParam = WBEM_S_NO_ERROR;
// Propagate error to error combining sink
// =======================================
HRESULT hRes = m_pInternalMerger->m_pDest->SetStatus(lFlags, lParam, strParam, pObjParam);
if ( FAILED ( hRes ) || !SUCCEEDED( lParam ) ) { HRESULT hrSet = ( FAILED( hRes ) ? hRes : lParam ); m_pInternalMerger->m_pWmiMergerRecord->GetWmiMerger()->Cancel( hrSet ); }
return hRes; }
//***************************************************************************
//
//***************************************************************************
CInternalMerger::COwnSink::~COwnSink() { }
//***************************************************************************
//
//***************************************************************************
STDMETHODIMP CInternalMerger::COwnSink::Indicate(long lNumObjects, IWbemClassObject** apObjects) { long lNumIndicated = 0L;
// Internal calls don't use this, so we know we're the lowest level
return m_pInternalMerger->AddOwnObjects( lNumObjects, apObjects, true, &lNumIndicated ); }
//***************************************************************************
//
//***************************************************************************
HRESULT CInternalMerger::COwnSink::Indicate(long lObjectCount, IWbemClassObject** pObjArray, bool bLowestLevel, long* plNumIndicated ) { // Really just a place holder here. Just call the standard version
return m_pInternalMerger->AddOwnObjects( lObjectCount, pObjArray, bLowestLevel, plNumIndicated ); }
//***************************************************************************
//
//***************************************************************************
HRESULT CInternalMerger::COwnSink::OnFinalRelease( void ) { // Final cleanup occurs here.
m_pInternalMerger->DispatchChildren(); m_pInternalMerger->Release();
return WBEM_S_NO_ERROR; }
//***************************************************************************
//
//***************************************************************************
CInternalMerger::CChildSink::~CChildSink() { }
//***************************************************************************
//
//***************************************************************************
STDMETHODIMP CInternalMerger::CChildSink::Indicate(long lNumObjects, IWbemClassObject** apObjects) { long lNumIndicated = 0L;
// Internal calls don't use this, so we know we're the lowest level
return m_pInternalMerger->AddChildObjects( lNumObjects, apObjects, true, &lNumIndicated ); }
//***************************************************************************
//
//***************************************************************************
HRESULT CInternalMerger::CChildSink::Indicate(long lObjectCount, IWbemClassObject** pObjArray, bool bLowestLevel, long* plNumIndicated ) { // Pass the lowest level parameter on
return m_pInternalMerger->AddChildObjects( lObjectCount, pObjArray, bLowestLevel, plNumIndicated ); }
//***************************************************************************
//
//***************************************************************************
HRESULT CInternalMerger::CChildSink::OnFinalRelease( void ) { // Final cleanup occurs here.
m_pInternalMerger->DispatchOwn(); m_pInternalMerger->Release();
return WBEM_S_NO_ERROR;
}
//***************************************************************************
//
//***************************************************************************
HRESULT CInternalMerger::CreateMergingSink( MergerSinkType eType, CInternalMerger* pMerger, CWmiMerger* pWmiMerger, CMergerSink** ppSink ) { if ( eType == eMergerOwnSink ) { *ppSink = new COwnSink( pMerger, pWmiMerger ); } else if ( eType == eMergerChildSink ) { *ppSink = new CChildSink( pMerger, pWmiMerger ); } else if ( eType == eMergerOwnInstanceSink ) { *ppSink = new COwnInstanceSink( pMerger, pWmiMerger ); }
return ( NULL == *ppSink ? WBEM_E_OUT_OF_MEMORY : WBEM_S_NO_ERROR ); }
// Sets our error state, and cleans up objects we're holding onto -
// no further objects should get in. When we cancel the throttler,
// it will release any threads it is holding onto.
void CInternalMerger::Cancel( HRESULT hRes /* = WBEM_E_CALL_CANCELLED */ ) { long lArbitratorAdjust = 0L;
// Scoped for proper cleanup if anything bad happens
CCheckedInCritSec ics( m_Throttler.GetCritSec() ); // SEC:REVIEWED 2002-03-22 : Assumes entry
// Only cancel if we're not already cancelled
if ( WBEM_S_NO_ERROR == m_hErrorRes ) { m_hErrorRes = hRes;
// Dump the map
MRGRKEYTOINSTMAPITER it = m_map.begin();
while ( it != m_map.end()) { // Subtract since we are removing from the map
lArbitratorAdjust -= it->second.m_dwObjSize;
// Inform the arbitrator of the removed object size
it->second.m_pData->Release(); it = m_map.erase(it); } // WHILE dumping map
// Adjust total object size now. Actual Arbitrator adjustment should occur outside a critical
// section
AdjustLocalObjectSize( lArbitratorAdjust );
// This will prevent DispatchOwn() from continuing with a now bogus
// iteration
m_DispatchOwnIter = m_map.end();
m_Throttler.Cancel();
ics.Leave();
// Always report adjustments
HRESULT hrArbitrate = ReportMemoryUsage( lArbitratorAdjust );
// No sense reporting errors here, since we've just told the arbitrator to cancel anyway
} // IF not already cancelled
}
HRESULT CInternalMerger::ReportMemoryUsage( long lMemUsage ) { // Always report adjustments
HRESULT hRes = m_pWmiMergerRecord->GetWmiMerger()->ReportMemoryUsage( lMemUsage );
// An indication we *should* throttle is not considered an error for purposes
// of this function.
if ( WBEM_E_ARB_THROTTLE == hRes || WBEM_S_ARB_NOTHROTTLING == hRes ) { hRes = WBEM_S_NO_ERROR; }
return hRes; }
//***************************************************************************
//
// CMergerSink::QueryInterface
//
// Exports IWbemOnjectSink interface.
//
//***************************************************************************
STDMETHODIMP CMergerSink::QueryInterface( IN REFIID riid, OUT LPVOID *ppvObj ) { *ppvObj = 0;
if (IID_IUnknown==riid || IID_IWbemObjectSink==riid) { *ppvObj = (IWbemObjectSink*)this; AddRef(); return S_OK; } return E_NOINTERFACE; }
//***************************************************************************
//
//***************************************************************************
//
ULONG CMergerSink::AddRef() { // We keep an internal ref count and also pass up to the
// merger
InterlockedIncrement( &m_lRefCount );
return m_pMerger->AddRef(); }
//***************************************************************************
//
//***************************************************************************
//
ULONG CMergerSink::Release() { // We keep an internal ref count and also pass up to the
// merger
long lRef = InterlockedDecrement( &m_lRefCount );
// Ref Count should never go below 0L
_DBG_ASSERT( lRef >= 0 );
// If we are at the final release for the sink, we will perform cleanup.
// otherwise, the sink is more or less dead and just waiting for the WMI
// Merger object to be destructed so we get cleaned up.
if ( lRef == 0 ) { OnFinalRelease(); }
return m_pMerger->Release(); }
CMergerTargetSink::CMergerTargetSink( CWmiMerger* pMerger, IWbemObjectSink* pDest ) : CMergerSink( pMerger ), m_pDest( pDest ) { if ( NULL != m_pDest ) { m_pDest->AddRef(); } }
CMergerTargetSink::~CMergerTargetSink() { if ( NULL != m_pDest ) { m_pDest->Release(); } }
HRESULT STDMETHODCALLTYPE CMergerTargetSink::Indicate(long lObjectCount, IWbemClassObject** pObjArray) { // Since we don't want the fact we're sleeping in the arbitrator to
// cause the merger to cancel operations, we'll increase the count
// of threads throttling, and decrement when we return.
// We do this here because the call to Indicate goes outside the scope
// of the merger, and this call may end up throttling.
m_pMerger->IncrementArbitratorThrottling();
HRESULT hr = m_pDest->Indicate( lObjectCount, pObjArray );
m_pMerger->DecrementArbitratorThrottling();
return hr; }
HRESULT STDMETHODCALLTYPE CMergerTargetSink::SetStatus( long lFlags, long lParam, BSTR strParam, IWbemClassObject* pObjParam ) { return m_pDest->SetStatus( lFlags, lParam, strParam, pObjParam ); }
HRESULT CMergerTargetSink::Indicate(long lObjectCount, IWbemClassObject** pObjArray, bool bLowestLevel, long* plNumIndicated ) { // Well, we're indicating this number of objects, aren't we?
if ( NULL != plNumIndicated ) { *plNumIndicated = lObjectCount; }
// Really just a place holder here. Just call the standard version
return Indicate( lObjectCount, pObjArray ); }
HRESULT CMergerTargetSink::OnFinalRelease( void ) { // This is where we will send the actual status *and* tell the merger we're done
return m_pMerger->Shutdown(); }
long g_lNumMergerSinks = 0L;
CMergerSink::CMergerSink( CWmiMerger* pMerger ) : m_pMerger( pMerger ), m_lRefCount( 0L ) { InterlockedIncrement( &g_lNumMergerSinks ); }
CMergerSink::~CMergerSink( void ) { InterlockedDecrement( &g_lNumMergerSinks ); }
// OwnInstance Sink
CInternalMerger::COwnInstanceSink::~COwnInstanceSink() { if ( NULL != m_pMergedInstance ) { m_pMergedInstance->Release(); } }
// Called in response to a request for GetObject(). In this case, there should be only
// one object indicated. Additionally, it should match the requested path.
HRESULT STDMETHODCALLTYPE CInternalMerger::COwnInstanceSink::Indicate(long lObjectCount, IWbemClassObject** pObjArray ) { HRESULT hRes = WBEM_S_NO_ERROR;
if ( lObjectCount > 0L ) { CCheckedInCritSec ics( &m_cs ); // SEC:REVIEWED 2002-03-22 : Assumes entry
// Only do this if we don't have a merged instance
if ( NULL == m_pMergedInstance ) { if ( !m_bTriedRetrieve ) { // This call doesn't throttle, so don't worry about crit secs here
for ( long x = 0; SUCCEEDED( hRes ) && x < lObjectCount; x++ ) { hRes = m_pInternalMerger->AddOwnInstance( pObjArray[x], m_wsInstPath, &m_pMergedInstance ); }
// Record the final status if we need to
if ( FAILED( hRes ) ) { SetFinalStatus( hRes ); }
} else { // The following call can and will throttle, so do it
// outside of our critical section
ics.Leave();
// Clearly a lowest level indicate
hRes = m_pInternalMerger->AddOwnObjects( lObjectCount, pObjArray, true, NULL );
// We beefed - reflect this in the final status
if ( FAILED( hRes ) ) { ics.Enter(); SetFinalStatus( hRes ); } } } else { hRes = WBEM_E_INVALID_OPERATION; } }
return hRes; }
HRESULT STDMETHODCALLTYPE CInternalMerger::COwnInstanceSink::SetStatus( long lFlags, long lParam, BSTR strParam, IWbemClassObject* pObjParam ) { HRESULT hr = WBEM_S_NO_ERROR;
// If we got a complete, remove the instance if it was never merged
if ( lFlags == WBEM_STATUS_COMPLETE ) { CCheckedInCritSec ics( &m_cs ); // SEC:REVIEWED 2002-03-22 : Assumes entry
if ( SUCCEEDED( lParam ) ) { if ( NULL == m_pMergedInstance ) { hr = m_pInternalMerger->RemoveInstance( m_wsInstPath );
// If we tanked here, we are so busted.
if ( FAILED( hr ) ) { lParam = hr; }
} // IF NULL == m_pMergedInstance
} else { // Remove the Instance now as well
hr = m_pInternalMerger->RemoveInstance( m_wsInstPath );
// If we tanked here, we are so busted.
if ( FAILED( hr ) ) { lParam = hr; }
// We should record the final status if it is not WBEM_E_NOT_FOUND
if ( WBEM_E_NOT_FOUND != lParam ) { SetFinalStatus( lParam ); }
// If we got a failure status, axe the instance now
if ( NULL != m_pMergedInstance ) { m_pMergedInstance->Release(); m_pMergedInstance = NULL; } }
ics.Leave();
// Always pass down to the base class
hr = CMemberSink::SetStatus( lFlags, lParam, strParam, pObjParam );
} else { // Always pass down to the base class
hr = CMemberSink::SetStatus( lFlags, lParam, strParam, pObjParam ); }
return hr; }
HRESULT CInternalMerger::COwnInstanceSink::Indicate(long lObjectCount, IWbemClassObject** pObjArray, bool bLowestLevel, long* plNumIndicated ) { // This should Never be called
_DBG_ASSERT( 0 ); return WBEM_E_INVALID_OPERATION; }
HRESULT CInternalMerger::COwnInstanceSink::SetInstancePath( LPCWSTR pwszPath ) { HRESULT hRes = WBEM_S_NO_ERROR;
try { m_wsInstPath = pwszPath; } catch( CX_MemoryException ) { hRes = WBEM_E_OUT_OF_MEMORY; } catch(...) { ExceptionCounter c; hRes = WBEM_E_FAILED; }
return hRes; }
HRESULT CInternalMerger::COwnInstanceSink::GetObject( IWbemClassObject** ppMergedInst ) { HRESULT hRes = WBEM_S_NO_ERROR;
CInCritSec ics( &m_cs ); // SEC:REVIEWED 2002-03-22 : Assumes entry
// If final status on this sink shows a failure, then we should return that failure
// mostly cause we're doomed anyway at this point
if ( SUCCEEDED( m_hFinalStatus ) ) { if ( NULL != m_pMergedInstance ) { m_pMergedInstance->AddRef(); *ppMergedInst = m_pMergedInstance; } else { hRes = WBEM_S_FALSE; }
} else { hRes = m_hFinalStatus; }
// We tried to retrieve it once - so if further indicates come in,
// they will be passed off to AddOwnObjects
m_bTriedRetrieve = true;
return hRes; }
HRESULT CInternalMerger::COwnInstanceSink::OnFinalRelease( void ) { // Wer should clean this up
m_pInternalMerger->Release();
return WBEM_S_NO_ERROR; }
// Only reports negative memory usage if the original number was positive
CInternalMerger::CScopedMemoryUsage::~CScopedMemoryUsage( void ) { Cleanup(); }
// Reports memory usage and accounts for errors as deemed appropriate
HRESULT CInternalMerger::CScopedMemoryUsage::ReportMemoryUsage( long lMemUsage ) { _DBG_ASSERT( m_lMemUsage >= 0L );
HRESULT hr = m_pInternalMerger->ReportMemoryUsage( lMemUsage );
// If we get a suceess code or WBEM_E_ARB_CANCEL, we need to cleanup
// the memory usage when we go out of scope.
if ( ( SUCCEEDED( hr ) || hr == WBEM_E_ARB_CANCEL ) ) { m_lMemUsage += lMemUsage; m_bCleanup = true; }
return hr; }
// Cleans up any memory usage as we deemed appropriate
HRESULT CInternalMerger::CScopedMemoryUsage::Cleanup( void ) { _DBG_ASSERT( m_lMemUsage >= 0L );
HRESULT hr = WBEM_S_NO_ERROR;
// Cleanup as appropriate
if ( m_bCleanup && m_lMemUsage > 0L ) { hr = m_pInternalMerger->ReportMemoryUsage( -m_lMemUsage ); }
m_bCleanup = false; m_lMemUsage = 0L;
return hr; }
|