|
|
/*++
Copyright (C) 1996-2001 Microsoft Corporation
Module Name:
WMIMERGER.CPP
Abstract:
Implements the _IWmiMerger interface
History:
sanjes 16-Nov-00 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 <scopeguard.h>
static long g_lNumMergers = 0L;
//***************************************************************************
//
//***************************************************************************
//
CWmiMerger::CWmiMerger( CWbemNamespace* pNameSpace ) : m_lRefCount( 0 ), m_pTargetSink( NULL ), m_pTask( NULL ), m_pNamespace( pNameSpace ), m_wsTargetClassName(), m_dwProviderDeliveryPing( 0L ), m_pArbitrator( NULL ), m_lNumArbThrottled( 0L ), m_lDebugMemUsed( 0L ), m_hOperationRes( WBEM_S_NO_ERROR ), m_cs(), m_dwMaxLevel( 0 ), m_pRequestMgr( NULL ), m_dwMinReqLevel( 0xFFFFFFFF ), m_bMergerThrottlingEnabled( true ) { if ( NULL != m_pNamespace ) { m_pNamespace->AddRef(); }
InterlockedIncrement( &g_lNumMergers ); }
//***************************************************************************
//
// CWmiMerger::~CWmiMerger
//
// Notifies the ESS of namespace closure and frees up all the class providers.
//
//***************************************************************************
CWmiMerger::~CWmiMerger() { _DBG_ASSERT( 0L == m_lNumArbThrottled ); _DBG_ASSERT( 0L == m_lDebugMemUsed ); if ( NULL != m_pNamespace ) { m_pNamespace->Release(); }
if ( NULL != m_pArbitrator ) { m_pArbitrator->Release(); }
if ( NULL != m_pTask ) { m_pTask->Release(); }
if ( NULL != m_pRequestMgr ) { delete m_pRequestMgr; m_pRequestMgr = NULL; }
InterlockedDecrement( &g_lNumMergers );
}
//***************************************************************************
//
// CWmiMerger::QueryInterface
//
// Exports _IWmiMerger interface.
//
//***************************************************************************
STDMETHODIMP CWmiMerger::QueryInterface( IN REFIID riid, OUT LPVOID *ppvObj ) { if ( riid == IID__IWmiArbitratee ) { *ppvObj = (_IWmiArbitratee*) this; } else if ( riid == IID__IWmiArbitratedQuery ) { *ppvObj = (_IWmiArbitratedQuery*) this; } else { return E_NOINTERFACE; }
AddRef(); return S_OK; }
//***************************************************************************
//
//***************************************************************************
//
ULONG CWmiMerger::AddRef() { return InterlockedIncrement(&m_lRefCount); }
//***************************************************************************
//
//***************************************************************************
//
ULONG CWmiMerger::Release() { long lNewCount = InterlockedDecrement(&m_lRefCount); if (0 != lNewCount) return lNewCount; delete this; return 0; }
// Sets initial parameters for merger. Establishes the target class and sink for the
// query associated with the merger
STDMETHODIMP CWmiMerger::Initialize( _IWmiArbitrator* pArbitrator, _IWmiCoreHandle* pTask, LPCWSTR pwszTargetClass, IWbemObjectSink* pTargetSink, CMergerSink** ppFinalSink ) { if ( NULL == pwszTargetClass || NULL == pTargetSink ) { return WBEM_E_INVALID_PARAMETER; }
// Cannot initialize twice
if ( NULL != m_pTargetSink ) { return WBEM_E_INVALID_OPERATION; }
HRESULT hr = WBEM_S_NO_ERROR;
try { m_wsTargetClassName = pwszTargetClass; // throws
// Create the final target sink
hr = CreateMergingSink( eMergerFinalSink, pTargetSink, NULL, (CMergerSink**) &m_pTargetSink );
if ( SUCCEEDED( hr ) ) { *ppFinalSink = m_pTargetSink; m_pTargetSink->AddRef();
m_pArbitrator = pArbitrator; m_pArbitrator->AddRef();
// AddRef the Task here
m_pTask = pTask;
// Only register for arbitration if we have a task handle
if ( NULL != pTask ) { m_pTask->AddRef(); hr = m_pArbitrator->RegisterArbitratee( 0L, m_pTask, this ); } } } catch ( CX_Exception & ) { hr = WBEM_E_OUT_OF_MEMORY; } return hr; }
// Called to request a delivery sink for a class in the query chain. The returned
// sink is determined by the specified flags as well as settings on the parent class
STDMETHODIMP CWmiMerger::RegisterSinkForClass( LPCWSTR pwszClass, _IWmiObject* pClass, IWbemContext* pContext, BOOL fHasChildren, BOOL fHasInstances, BOOL fDerivedFromTarget, bool bStatic, CMergerSink* pDestSink, CMergerSink** ppOwnSink, CMergerSink** ppChildSink ) { try { LPCWSTR pwszParentClass = NULL;
DWORD dwSize = NULL; BOOL fIsNull = NULL;
// Get the derivation information. The number of antecedents determines our
// level in the hierarchy (we're 0 based)
HRESULT hr; DWORD dwLevel = 0L; _variant_t vSuperClass;
hr = GetLevelAndSuperClass( pClass, &dwLevel, vSuperClass ); if (FAILED(hr)) return hr;
BSTR wsSuperClass = V_BSTR(&vSuperClass); // it can be NULL if no SuperClass
CCheckedInCritSec ics( &m_cs ); // We're dead - take no positive adjustments
if (FAILED (m_hOperationRes)) return m_hOperationRes;
wmilib::auto_ptr<CWmiMergerRecord> pRecord; pRecord.reset(new CWmiMergerRecord( this, fHasInstances, fHasChildren, pwszClass, pDestSink, dwLevel, bStatic )); // throw
if ( NULL == pRecord.get() ) return WBEM_E_OUT_OF_MEMORY;
// Now attach aninternal merger if we have both instances and children
if ( fHasInstances && fHasChildren ) { // We shouldn't have a NULL task here if this is not a static class.
// Note that the only case this appears to happen is when ESS calls
// into us on internal APIs and uses requests on its own queues and
// not the main Core Queue.
_DBG_ASSERT( NULL != m_pTask || ( NULL == m_pTask && bStatic ) ); // throws
hr = pRecord->AttachInternalMerger( (CWbemClass*) pClass, m_pNamespace, pContext, fDerivedFromTarget, bStatic ); }
// Check that we're still okay
if (FAILED(hr)) return hr;
// Find the record for the superclass if there is one (unless the array is
// empty of course).
if ( wsSuperClass && wsSuperClass[0] && m_MergerRecord.GetSize() > 0 ) { // There MUST be a record, or something is quite not okay.
CWmiMergerRecord* pSuperClassRecord = m_MergerRecord.Find( wsSuperClass );
_DBG_ASSERT( NULL != pSuperClassRecord );
// Now add the new record to the child array for the superclass record
// This will allow us to quickly determine the classes we need to obtain
// submit requests for if the parent class is throttled.
if ( NULL == pSuperClassRecord ) return WBEM_E_FAILED;
hr = pSuperClassRecord->AddChild(pRecord.get()); }
if (FAILED(hr)) return hr;
// Make sure the add is successful
if ( m_MergerRecord.Insert( pRecord.get() ) < 0 ) return WBEM_E_OUT_OF_MEMORY;
#ifdef __DEBUG_MERGER_THROTTLING
// Verify the sort order for now
m_MergerRecord.Verify(); #endif
*ppOwnSink = pRecord->GetOwnSink(); *ppChildSink = pRecord->GetChildSink(); pRecord.release(); // array took ownership
// Store the maximum level in the hierarchy
if ( dwLevel > m_dwMaxLevel ) { m_dwMaxLevel = dwLevel; }
if ( !bStatic && dwLevel < m_dwMinReqLevel ) { m_dwMinReqLevel = dwLevel; }
return hr; } catch(CX_Exception & ) { return WBEM_E_OUT_OF_MEMORY; } }
// Called to request a delivery sink for child classes in the query chain. This is especially
// important when instances are merged under the covers.
STDMETHODIMP CWmiMerger::GetChildSink( LPCWSTR pwszClass, CBasicObjectSink** ppSink ) { HRESULT hr = WBEM_S_NO_ERROR; CInCritSec ics( &m_cs ); // SEC:REVIEWED 2002-03-22 : Assumes entry
// Search for a parent class's child sink
for ( int x = 0; SUCCEEDED( hr ) && x < m_MergerRecord.GetSize(); x++ ) { if ( m_MergerRecord[x]->IsClass( pwszClass ) ) { *ppSink = m_MergerRecord[x]->GetChildSink(); break; } }
// We should never get a failure
_DBG_ASSERT( x < m_MergerRecord.GetSize() );
if ( x >= m_MergerRecord.GetSize() ) { hr = WBEM_E_NOT_FOUND; }
return hr; }
// Can be used to hold off indicates - if we're merging instances from multiple providers, we need
// to ensure that we don't get lopsided in the number of objects we've got queued up for merging.
STDMETHODIMP CWmiMerger::Throttle( void ) { // We're dead - take no positive adjustments
if ( FAILED ( m_hOperationRes ) ) { return m_hOperationRes; }
// Check for NULL m_pTask
HRESULT hr = WBEM_S_NO_ERROR;
if ( NULL != m_pTask ) { hr = m_pArbitrator->Throttle( 0L, m_pTask ); }
return hr; }
// Merger will hold information regarding the total number of objects it has queued up waiting
// for merging and the amount of memory consumed by those objects.
STDMETHODIMP CWmiMerger::GetQueuedObjectInfo( DWORD* pdwNumQueuedObjects, DWORD* pdwQueuedObjectMemSize ) { return WBEM_E_NOT_AVAILABLE; }
// If this is called, all underlying sinks will be cancelled in order to prevent accepting additional
// objects. This will also automatically free up resources consumed by queued objects.
STDMETHODIMP CWmiMerger::Cancel( void ) { return Cancel( WBEM_E_CALL_CANCELLED ); }
// Helper function to control sink creation. The merger is responsible for deletion of
// all internally created sinks. So this function ensures that the sinks are added into
// the array that will destroy them.
HRESULT CWmiMerger::CreateMergingSink( MergerSinkType eType, IWbemObjectSink* pDestSink, CInternalMerger* pMerger, CMergerSink** ppSink ) {
if ( eType == eMergerFinalSink ) { *ppSink = new CMergerTargetSink( this, pDestSink ); if ( NULL == *ppSink ) return WBEM_E_OUT_OF_MEMORY; } else { HRESULT hr; hr = CInternalMerger::CreateMergingSink( eType, pMerger, this, ppSink ); if (FAILED(hr)) return hr; }
// If we have a sink, we should now add it to the
// Sink array, the MergerSinks array will do the operator delete call,
// but the objects will have a special callback on the last release
if ( m_MergerSinks.Add( *ppSink ) < 0 ) { delete *ppSink; *ppSink = NULL; return WBEM_E_OUT_OF_MEMORY; } return WBEM_S_NO_ERROR; }
// Iterates the array of MergerRecords and cancels each of them.
HRESULT CWmiMerger::Cancel( HRESULT hRes ) { #ifdef __DEBUG_MERGER_THROTTLING
DbgPrintfA(0,"CANCEL CALLED: Merger %p Cancelled with hRes: 0x%x on Thread 0x%x\n",this, hRes, GetCurrentThreadId() ); #endif
// We shouldn't be called with a success code
_DBG_ASSERT( FAILED( hRes ) );
HRESULT hr = WBEM_S_NO_ERROR;
// If we're here and this is non-NULL, tell the Arbitrator to tank us.
if ( NULL != m_pTask ) { m_pArbitrator->CancelTask( 0L, m_pTask ); }
CCheckedInCritSec ics( &m_cs ); // SEC:REVIEWED 2002-03-22 : Assumes entry
if ( WBEM_S_NO_ERROR == m_hOperationRes ) // if it is the first time
{ m_hOperationRes = hRes; }
// Search for a parent class's child sink
for ( int x = 0; SUCCEEDED( hr ) && x < m_MergerRecord.GetSize(); x++ ) { m_MergerRecord[x]->Cancel( hRes ); }
// Copy into a temporary variable, clear the member, exit the critsec
// THEN call delete. Requests can have multiple releases, which could call
// back in here and cause all sorts of problems if we're inside a critsec.
CWmiMergerRequestMgr* pReqMgr = m_pRequestMgr; m_pRequestMgr = NULL;
ics.Leave();
// Tank any and all outstanding requests
if ( NULL != pReqMgr ) { delete pReqMgr; }
return hr; }
// Final Shutdown. Called when the target sink is released. At this point, we should
// unregister ourselves from the world
HRESULT CWmiMerger::Shutdown( void ) { HRESULT hr = WBEM_S_NO_ERROR;
CCheckedInCritSec ics( &m_cs ); // SEC:REVIEWED 2002-03-22 : Assumes entry
_IWmiCoreHandle* pTask = m_pTask;
// Done with this, NULL it out - we release and unregister outside the critical section
if ( NULL != m_pTask ) { m_pTask = NULL; }
ics.Leave();
if ( NULL != pTask ) { hr = m_pArbitrator->UnRegisterArbitratee( 0L, pTask, this ); pTask->Release(); }
return hr; }
// Pas-thru to arbitrator
HRESULT CWmiMerger::ReportMemoryUsage( long lAdjustment ) { // Task can be NULL
HRESULT hr = WBEM_S_NO_ERROR;
if ( NULL != m_pTask ) { hr = m_pArbitrator->ReportMemoryUsage( 0L, lAdjustment, m_pTask ); }
// SUCCESS, WBEM_E_ARB_CANCEL or WBEM_E_ARB_THROTTLE means that we need to
// account for the memory
if ( ( SUCCEEDED( hr ) || hr == WBEM_E_ARB_CANCEL || hr == WBEM_E_ARB_THROTTLE ) ) { InterlockedExchangeAdd( &m_lDebugMemUsed, lAdjustment ); }
return hr; }
/* _IWmiArbitratee methods. */
STDMETHODIMP CWmiMerger::SetOperationResult( ULONG uFlags, HRESULT hRes ) { HRESULT hr = WBEM_S_NO_ERROR;
if ( FAILED( hRes ) ) { hr = Cancel( hRes ); }
return hr; }
// Why are we here?
STDMETHODIMP CWmiMerger::SetTaskHandle( _IWmiCoreHandle* pTask ) { _DBG_ASSERT( 0 ); HRESULT hr = WBEM_S_NO_ERROR;
return hr; }
// Noop for now
STDMETHODIMP CWmiMerger::DumpDebugInfo( ULONG uFlags, const BSTR strFile ) { HRESULT hr = WBEM_S_NO_ERROR;
return hr; }
// Returns SUCCESS for now
STDMETHODIMP CWmiMerger::IsMerger( void ) { return WBEM_S_NO_ERROR; }
HRESULT CWmiMerger::GetLevelAndSuperClass( _IWmiObject* pObj, DWORD* pdwLevel, _variant_t & vSuperClass ) { // Get the derivation information. The number of antecedents determines our
// level in the hierarchy (we're 0 based)
DWORD dwTemp = 0L;
HRESULT hr = pObj->GetDerivation( 0L, 0L, pdwLevel, &dwTemp, NULL );
if ( FAILED( hr ) && WBEM_E_BUFFER_TOO_SMALL != hr ) { return hr; }
hr = pObj->Get( L"__SUPERCLASS", 0L, &vSuperClass, NULL, NULL );
if ( SUCCEEDED( hr )) { if ( VT_BSTR == V_VT(&vSuperClass)) return S_OK; if ( VT_NULL == V_VT(&vSuperClass)) { V_BSTR(&vSuperClass) = NULL; return S_OK; }; throw CX_Exception(); } return hr; }
HRESULT CWmiMerger::RegisterArbitratedInstRequest( CWbemObject* pClassDef, long lFlags, IWbemContext* pCtx, CBasicObjectSink* pSink, BOOL bComplexQuery, CWbemNamespace* pNs ) { HRESULT hr = WBEM_S_NO_ERROR;
// Allocate a new request then place it in the arbitrator.
try { wmilib::auto_ptr<CMergerDynReq_DynAux_GetInstances> pReq; pReq.reset(new CMergerDynReq_DynAux_GetInstances(pNs, pClassDef, lFlags, pCtx, pSink));
if (NULL == pReq.get()) return WBEM_E_OUT_OF_MEMORY;
// Make sure a context exists under the cover
if (NULL == pReq->GetContext()) return WBEM_E_OUT_OF_MEMORY;
CCheckedInCritSec ics( &m_cs ); // SEC:REVIEWED 2002-03-22 : Assumes entry
if (FAILED(m_hOperationRes)) return m_hOperationRes;
// Allocate a request manager if we need one
if ( NULL == m_pRequestMgr ) { m_pRequestMgr = new CWmiMergerRequestMgr(this); if (NULL == m_pRequestMgr) return WBEM_E_OUT_OF_MEMORY; }
// We need the record to find out what level we need to add
// the request to
CWmiMergerRecord* pRecord = m_MergerRecord.Find( pReq->GetName() ); _DBG_ASSERT( NULL != pRecord );
if ( NULL == pRecord ) return WBEM_E_FAILED;
// Set the task for the request - we'll just use the existing one
m_pTask->AddRef(); pReq->m_phTask = m_pTask;
hr = m_pRequestMgr->AddRequest( pReq.get(), pRecord->GetLevel() ); // Cleanup the request if anything went wrong
if ( FAILED( hr ) ) return hr; pReq.release(); } catch(CX_Exception &) { ExceptionCounter c; hr = WBEM_E_OUT_OF_MEMORY; }
return hr; }
HRESULT CWmiMerger::RegisterArbitratedQueryRequest( CWbemObject* pClassDef, long lFlags, LPCWSTR Query,LPCWSTR QueryFormat, IWbemContext* pCtx, CBasicObjectSink* pSink, CWbemNamespace* pNs ) { HRESULT hr = WBEM_S_NO_ERROR;
// Allocate a new request then place it in the arbitrator.
try { wmilib::auto_ptr<CMergerDynReq_DynAux_ExecQueryAsync> pReq; pReq.reset(new CMergerDynReq_DynAux_ExecQueryAsync(pNs, pClassDef, lFlags, Query, QueryFormat, pCtx, pSink ));
if (NULL == pReq.get()) return WBEM_E_OUT_OF_MEMORY;
// Make sure a context was properly allocated
if (NULL == pReq->GetContext()) return WBEM_E_OUT_OF_MEMORY;
// Make sure the request is functional
if (FAILED(hr = pReq->Initialize())) return hr;
CInCritSec ics( &m_cs ); // SEC:REVIEWED 2002-03-22 : Assumes entry
if (FAILED(m_hOperationRes)) return m_hOperationRes;
// Allocate a request manager if we need one
if ( NULL == m_pRequestMgr ) { m_pRequestMgr = new CWmiMergerRequestMgr( this ); if ( NULL == m_pRequestMgr ) return WBEM_E_OUT_OF_MEMORY; }
// We need the record to find out what level we need to add
// the request to
CWmiMergerRecord* pRecord = m_MergerRecord.Find( pReq->GetName() ); _DBG_ASSERT( NULL != pRecord ); // Couldn't find the record
if ( NULL == pRecord ) return WBEM_E_FAILED;
// Set the task for the request - we'll just use the existing one
m_pTask->AddRef(); pReq->m_phTask = m_pTask;
hr = m_pRequestMgr->AddRequest( pReq.get(), pRecord->GetLevel() ); if (FAILED(hr)) return hr; pReq.release(); } catch(CX_Exception &) { ExceptionCounter c; hr = WBEM_E_OUT_OF_MEMORY; }
return hr; }
HRESULT CWmiMerger::RegisterArbitratedStaticRequest( CWbemObject* pClassDef, long lFlags, IWbemContext* pCtx, CBasicObjectSink* pSink, CWbemNamespace* pNs, QL_LEVEL_1_RPN_EXPRESSION* pParsedQuery ) { HRESULT hr = WBEM_S_NO_ERROR;
// Allocate a new request then place it in the arbitrator.
try { wmilib::auto_ptr<CMergerDynReq_Static_GetInstances> pReq; pReq.reset(new CMergerDynReq_Static_GetInstances( pNs, pClassDef, lFlags, pCtx, pSink, pParsedQuery )); if ( NULL == pReq.get() ) return WBEM_E_OUT_OF_MEMORY;
// Make sure a context was properly allocated
if ( NULL == pReq->GetContext() ) return WBEM_E_OUT_OF_MEMORY;
CInCritSec ics( &m_cs ); // SEC:REVIEWED 2002-03-22 : Assumes entry
if ( FAILED( m_hOperationRes ) ) return m_hOperationRes;
// Allocate a request manager if we need one
if ( NULL == m_pRequestMgr ) { m_pRequestMgr = new CWmiMergerRequestMgr( this ); if ( NULL == m_pRequestMgr ) return WBEM_E_OUT_OF_MEMORY; }
// We need the record to find out what level we need to add
// the request to
CWmiMergerRecord* pRecord = m_MergerRecord.Find( pReq->GetName() ); _DBG_ASSERT( NULL != pRecord );
// Couldn't find the record
if ( NULL == pRecord ) return WBEM_E_FAILED;
// Set the task for the request - we'll just use the existing one
m_pTask->AddRef(); pReq->m_phTask = m_pTask;
hr = m_pRequestMgr->AddRequest( pReq.get(), pRecord->GetLevel() ); if (FAILED(hr)) return hr; pReq.release();
} catch(CX_Exception &) { ExceptionCounter c; hr = WBEM_E_OUT_OF_MEMORY; }
return hr; }
//
// Executes the parent request. In this case, we simply ask the request manager for the
// next top level request and execute that request. We do this in a loop until something
// goes wrong.
//
HRESULT CWmiMerger::Exec_MergerParentRequest( CWmiMergerRecord* pParentRecord, CBasicObjectSink* pSink ) { HRESULT hr = WBEM_S_NO_ERROR; IWbemClassObject * pErr = NULL; CSetStatusOnMe setOnMe(pSink,hr,pErr);
CCheckedInCritSec ics( &m_cs ); // SEC:REVIEWED 2002-03-22 : Assumes entry
// While we have requests to execute, we should get each next logical one
while ( SUCCEEDED(hr ) && NULL != m_pRequestMgr && m_pRequestMgr->GetNumRequests() > 0 ) { if ( FAILED( m_hOperationRes ) ) { hr = m_hOperationRes; break; }
// Obtain the next topmost parent record if we have to
if ( NULL == pParentRecord ) { WString wsClassName; // throw
hr = m_pRequestMgr->GetTopmostParentReqName( wsClassName );
if ( SUCCEEDED( hr ) ) { pParentRecord = m_MergerRecord.Find( wsClassName );
// If there's a request, there better be a record
_DBG_ASSERT( NULL != pParentRecord );
if ( NULL == pParentRecord ) { hr = WBEM_E_FAILED; }
} // IF Got Topmost Parent Request
} // IF NULL == pParentRecord
if ( FAILED( hr ) ) break;
// This will remove the request from its array and return it
// to us - we need to delete it
wmilib::auto_ptr<CMergerReq> pReq; hr = m_pRequestMgr->RemoveRequest( pParentRecord->GetLevel(), pParentRecord->GetName(), pReq ); if (FAILED(hr)) break; hr = pParentRecord->SetExecutionContext( pReq->GetContext() ); if (FAILED(hr)) break;
// Clearly, we should do this outside the critsec
ics.Leave();
#ifdef __DEBUG_MERGER_THROTTLING
DbgPrintfA(0,"BEGIN: Merger 0x%x querying instances of parent class: %S, Level %d on Thread 0x%x\n", (DWORD_PTR) this, pParentRecord->GetName(), pParentRecord->GetLevel(), GetCurrentThreadId() ); #endif
// This will delete the request when it is done with it
hr = CCoreQueue::ExecSubRequest( pReq.get() );
if ( SUCCEEDED(hr) ) pReq.release(); // queue took ownership
#ifdef __DEBUG_MERGER_THROTTLING
DbgPrintfA(0,"END: Merger 0x%x querying instances of parent class: %S, Level %d on Thread 0x%x\n", (DWORD_PTR) this, pParentRecord->GetName(), pParentRecord->GetLevel(), GetCurrentThreadId() );// SEC:REVIEWED 2002-03-22 : OK
#endif
ics.Enter();
// We're done with this record, so we need to get the next top level
// record.
pParentRecord = NULL; } // SetStatus called by the guard
return hr; }
void CWmiMerger::CleanChildRequests(CWmiMergerRecord* pParentRecord, int cleanFrom) { CCheckedInCritSec ics(&m_cs); if (NULL == m_pRequestMgr) return; // we want to see if there are un-executed requests laying around
int localClean = cleanFrom; while(true) { CWmiMergerRecord* pChildRecord = pParentRecord->GetChildRecord( localClean++ ); if ( NULL == pChildRecord ){ break; } // This will remove the request from its array and return it
// to us - we need to delete it
wmilib::auto_ptr<CMergerReq> pReq; m_pRequestMgr->RemoveRequest( pChildRecord->GetLevel(), pChildRecord->GetName(), pReq ); if (pReq.get() != 0) { ERRORTRACE((LOG_WBEMCORE,"deleting un-executed requests for class %S\n",pReq->GetName())); } } }
//
// Executes the child request. In this case, we enumerate the child classes of the parent
// record, and execute the corresponding requests. We do so in a loop until we either
// finish or something goes wrong.
//
HRESULT CWmiMerger::Exec_MergerChildRequest( CWmiMergerRecord* pParentRecord, CBasicObjectSink* pSink ) { HRESULT hr = WBEM_S_NO_ERROR; IWbemClassObject * pErr = NULL; CSetStatusOnMe setOnMe(pSink,hr,pErr); int cleanFrom = 0; ScopeGuard CleanChildReq = MakeObjGuard(*this, CWmiMerger::CleanChildRequests, pParentRecord, ByRef(cleanFrom)); bool bLast = false;
CCheckedInCritSec ics( &m_cs ); // SEC:REVIEWED 2002-03-22 : Assumes entry
// While we have child requests to execute, we should get each one
for (int x = 0; SUCCEEDED( hr ) && NULL != m_pRequestMgr && !bLast; x++ ) { // m_pRequestMgr will be NULL if we were cancelled, in which
// case m_hOperationRes will be a failure
if (FAILED(m_hOperationRes)){ hr = m_hOperationRes; break; }; CWmiMergerRecord* pChildRecord = pParentRecord->GetChildRecord( x ); if ( NULL == pChildRecord ){ bLast = true; break; }
// This will remove the request from its array and return it
// to us - we need to delete it
wmilib::auto_ptr<CMergerReq> pReq; hr = m_pRequestMgr->RemoveRequest( pChildRecord->GetLevel(), pChildRecord->GetName(), pReq );
if ( WBEM_E_NOT_FOUND == hr ) { // If we don't find the request we're looking for, another thread
// already processed it. We should, however, still look for child
// requests to process before we go away.
hr = WBEM_S_NO_ERROR; continue; } cleanFrom = x+1;
if ( FAILED( hr ) ) break;
hr = pChildRecord->SetExecutionContext(pReq->GetContext()); if (FAILED(hr)) break;
// Clearly, we should do this outside the critsec
ics.Leave();
#ifdef __DEBUG_MERGER_THROTTLING
DbgPrintfA(0,"BEGIN: Merger 0x%x querying instances of child class: %S, Level %d for parent class: %S on Thread 0x%x\n", (DWORD_PTR) this, pChildRecord->GetName(), pChildRecord->GetLevel(), pParentRecord->GetName(), GetCurrentThreadId() ); #endif
// This will delete the request when it is done with it
hr = CCoreQueue::ExecSubRequest( pReq.get() ); if ( SUCCEEDED(hr) ) pReq.release(); // queue took ownership
#ifdef __DEBUG_MERGER_THROTTLING
DbgPrintfA(0,"END: Merger 0x%x querying instances of child class: %S, Level %d for parent class: %S on Thread 0x%x\n", (DWORD_PTR) this, pChildRecord->GetName(), pChildRecord->GetLevel(), pParentRecord->GetName(), GetCurrentThreadId() ); #endif
ics.Enter(); } // FOR enum child requests
// SetStatus invoked by the guard
return hr; }
// Schedules the parent class request
HRESULT CWmiMerger::ScheduleMergerParentRequest( IWbemContext* pCtx ) { // Check if query arbitration is enabled
if ( !ConfigMgr::GetEnableQueryArbitration() ) { return WBEM_S_NO_ERROR; }
CCheckedInCritSec ics( &m_cs ); // SEC:REVIEWED 2002-03-22 : Assumes entry
HRESULT hr = WBEM_S_NO_ERROR;
do {
if (FAILED( m_hOperationRes )){ hr = m_hOperationRes; break; } if ( NULL == m_pRequestMgr ) { break; // The request manager will be non-NULL
// only if we had to add a request.
}
#ifdef __DEBUG_MERGER_THROTTLING
m_pRequestMgr->DumpRequestHierarchy(); #endif
// Make sure we've got at least one request
if ( 0 == m_pRequestMgr->GetNumRequests() ) break; // If there isn't a task, we've got a BIG problem.
_DBG_ASSERT( NULL != m_pTask ); if ( NULL == m_pTask ) { hr = WBEM_E_FAILED; break; } // If we have a single static request in the merger, we'll
// execute it now. Otherwise, we'll do normal processing.
// Note that we *could* theoretically do this for single
// dynamic requests as well
if ( IsSingleStaticRequest() ) { // We MUST leave the critical section here, since the parent request
// could get cancelled or we may end up sleeping and we don't want
// to own the critical section in that time.
ics.Leave(); hr = Exec_MergerParentRequest( NULL, m_pTargetSink ); } else { // If we've never retrieved the number of processors, do so
// now.
static g_dwNumProcessors = 8L;
/*
if ( 0L == g_dwNumProcessors ) { SYSTEM_INFO sysInfo; ZeroMemory( &sysInfo, sizeof( sysInfo ) ); // SEC:REVIEWED 2002-03-22 : OK
GetSystemInfo( &sysInfo );
_DBG_ASSERT( sysInfo.dwNumberOfProcessors > 0L );
// Ensure we're always at least 1
g_dwNumProcessors = ( 0L == sysInfo.dwNumberOfProcessors ? 1L : sysInfo.dwNumberOfProcessors ); } */
// We will generate a number of parent requests based on the minimum
// of the number of requests and the number of actual processors.
DWORD dwNumToSchedule = min( m_pRequestMgr->GetNumRequests(), g_dwNumProcessors );
for ( DWORD dwCtr = 0L; SUCCEEDED( hr ) && dwCtr < dwNumToSchedule; dwCtr++ ) { // Parent request will search for the next available request
wmilib::auto_ptr<CMergerParentReq> pReq; pReq.reset(new CMergerParentReq(this,NULL,m_pNamespace,m_pTargetSink,pCtx));
if ( NULL == pReq.get() ) { hr = WBEM_E_OUT_OF_MEMORY; break; }
if ( NULL == pReq->GetContext() ){ hr = WBEM_E_OUT_OF_MEMORY; break; } // Set the task for the request - we'll just use the existing one
m_pTask->AddRef(); pReq->m_phTask = m_pTask; // This may sleep, so exit the critsec before calling into this
ics.Leave();
hr = ConfigMgr::EnqueueRequest( pReq.get() ); if ( SUCCEEDED(hr) ) pReq.release(); // queue took ownership
// reenter the critsec
ics.Enter(); } // For schedule requests
} // IF !SingleStaticRequest
}while(0);
// If we have to cancel, do so OUTSIDE of the critsec
ics.Leave();
if ( FAILED( hr ) ) { Cancel( hr ); }
return hr; }
// Schedules a child class request
HRESULT CWmiMerger::ScheduleMergerChildRequest( CWmiMergerRecord* pParentRecord ) { // Check if query arbitration is enabled
if (!ConfigMgr::GetEnableQueryArbitration()) return WBEM_S_NO_ERROR;
CCheckedInCritSec ics( &m_cs ); // SEC:REVIEWED 2002-03-22 : Assumes entry
HRESULT hr = WBEM_S_NO_ERROR;
// We must be in a success state and not have previously scheduled a child
// request.
do { if (FAILED(m_hOperationRes)) { hr = m_hOperationRes; break; } if (pParentRecord->ScheduledChildRequest()) { break; // if already scheduled, bail out, with success
}
// If there isn't a task, we've got a BIG problem.
_DBG_ASSERT( NULL != m_pTask ); if ( NULL == m_pTask ) { hr = WBEM_E_FAILED; break; }
wmilib::auto_ptr<CMergerChildReq> pReq; pReq.reset(new CMergerChildReq (this,pParentRecord, m_pNamespace,m_pTargetSink, pParentRecord->GetExecutionContext()));
if (NULL == pReq.get()) { hr = WBEM_E_OUT_OF_MEMORY; break; } if ( NULL == pReq->GetContext()) { hr = WBEM_E_OUT_OF_MEMORY; break; } // Set the task for the request - we'll just use the existing one
m_pTask->AddRef(); pReq->m_phTask = m_pTask;
// This may sleep, so exit the critsec before calling into this
ics.Leave(); hr = ConfigMgr::EnqueueRequest( pReq.get() ); ics.Enter(); if (SUCCEEDED(hr)) { // We've basically scheduled one at this point
pParentRecord->SetScheduledChildRequest(); pReq.release(); } }while(0);
// If we have to cancel, do so OUTSIDE of the critsec
ics.Leave();
if (FAILED(hr)) Cancel(hr);
return hr; }
// Returns whether or not we have a single static class request in the merger
// or not
BOOL CWmiMerger::IsSingleStaticRequest( void ) { CCheckedInCritSec ics( &m_cs ); // SEC:REVIEWED 2002-03-22 : Assumes entry
BOOL fRet = FALSE;
if ( NULL != m_pRequestMgr ) { // Ask if we've got a single request
fRet = m_pRequestMgr->HasSingleStaticRequest(); } // IF NULL != m_pRequestMgr
return fRet; }
//
// CWmiMergerRecord
//
// Support class for CWmiMerger - encapsulates sub-sink functionality for the CWmiMerger
// class. The merger calls the records which actually know whether or not they sit on
// top of sinks or actual mergers.
//
CWmiMergerRecord::CWmiMergerRecord( CWmiMerger* pMerger, BOOL fHasInstances, BOOL fHasChildren, LPCWSTR pwszClass, CMergerSink* pDestSink, DWORD dwLevel, bool bStatic ) : m_pMerger( pMerger ), m_fHasInstances( fHasInstances ), m_fHasChildren( fHasChildren ), m_dwLevel( dwLevel ), m_wsClass( pwszClass ), // throw
m_pDestSink( pDestSink ), m_pInternalMerger( NULL ), m_ChildArray(), m_bScheduledChildRequest( false ), m_pExecutionContext( NULL ), m_bStatic( bStatic ) { // No Addrefing internal sinks, since they really AddRef the entire merger
// and we don't want to create Circular Dependencies
}
CWmiMergerRecord::~CWmiMergerRecord() { if ( NULL != m_pInternalMerger ) { delete m_pInternalMerger; }
if ( NULL != m_pExecutionContext ) { m_pExecutionContext->Release(); } }
HRESULT CWmiMergerRecord::AttachInternalMerger( CWbemClass* pClass, CWbemNamespace* pNamespace, IWbemContext* pCtx, BOOL fDerivedFromTarget, bool bStatic ) { if ( NULL != m_pInternalMerger ) { return WBEM_E_INVALID_OPERATION; }
HRESULT hr = WBEM_S_NO_ERROR;
// m_pDestSink is not addrefed by the MergerRecord
m_pInternalMerger = new CInternalMerger( this, m_pDestSink, pClass, pNamespace, pCtx );
if ( NULL == m_pInternalMerger ) return WBEM_E_OUT_OF_MEMORY;
hr = m_pInternalMerger->Initialize();
if ( FAILED( hr ) ) { delete m_pInternalMerger; m_pInternalMerger = NULL; } else { m_pInternalMerger->SetIsDerivedFromTarget( fDerivedFromTarget ); }
return hr; }
CMergerSink* CWmiMergerRecord::GetChildSink( void ) { CMergerSink* pSink = NULL;
if ( NULL != m_pInternalMerger ) { pSink = m_pInternalMerger->GetChildSink(); } else if ( m_fHasChildren ) { m_pDestSink->AddRef(); // addref-it before giving-it out, but not ref for itself
pSink = m_pDestSink; }
return pSink; }
CMergerSink* CWmiMergerRecord::GetOwnSink( void ) { CMergerSink* pSink = NULL;
if ( NULL != m_pInternalMerger ) { pSink = m_pInternalMerger->GetOwnSink(); } else if ( !m_fHasChildren ) { m_pDestSink->AddRef(); pSink = m_pDestSink; // addref-it before giving-it out, but not ref for itself
}
return pSink; }
CMergerSink* CWmiMergerRecord::GetDestSink( void ) { if ( NULL != m_pDestSink ) { m_pDestSink->AddRef(); } // addref-it before giving-it out, but not ref for itself
CMergerSink* pSink = m_pDestSink;
return pSink; }
void CWmiMergerRecord::Cancel( HRESULT hRes ) { if ( NULL != m_pInternalMerger ) { m_pInternalMerger->Cancel( hRes ); }
}
HRESULT CWmiMergerRecord::AddChild( CWmiMergerRecord* pRecord ) { HRESULT hr = WBEM_S_NO_ERROR;
if ( m_ChildArray.Add( pRecord ) < 0 ) { hr = WBEM_E_OUT_OF_MEMORY; }
return hr; }
CWmiMergerRecord* CWmiMergerRecord::GetChildRecord( int nIndex ) { // Check if the index is a valid record, then return it
if ( nIndex < m_ChildArray.GetSize() ) { return m_ChildArray[nIndex]; }
return NULL; }
HRESULT CWmiMergerRecord::SetExecutionContext( IWbemContext* pContext ) { // We can only do this once
_DBG_ASSERT( NULL == m_pExecutionContext );
if ( NULL != m_pExecutionContext ) { return WBEM_E_INVALID_OPERATION; }
if (pContext) { pContext->AddRef(); m_pExecutionContext = pContext; } else { return WBEM_E_INVALID_PARAMETER; }
return WBEM_S_NO_ERROR; }
|