/****************************************************************************** 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 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 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 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 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 persist; CComPtr 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 pColl; CComPtr persist; CComPtr 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 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); }