/****************************************************************************** Copyright (c) 2000 Microsoft Corporation Module Name: Session.cpp Abstract: This file contains the implementation of the JetBlue::Session* classes. Revision History: Davide Massarenti (Dmassare) 05/17/2000 created ******************************************************************************/ #include //////////////////////////////////////////////////////////////////////////////// JetBlue::Session::Session( /*[in]*/ SessionPool* parent , /*[in]*/ JET_INSTANCE inst ) { m_parent = parent; // SessionPool* m_parent; m_inst = inst; // JET_INSTANCE m_inst; m_sesid = JET_sesidNil; // JET_SESID m_sesid; // DbMap m_mapDBs; m_dwTransactionNesting = 0; // DWORD m_dwTransactionNesting; m_fAborted = false; // bool m_fAborted; } JetBlue::Session::~Session() { (void)Close( true ); } //////////////////////////////////////// bool JetBlue::Session::LockDatabase ( /*[in]*/ const MPC::string& strDB, /*[in]*/ bool fReadOnly ) { return m_parent->LockDatabase ( this, strDB, fReadOnly ); } void JetBlue::Session::UnlockDatabase ( /*[in]*/ const MPC::string& strDB ) { m_parent->UnlockDatabase ( this, strDB ); } HRESULT JetBlue::Session::ReleaseDatabase( /*[in]*/ const MPC::string& strDB ) { return m_parent->ReleaseDatabase( strDB.c_str() ); } //////////////////////////////////////// HRESULT JetBlue::Session::Init() { __HCP_FUNC_ENTRY( "JetBlue::Session::Init" ); HRESULT hr; if(m_sesid == JET_sesidNil) { __MPC_EXIT_IF_JET_FAILS(hr, ::JetBeginSession( m_inst, &m_sesid, NULL, NULL )); } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT JetBlue::Session::Close( /*[in]*/ bool fForce ) { __HCP_FUNC_ENTRY( "JetBlue::Session::Close" ); HRESULT hr; DbIter it; for(it = m_mapDBs.begin(); it != m_mapDBs.end(); it++) { Database* db = it->second; if(db) { HRESULT hr2 = db->Close( fForce ); if(!fForce) __MPC_EXIT_IF_METHOD_FAILS(hr, hr2); delete db; } } m_mapDBs.clear(); if(m_sesid != JET_sesidNil) { JET_ERR err = ::JetEndSession( m_sesid, 0 ); if(!fForce) __MPC_EXIT_IF_JET_FAILS(hr, err); m_sesid = JET_sesidNil; } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } void JetBlue::Session::Release() { if(m_fAborted) { DbIter it; for(it = m_mapDBs.begin(); it != m_mapDBs.end(); it++) { Database* db = it->second; if(db) { db->Close( /*fForce*/true, /*fAll*/false ); } } __MPC_JET__MTSAFE_NORESULT(m_sesid, ::JetRollback( m_sesid, JET_bitRollbackAll )); m_fAborted = false; } } //////////////////////////////////////// HRESULT JetBlue::Session::GetDatabase( /*[in] */ LPCSTR szName , /*[out]*/ Database*& db , /*[in]*/ bool fReadOnly , /*[in]*/ bool fCreate , /*[in]*/ bool fRepair ) { __HCP_FUNC_ENTRY( "JetBlue::Session::GetDatabase" ); HRESULT hr; Database* dbNew = NULL; DbIter it; db = NULL; it = m_mapDBs.find( szName ); if(it == m_mapDBs.end()) { __MPC_EXIT_IF_ALLOC_FAILS(hr, dbNew, new Database( this, m_sesid, szName )); m_mapDBs[szName] = dbNew; db = dbNew; dbNew = NULL; } else { db = it->second; } __MPC_EXIT_IF_METHOD_FAILS(hr, db->Open( fReadOnly, fCreate, fRepair )); hr = S_OK; __HCP_FUNC_CLEANUP; if(dbNew) delete dbNew; __HCP_FUNC_EXIT(hr); } //////////////////////////////////////// HRESULT JetBlue::Session::BeginTransaction() { __HCP_FUNC_ENTRY( "JetBlue::Session::BeginTransaction" ); HRESULT hr; __MPC_EXIT_IF_JET_FAILS__MTSAFE(m_sesid, hr, ::JetBeginTransaction( m_sesid )); m_dwTransactionNesting++; hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT JetBlue::Session::CommitTransaction() { __HCP_FUNC_ENTRY( "JetBlue::Session::CommitTransaction" ); HRESULT hr; if(m_dwTransactionNesting > 0) { __MPC_EXIT_IF_JET_FAILS__MTSAFE(m_sesid, hr, ::JetCommitTransaction( m_sesid, JET_bitCommitLazyFlush )); m_dwTransactionNesting--; } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT JetBlue::Session::RollbackTransaction() { __HCP_FUNC_ENTRY( "JetBlue::Session::RollbackTransaction" ); HRESULT hr; if(m_dwTransactionNesting > 0) { m_fAborted = true; __MPC_EXIT_IF_JET_FAILS__MTSAFE(m_sesid, hr, ::JetRollback( m_sesid, 0 )); m_dwTransactionNesting--; } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } //////////////////////////////////////// JetBlue::Database* JetBlue::Session::GetDB( /*[in]*/ int iPos ) { for(DbIter it = m_mapDBs.begin(); it != m_mapDBs.end(); it++) { if(iPos-- == 0) return it->second; } return NULL; } JetBlue::Database* JetBlue::Session::GetDB( LPCSTR szDB ) { DbIter it = m_mapDBs.find( szDB ); return (it == m_mapDBs.end()) ? NULL : it->second; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// JetBlue::TransactionHandle::TransactionHandle() { m_sess = NULL; // Session* m_sess; } JetBlue::TransactionHandle::~TransactionHandle() { (void)Rollback(); } HRESULT JetBlue::TransactionHandle::Begin( /*[in]*/ Session* sess ) { __HCP_FUNC_ENTRY( "JetBlue::TransactionHandle::Begin" ); HRESULT hr; __MPC_EXIT_IF_METHOD_FAILS(hr, Rollback()); if(sess) { __MPC_EXIT_IF_METHOD_FAILS(hr, sess->BeginTransaction()); m_sess = sess; } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT JetBlue::TransactionHandle::Commit() { __HCP_FUNC_ENTRY( "JetBlue::TransactionHandle::Commit" ); HRESULT hr; if(m_sess) { __MPC_EXIT_IF_METHOD_FAILS(hr, m_sess->CommitTransaction()); m_sess = NULL; } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT JetBlue::TransactionHandle::Rollback() { __HCP_FUNC_ENTRY( "JetBlue::TransactionHandle::Rollback" ); HRESULT hr; if(m_sess) { __MPC_EXIT_IF_METHOD_FAILS(hr, m_sess->RollbackTransaction()); m_sess = NULL; } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// JetBlue::SessionHandle::SessionHandle() { m_pool = NULL; // SessionPool* m_pool; m_sess = NULL; // Session* m_sess; } JetBlue::SessionHandle::~SessionHandle() { Release(); } //////////////////////////////////////// void JetBlue::SessionHandle::Release() { if(m_pool) { m_pool->ReleaseSession( m_sess ); m_pool = NULL; } m_sess = NULL; } void JetBlue::SessionHandle::Init( /*[in]*/ SessionPool* pool , /*[in]*/ Session* sess ) { Release(); m_pool = pool; // SessionPool* m_pool; m_sess = sess; // Session* m_sess; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// JetBlue::SessionPool::SessionPool() { m_fInitialized = false; // bool m_fInitialized; m_inst = JET_instanceNil; // JET_INSTANCE m_inst; // SessionList m_lstSessions; // DbInUseList m_lstDbInUse; m_iAllocated = 0; // int m_iAllocated; m_iInUse = 0; // int m_iInUse; (void)MPC::_MPC_Module.RegisterCallback( this, (void (JetBlue::SessionPool::*)())Shutdown ); } JetBlue::SessionPool::~SessionPool() { MPC::_MPC_Module.UnregisterCallback( this ); Shutdown(); } //////////////////// JetBlue::SessionPool* JetBlue::SessionPool::s_GLOBAL( NULL ); HRESULT JetBlue::SessionPool::InitializeSystem() { if(s_GLOBAL == NULL) { s_GLOBAL = new JetBlue::SessionPool; } return s_GLOBAL ? S_OK : E_OUTOFMEMORY; } void JetBlue::SessionPool::FinalizeSystem() { if(s_GLOBAL) { delete s_GLOBAL; s_GLOBAL = NULL; } } //////////////////// void JetBlue::SessionPool::Shutdown() { (void)Close( true ); } //////////////////////////////////////// void JetBlue::SessionPool::ReleaseSession( /*[in]*/ Session* sess ) { MPC::SmartLock<_ThreadModel> lock( this ); SessionIter it; for(it = m_lstSessions.begin(); it != m_lstSessions.end(); it++) { if(it->m_sess == sess) { sess->Release(); it->m_fInUse = false; m_iInUse--; if(m_iAllocated - m_iInUse > l_MaxFreePoolSize) { m_lstSessions.erase( it ); m_iAllocated--; } break; } } } bool JetBlue::SessionPool::LockDatabase( /*[in]*/ Session* sess, /*[in]*/ const MPC::string& strDB, /*[in]*/ bool fReadOnly ) { MPC::SmartLock<_ThreadModel> lock( this ); DbInUseIter it; bool fLockedForRead = false; bool fLockedForWrite = false; for(it = m_lstDbInUse.begin(); it != m_lstDbInUse.end(); it++) { DatabaseInUse& db = *it; if(db.m_strDB == strDB) { if(db.m_fReadOnly) fLockedForRead = true; else fLockedForWrite = true; } } if(fLockedForRead && !fReadOnly) return false; // Someone has the database opened in read-only mode... if(fLockedForWrite ) fReadOnly = false; // The database is already opened for writing, so do the same. for(it = m_lstDbInUse.begin(); it != m_lstDbInUse.end(); it++) { DatabaseInUse& db = *it; if(db.m_sess == sess && db.m_strDB == strDB ) { return true; // Already locked. } } // // Create new entry. // it = m_lstDbInUse.insert( m_lstDbInUse.end() ); it->m_sess = sess; it->m_strDB = strDB; it->m_fReadOnly = fReadOnly; return true; } void JetBlue::SessionPool::UnlockDatabase( /*[in]*/ Session* sess, /*[in]*/ const MPC::string& strDB ) { MPC::SmartLock<_ThreadModel> lock( this ); DbInUseIter it = m_lstDbInUse.begin(); bool fInUse = false; bool fSeen = false; while(it != m_lstDbInUse.end()) { DbInUseIter it2 = it++; // Copy iterator and move to the next one. This protects us from node removal. DatabaseInUse& db = *it2; if(db.m_strDB == strDB) { fSeen = true; if(db.m_sess == sess) { m_lstDbInUse.erase( it2 ); } else { fInUse = true; } } } // // Last session to release the database, detach from it. // if(fSeen && !fInUse) { (void)::JetDetachDatabase( sess->GetSESID(), strDB.c_str() ); } } HRESULT JetBlue::SessionPool::ReleaseDatabase( /*[in]*/ LPCSTR szDB ) { __HCP_FUNC_ENTRY( "JetBlue::SessionPool::ReleaseDatabase" ); HRESULT hr; MPC::SmartLock<_ThreadModel> lock ( this ); MPC::string strDB( szDB ); DbInUseIter it; for(it = m_lstDbInUse.begin(); it != m_lstDbInUse.end();) { DatabaseInUse& db = *it; if(db.m_strDB == strDB) { SessionIter it2; for(it2 = m_lstSessions.begin(); it2 != m_lstSessions.end(); it2++) { if(it2->m_sess == db.m_sess) break; } if(it2 != m_lstSessions.end()) { if(it2->m_fInUse) { __MPC_SET_WIN32_ERROR_AND_EXIT(hr, ERROR_BUSY); } m_lstSessions.erase( it2 ); m_iAllocated--; // // The list of databases in use has been changed by the delete operator. // it = m_lstDbInUse.begin(); continue; } } it++; } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } //////////////////////////////////////// HRESULT JetBlue::SessionPool::Init( /*[in]*/ LPCWSTR szLogs ) { __HCP_FUNC_ENTRY( "JetBlue::SessionPool::Init" ); USES_CONVERSION; HRESULT hr; MPC::SmartLock<_ThreadModel> lock( this ); if(m_fInitialized == false) { static const JET_SETSYSPARAM sConfig_Normal[] = { //unsigned long paramid , ULONG_PTR lParam, const char *sz, JET_ERR err #ifdef DEBUG { JET_paramAssertAction , JET_AssertMsgBox, NULL , JET_errSuccess }, #endif { JET_paramSystemPath , 0 , (LPSTR)-1 , JET_errSuccess }, { JET_paramTempPath , 0 , (LPSTR)-1 , JET_errSuccess }, { JET_paramLogFilePath , 0 , (LPSTR)-1 , JET_errSuccess }, { JET_paramBaseName , 0 , "edb" , JET_errSuccess }, { JET_paramEventSource , 0 , "HelpSvc.exe" , JET_errSuccess }, { JET_paramNoInformationEvent, 1 , NULL , JET_errSuccess }, { JET_paramGlobalMinVerPages , 1 , NULL , JET_errSuccess }, { JET_paramMaxVerPages , 1024 , NULL , JET_errSuccess }, { JET_paramCacheSizeMax , 1024 , NULL , JET_errSuccess }, //// { JET_paramLogFileSize , 128 , NULL , JET_errSuccess }, //// { JET_paramCircularLog , 1 , NULL , JET_errSuccess }, { -1 } }; static const JET_SETSYSPARAM sConfig_LargeSet[] = { //unsigned long paramid , ULONG_PTR lParam, const char *sz, JET_ERR err #ifdef DEBUG { JET_paramAssertAction , JET_AssertMsgBox, NULL , JET_errSuccess }, #endif { JET_paramSystemPath , 0 , (LPSTR)-1 , JET_errSuccess }, { JET_paramTempPath , 0 , (LPSTR)-1 , JET_errSuccess }, { JET_paramLogFilePath , 0 , (LPSTR)-1 , JET_errSuccess }, { JET_paramBaseName , 0 , "edb" , JET_errSuccess }, { JET_paramEventSource , 0 , "HelpSvc.exe" , JET_errSuccess }, { JET_paramNoInformationEvent, 1 , NULL , JET_errSuccess }, { JET_paramGlobalMinVerPages , 64 , NULL , JET_errSuccess }, { JET_paramMaxVerPages , 2048 , NULL , JET_errSuccess }, { JET_paramCacheSizeMax , 4096 , NULL , JET_errSuccess }, //// { JET_paramLogFileSize , 1024 , NULL , JET_errSuccess }, //// { JET_paramCircularLog , 0 , NULL , JET_errSuccess }, { -1 } }; //////////////////// MPC::wstring strDir; LPCSTR szDirAnsi; const JET_SETSYSPARAM* pParam; JET_ERR err; if(szLogs == NULL) { szLogs = HC_ROOT_HELPSVC_CONFIG L"\\CheckPoint\\"; pParam = sConfig_Normal; } else { pParam = sConfig_LargeSet; } MPC::SubstituteEnvVariables( strDir = szLogs ); szDirAnsi = W2A( strDir.c_str() ); __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::MakeDir( strDir )); ::JetSetSystemParameter( &m_inst, 0, JET_paramRecovery, 0, "off" ); while(pParam->paramid != -1) { JET_SETSYSPARAM param = *pParam++; JET_ERR err; if(param.sz == (LPSTR)-1) { param.sz = szDirAnsi; } err = ::JetSetSystemParameter( &m_inst, 0, param.paramid, param.lParam, param.sz ); if(err == JET_errInvalidParameter) continue; // Ignore version problems. __MPC_EXIT_IF_JET_FAILS(hr, err); } err = ::JetInit( &m_inst ); // // If it's a log problem, delete the log files and retry. // if(err >= JET_errSoftRecoveryOnSnapshot && err <= JET_errInvalidLoggedOperation ) { MPC::FileSystemObject fso( strDir.c_str() ); __MPC_EXIT_IF_METHOD_FAILS(hr, fso.DeleteChildren( true, false )); err = ::JetInit( &m_inst ); } __MPC_EXIT_IF_JET_FAILS(hr, err); m_fInitialized = true; } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT JetBlue::SessionPool::Close( /*[in]*/ bool fForce ) { __HCP_FUNC_ENTRY( "JetBlue::SessionPool::Close" ); HRESULT hr; MPC::SmartLock<_ThreadModel> lock( this ); while(m_lstSessions.size() > 0) { SessionState& ss = m_lstSessions.front(); if(ss.m_sess) { HRESULT hr2 = ss.m_sess->Close( fForce ); if(!fForce) __MPC_EXIT_IF_JET_FAILS(hr, hr2); } m_lstSessions.pop_front(); } if(m_fInitialized) { JET_ERR err = ::JetTerm2( m_inst, JET_bitTermComplete ); if(!fForce) __MPC_EXIT_IF_JET_FAILS(hr, err); m_inst = JET_instanceNil; m_fInitialized = false; } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } //////////////////////////////////////////////////////////////////////////////// HRESULT JetBlue::SessionPool::GetSession( /*[out]*/ SessionHandle& handle , /*[in]*/ DWORD dwTimeout ) { __HCP_FUNC_ENTRY( "JetBlue::SessionPool::GetSession" ); HRESULT hr; SessionIter it; Session* sess = NULL; MPC::SmartLock<_ThreadModel> lock( this ); handle.Release(); __MPC_EXIT_IF_METHOD_FAILS(hr, Init()); while(1) { if(m_iAllocated > m_iInUse) { // // Look for free session. // for(it = m_lstSessions.begin(); it != m_lstSessions.end(); it++) { if(it->m_fInUse == false) { it->m_fInUse = true; m_iInUse++; handle.Init( this, it->m_sess ); __MPC_SET_ERROR_AND_EXIT(hr, S_OK); } } } // // No free session, but still below maximum number of sessions, so let's create a new one. // if(m_iAllocated < l_MaxPoolSize) { __MPC_EXIT_IF_ALLOC_FAILS(hr, sess, new Session( this, m_inst )); __MPC_EXIT_IF_METHOD_FAILS(hr, sess->Init()); it = m_lstSessions.insert( m_lstSessions.end() ); m_iAllocated++; it->m_sess = sess; sess = NULL; it->m_fInUse = true; m_iInUse++; handle.Init( this, it->m_sess ); __MPC_SET_ERROR_AND_EXIT(hr, S_OK); } // // Out of resources, wait for other threads to release them... // if(dwTimeout == 0) { __MPC_SET_ERROR_AND_EXIT(hr, ERROR_NO_SYSTEM_RESOURCES); } lock = NULL; ::Sleep( 100 ); if(dwTimeout < 100) dwTimeout = 0; else dwTimeout -= 100; lock = this; } hr = S_OK; __HCP_FUNC_CLEANUP; if(sess) delete sess; __HCP_FUNC_EXIT(hr); }