windows-nt/Source/XPSP1/NT/admin/pchealth/helpctr/shell/proxies/asyncdatabase.cpp
2020-09-26 16:20:57 +08:00

740 lines
19 KiB
C++

/******************************************************************************
Copyright (c) 2000 Microsoft Corporation
Module Name:
AsyncDatabase.cpp
Abstract:
This file contains the implementation of the asynchronous database access.
Revision History:
Davide Massarenti (Dmassare) 08/13/2000
created
******************************************************************************/
#include "stdafx.h"
////////////////////////////////////////////////////////////////////////////////
AsynchronousTaxonomyDatabase::NotifyHandle::NotifyHandle()
{
m_iType = OfflineCache::ET_INVALID; // int m_iType;
// CComBSTR m_bstrID;
//
m_hEvent = NULL; // HANDLE m_hEvent;
//
m_hr = HRESULT_FROM_WIN32(ERROR_BUSY); // HRESULT m_hr;
m_pColl = NULL; // CPCHQueryResultCollection* m_pColl;
}
AsynchronousTaxonomyDatabase::NotifyHandle::~NotifyHandle()
{
Detach();
MPC::Release( m_pColl );
if(m_hEvent)
{
::CloseHandle( m_hEvent );
}
}
STDMETHODIMP_(ULONG) AsynchronousTaxonomyDatabase::NotifyHandle::AddRef()
{
return InternalAddRef();
}
STDMETHODIMP_(ULONG) AsynchronousTaxonomyDatabase::NotifyHandle::Release()
{
ULONG c = InternalRelease();
if(c == 0) delete this;
return c;
}
////////////////////////////////////////
HRESULT AsynchronousTaxonomyDatabase::NotifyHandle::Init()
{
__HCP_FUNC_ENTRY( "AsynchronousTaxonomyDatabase::NotifyHandle::Init" );
HRESULT hr;
MPC::SmartLock<_ThreadModel> lock( this );
__MPC_EXIT_IF_CALL_RETURNS_NULL(hr, (m_hEvent = ::CreateEvent( NULL, FALSE, FALSE, NULL )));
m_fAttached = true;
hr = S_OK;
__HCP_FUNC_CLEANUP;
__HCP_FUNC_EXIT(hr);
}
void AsynchronousTaxonomyDatabase::NotifyHandle::Bind( /*[in]*/ int iType, /*[in]*/ LPCWSTR szID )
{
MPC::SmartLock<_ThreadModel> lock( this );
m_iType = iType;
m_bstrID = szID;
m_hr = HRESULT_FROM_WIN32(ERROR_BUSY);
MPC::Release( m_pColl );
}
void AsynchronousTaxonomyDatabase::NotifyHandle::Call( /*[in]*/ QueryStore* qs )
{
MPC::SmartLock<_ThreadModel> lock( this );
MPC::Release( m_pColl );
m_hr = qs ? qs->GetData( &m_pColl ) : E_ABORT;
//
// Look ahead all the URLs, to Get all the Copy the items in the remote collection.
//
if(m_pColl)
{
long lCount;
long lPos;
if(SUCCEEDED(m_pColl->get_Count( &lCount )))
{
for(lPos=0; lPos<lCount; lPos++)
{
CComPtr<CPCHQueryResult> item;
if(SUCCEEDED(m_pColl->GetItem( lPos, &item )))
{
CComBSTR bstrURL;
if(SUCCEEDED(item->get_TopicURL( &bstrURL )))
{
if(STRINGISPRESENT(bstrURL)) (void)HyperLinks::Lookup::s_GLOBAL->Queue( bstrURL );
}
}
}
}
}
if(m_hEvent)
{
::SetEvent( m_hEvent );
}
}
void AsynchronousTaxonomyDatabase::NotifyHandle::Detach()
{
MPC::SmartLock<_ThreadModel> lock( this );
m_fAttached = false;
}
bool AsynchronousTaxonomyDatabase::NotifyHandle::IsAttached()
{
MPC::SmartLock<_ThreadModel> lock( this );
return m_fAttached;
}
////////////////////////////////////////
HRESULT AsynchronousTaxonomyDatabase::NotifyHandle::GetData( /*[out]*/ CPCHQueryResultCollection* *pColl )
{
MPC::SmartLock<_ThreadModel> lock( this );
if(pColl)
{
*pColl = NULL;
if(SUCCEEDED(m_hr))
{
if(m_pColl) (*pColl = m_pColl)->AddRef();
}
}
return m_hr;
}
HRESULT AsynchronousTaxonomyDatabase::NotifyHandle::Wait( /*[in]*/ DWORD dwTimeout )
{
if(IsAttached())
{
for(int pass=0; pass<2; pass++)
{
DWORD dwRes;
DWORD dwWait;
//
// On first pass, do a synchronous wait.
//
if(pass == 0)
{
dwRes = ::WaitForSingleObject( m_hEvent, min( dwTimeout, 200 ) );
}
else
{
dwRes = MPC::WaitForSingleObject( m_hEvent, dwTimeout );
}
if(dwRes == WAIT_OBJECT_0 ||
dwRes == WAIT_ABANDONED_0 )
{
break;
}
if(pass == 1)
{
return HRESULT_FROM_WIN32(ERROR_BUSY);
}
}
}
return S_OK;
}
////////////////////////////////////////////////////////////////////////////////
AsynchronousTaxonomyDatabase::Notifier::Notifier()
{
// List m_lstCallback;
}
AsynchronousTaxonomyDatabase::Notifier::~Notifier()
{
MPC::ReleaseAll( m_lstCallback );
}
////////////////////
void AsynchronousTaxonomyDatabase::Notifier::Notify( /*[in]*/ QueryStore* qs )
{
List lst;
Iter it;
//
// Phase 1: copy list, under lock.
//
{
MPC::SmartLock<_ThreadModel> lock( this );
for(it = m_lstCallback.begin(); it != m_lstCallback.end(); it++)
{
NotifyHandle* nb = *it;
if(qs)
{
if(qs->m_iType != nb->m_iType || MPC::StrCmp( qs->m_bstrID, nb->m_bstrID )) continue;
}
lst.push_back( nb ); nb->AddRef();
}
}
//
// Phase 2: send the notification.
//
for(it = lst.begin(); it != lst.end(); it++)
{
NotifyHandle* nb = *it;
nb->Call ( qs );
nb->Detach( );
}
MPC::ReleaseAll( lst );
//
// Phase 3: clean list, under lock.
//
{
MPC::SmartLock<_ThreadModel> lock( this );
for(it = m_lstCallback.begin(); it != m_lstCallback.end();)
{
NotifyHandle* nb = *it;
if(nb->IsAttached() == false)
{
m_lstCallback.erase( it ); nb->Release();
it = m_lstCallback.begin();
}
else
{
it++;
}
}
}
}
////////////////////
HRESULT AsynchronousTaxonomyDatabase::Notifier::AddNotification( /*[in]*/ QueryStore* qs, /*[in]*/ NotifyHandle* nb )
{
__HCP_FUNC_ENTRY( "AsynchronousTaxonomyDatabase::Notifier::AddNotification" );
HRESULT hr;
MPC::SmartLock<_ThreadModel> lock( this );
if(nb == NULL)
{
__MPC_SET_ERROR_AND_EXIT(hr, E_OUTOFMEMORY);
}
__MPC_EXIT_IF_METHOD_FAILS(hr, nb->Init());
nb->Bind( qs->m_iType, qs->m_bstrID );
m_lstCallback.push_back( nb ); nb->AddRef();
hr = S_OK;
__HCP_FUNC_CLEANUP;
__HCP_FUNC_EXIT(hr);
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
AsynchronousTaxonomyDatabase::QueryStore::QueryStore( /*[in]*/ int iType, /*[in]*/ LPCWSTR szID, /*[in]*/ VARIANT* option )
{
m_iType = iType; // int m_iType;
m_bstrID = szID; // CComBSTR m_bstrID;
// CComVariant m_vOption;
//
m_fDone = false; // bool m_fDone;
m_hr = E_INVALIDARG; // HRESULT m_hr;
// MPC::CComHGLOBAL m_hgData;
// FILETIME m_dLastUsed;
if(option) m_vOption = *option;
}
AsynchronousTaxonomyDatabase::QueryStore::~QueryStore()
{
Invalidate();
}
////////////////////
bool AsynchronousTaxonomyDatabase::QueryStore::LessThen( /*[in]*/ QueryStore const &qs ) const
{
int iCmp = (m_iType - qs.m_iType);
if(iCmp == 0)
{
iCmp = MPC::StrCmp( m_bstrID, qs.m_bstrID );
if(iCmp == 0)
{
switch(m_iType)
{
case OfflineCache::ET_LOCATECONTEXT:
case OfflineCache::ET_SEARCH :
{
bool fHasLeft = ( m_vOption.vt == VT_BSTR);
bool fHasRight = (qs.m_vOption.vt == VT_BSTR);
if(fHasLeft)
{
iCmp = fHasRight ? MPC::StrCmp( m_vOption.bstrVal, qs.m_vOption.bstrVal ) : 1;
}
else
{
iCmp = fHasRight ? -1 : 0;
}
}
break;
}
}
}
return (iCmp < 0);
}
bool AsynchronousTaxonomyDatabase::QueryStore::NewerThen( /*[in]*/ QueryStore const &qs ) const
{
return ::CompareFileTime( &m_dLastUsed, &qs.m_dLastUsed ) > 0;
}
////////////////////
HRESULT AsynchronousTaxonomyDatabase::QueryStore::Execute( /*[in]*/ OfflineCache::Handle* handle ,
/*[in]*/ CPCHProxy_IPCHTaxonomyDatabase* parent ,
/*[in]*/ bool fForce )
{
__HCP_FUNC_ENTRY( "AsynchronousTaxonomyDatabase::QueryStore::Execute" );
HRESULT hr;
CComPtr<IPCHCollection> pColl;
__MPC_TRY_BEGIN();
if(fForce) m_fDone = false;
if(m_fDone == false)
{
DebugLog( L"AsynchronousTaxonomyDatabase::QueryStore::Execute - Start : %s\n", SAFEBSTR( m_bstrID ) );
DEBUG_AppendPerf( DEBUG_PERF_QUERIES, L"AsynchronousTaxonomyDatabase::QueryStore::Execute - Start : %s", SAFEBSTR( m_bstrID ) );
if(handle)
{
CComPtr<CPCHQueryResultCollection> coll;
if(SUCCEEDED((*handle)->Retrieve( m_bstrID, m_iType, &coll )))
{
DEBUG_AppendPerf( DEBUG_PERF_QUERIES, L"AsynchronousTaxonomyDatabase::QueryStore::Execute - Cache Hit" );
pColl = coll;
}
}
if(!pColl && parent)
{
CComPtr<IPCHTaxonomyDatabase> db;
DEBUG_AppendPerf( DEBUG_PERF_QUERIES, L"AsynchronousTaxonomyDatabase::QueryStore::Execute - Get DB" );
__MPC_EXIT_IF_METHOD_FAILS(hr, parent->EnsureDirectConnection( db ));
DEBUG_AppendPerf( DEBUG_PERF_QUERIES, L"AsynchronousTaxonomyDatabase::QueryStore::Execute - Got DB" );
switch(m_iType)
{
case OfflineCache::ET_NODE : hr = db->LookupNode ( m_bstrID, &pColl ); break;
case OfflineCache::ET_SUBNODES : hr = db->LookupSubNodes ( m_bstrID, VARIANT_FALSE, &pColl ); break;
case OfflineCache::ET_SUBNODES_VISIBLE : hr = db->LookupSubNodes ( m_bstrID, VARIANT_TRUE , &pColl ); break;
case OfflineCache::ET_NODESANDTOPICS : hr = db->LookupNodesAndTopics( m_bstrID, VARIANT_FALSE, &pColl ); break;
case OfflineCache::ET_NODESANDTOPICS_VISIBLE: hr = db->LookupNodesAndTopics( m_bstrID, VARIANT_TRUE , &pColl ); break;
case OfflineCache::ET_TOPICS : hr = db->LookupTopics ( m_bstrID, VARIANT_FALSE, &pColl ); break;
case OfflineCache::ET_TOPICS_VISIBLE : hr = db->LookupTopics ( m_bstrID, VARIANT_TRUE , &pColl ); break;
case OfflineCache::ET_LOCATECONTEXT : hr = db->LocateContext ( m_bstrID, m_vOption , &pColl ); break;
case OfflineCache::ET_SEARCH : hr = db->KeywordSearch ( m_bstrID, m_vOption , &pColl ); break;
case OfflineCache::ET_NODES_RECURSIVE : hr = db->GatherNodes ( m_bstrID, VARIANT_TRUE , &pColl ); break;
case OfflineCache::ET_TOPICS_RECURSIVE : hr = db->GatherTopics ( m_bstrID, VARIANT_TRUE , &pColl ); break;
default : hr = E_INVALIDARG;
}
DEBUG_AppendPerf( DEBUG_PERF_QUERIES, L"AsynchronousTaxonomyDatabase::QueryStore::Execute - Query Done" );
}
}
if(pColl)
{
CComPtr<IPersistStream> persist;
CComPtr<IStream> stream;
DEBUG_AppendPerf( DEBUG_PERF_QUERIES, L"AsynchronousTaxonomyDatabase::QueryStore::Execute - Local Copy Start" );
__MPC_EXIT_IF_METHOD_FAILS(hr, pColl->QueryInterface( IID_IPersistStream, (void**)&persist ));
__MPC_EXIT_IF_METHOD_FAILS(hr, m_hgData.NewStream( &stream ));
__MPC_EXIT_IF_METHOD_FAILS(hr, persist->Save ( stream, FALSE ));
DEBUG_AppendPerf( DEBUG_PERF_QUERIES, L"AsynchronousTaxonomyDatabase::QueryStore::Execute - Local Copy Done" );
}
::GetSystemTimeAsFileTime( &m_dLastUsed );
hr = S_OK;
__HCP_FUNC_CLEANUP;
__MPC_TRY_CATCHALL(hr);
if(m_fDone == false)
{
m_hr = hr;
m_fDone = true;
}
__HCP_FUNC_EXIT(hr);
}
HRESULT AsynchronousTaxonomyDatabase::QueryStore::GetData( /*[out]*/ CPCHQueryResultCollection* *ppC )
{
__HCP_FUNC_ENTRY( "AsynchronousTaxonomyDatabase::QueryStore::GetData" );
HRESULT hr;
CComPtr<CPCHQueryResultCollection> pColl;
CComPtr<IPersistStream> persist;
CComPtr<IStream> stream;
__MPC_PARAMCHECK_BEGIN(hr)
__MPC_PARAMCHECK_POINTER_AND_SET(ppC,NULL);
__MPC_PARAMCHECK_END();
__MPC_EXIT_IF_METHOD_FAILS(hr, MPC::CreateInstance( &pColl ));
__MPC_EXIT_IF_METHOD_FAILS(hr, pColl->QueryInterface( IID_IPersistStream, (void**)&persist ));
__MPC_EXIT_IF_METHOD_FAILS(hr, m_hgData.GetAsStream( &stream, /*fClone*/false ));
__MPC_EXIT_IF_METHOD_FAILS(hr, persist->Load ( stream ));
*ppC = pColl.Detach();
hr = S_OK;
__HCP_FUNC_CLEANUP;
__HCP_FUNC_EXIT(hr);
}
void AsynchronousTaxonomyDatabase::QueryStore::Invalidate()
{
m_hgData.Release();
m_fDone = false;
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
bool AsynchronousTaxonomyDatabase::Engine::CompareQueryStores::operator()( /*[in]*/ const QueryStore *left, /*[in]*/ const QueryStore *right ) const
{
if(left)
{
return right ? left->LessThen( *right ) : true;
}
return false;
}
AsynchronousTaxonomyDatabase::Engine::Engine( /*[in]*/ CPCHProxy_IPCHTaxonomyDatabase* parent )
{
m_parent = parent; // CPCHProxy_IPCHTaxonomyDatabase* m_parent;
// Set m_setQueries;
// Notifier m_notifier;
}
AsynchronousTaxonomyDatabase::Engine::~Engine()
{
Passivate();
}
////////////////////
void AsynchronousTaxonomyDatabase::Engine::Passivate()
{
Thread_Wait();
MPC::CallDestructorForAll( m_setQueries );
}
void AsynchronousTaxonomyDatabase::Engine::RefreshConnection()
{
//
// The connection to the database has changed, so we need to flush all the cached queries...
//
InvalidateQueries();
Thread_Signal();
}
////////////////////////////////////////
bool AsynchronousTaxonomyDatabase::Engine::LookupCache( /*[out]*/ OfflineCache::Handle& handle )
{
if(m_parent && m_parent->Parent())
{
CComPtr<CPCHProxy_IPCHUserSettings2> us;
if(SUCCEEDED(m_parent->Parent()->GetUserSettings2( &us )))
{
if(SUCCEEDED(OfflineCache::Root::s_GLOBAL->Locate( us->THS(), handle )))
{
return true;
}
}
}
return false;
}
void AsynchronousTaxonomyDatabase::Engine::InvalidateQueries()
{
MPC::SmartLock<_ThreadModel> lock( this );
Iter it;
for(it = m_setQueries.begin(); it != m_setQueries.end(); it++)
{
QueryStore* qs = *it;
qs->Invalidate();
}
}
HRESULT AsynchronousTaxonomyDatabase::Engine::Run()
{
__HCP_FUNC_ENTRY( "AsynchronousTaxonomyDatabase::Engine::Run" );
HRESULT hr;
MPC::SmartLock<_ThreadModel> lock( this );
while(Thread_IsAborted() == false)
{
OfflineCache::Handle handle;
bool fValidCache = false;
bool fInit = false;
bool fSleep = true;
Iter it;
//
// Look for the first query store not ready and execute it.
//
for(it = m_setQueries.begin(); it != m_setQueries.end(); it++)
{
QueryStore* qs = *it;
if(qs->m_fDone == false)
{
if(fInit == false)
{
__MPC_PROTECT( fValidCache = LookupCache( handle ) );
fInit = true;
}
lock = NULL;
qs->Execute( fValidCache ? &handle : NULL, m_parent );
lock = this;
fSleep = false;
break;
}
}
//
// Notify all the query stores that are ready.
//
if(it == m_setQueries.end())
{
for(it = m_setQueries.begin(); it != m_setQueries.end(); it++)
{
QueryStore* qs = *it;
if(qs->m_fDone)
{
m_notifier.Notify( qs );
}
}
}
if(fSleep)
{
lock = NULL;
Thread_WaitForEvents( NULL, INFINITE );
lock = this;
}
}
hr = S_OK;
m_notifier.Notify( NULL );
Thread_Abort();
__HCP_FUNC_EXIT(hr);
}
////////////////////////////////////////
HRESULT AsynchronousTaxonomyDatabase::Engine::ExecuteQuery( /*[in]*/ int iType ,
/*[in]*/ LPCWSTR szID ,
/*[in]*/ VARIANT* option ,
/*[in]*/ NotifyHandle* nb )
{
__HCP_FUNC_ENTRY( "AsynchronousTaxonomyDatabase::Engine::ExecuteQuery" );
HRESULT hr;
MPC::SmartLock<_ThreadModel> lock( this );
QueryStore* qsNew = NULL;
QueryStore* qs;
Iter it;
__MPC_PARAMCHECK_BEGIN(hr)
__MPC_PARAMCHECK_NOTNULL(m_parent);
__MPC_PARAMCHECK_NOTNULL(nb);
__MPC_PARAMCHECK_END();
__MPC_EXIT_IF_ALLOC_FAILS(hr, qsNew, new QueryStore( iType, szID, option ));
it = m_setQueries.find( qsNew );
if(it == m_setQueries.end())
{
qs = qsNew;
m_setQueries.insert( qsNew ); qsNew = NULL;
}
else
{
qs = *it;
}
__MPC_EXIT_IF_METHOD_FAILS(hr, m_notifier.AddNotification( qs, nb ));
if(Thread_IsRunning() == false)
{
__MPC_EXIT_IF_METHOD_FAILS(hr, Thread_Start( this, Run, NULL ));
}
else
{
Thread_Signal();
}
hr = S_OK;
__HCP_FUNC_CLEANUP;
delete qsNew;
__HCP_FUNC_EXIT(hr);
}
HRESULT AsynchronousTaxonomyDatabase::Engine::ExecuteQuery( /*[in]*/ int iType ,
/*[in]*/ LPCWSTR szID ,
/*[in]*/ VARIANT* option ,
/*[out, retval]*/ CPCHQueryResultCollection* *ppC )
{
__HCP_FUNC_ENTRY( "AsynchronousTaxonomyDatabase::Engine::ExecuteQuery" );
HRESULT hr;
NotifyHandle* nb = new NotifyHandle(); if(nb) nb->AddRef();
__MPC_EXIT_IF_METHOD_FAILS(hr, ExecuteQuery( iType, szID, option, nb ));
__MPC_EXIT_IF_METHOD_FAILS(hr, nb->Wait());
__MPC_EXIT_IF_METHOD_FAILS(hr, nb->GetData( ppC ));
hr = S_OK;
__HCP_FUNC_CLEANUP;
if(nb) nb->Release();
__HCP_FUNC_EXIT(hr);
}