📄 sbcachemanager.cpp
字号:
/****************License************************************************ * * Copyright 2000-2003. ScanSoft, Inc. * * Use of this software is subject to notices and obligations set forth * in the SpeechWorks Public License - Software Version 1.2 which is * included with this software. * * ScanSoft is a registered trademark of ScanSoft, Inc., and OpenSpeech, * SpeechWorks and the SpeechWorks logo are registered trademarks or * trademarks of SpeechWorks International, Inc. in the United States * and other countries. * ***********************************************************************/ // -----1=0-------2=0-------3=0-------4=0-------5=0-------6=0-------7=0-------8 #include "SBcacheInternal.h" #include "SBcacheManager.hpp" // for this class #include <list> // for STL list template class #include <vector> // for STL list template class #include <algorithm> #include <limits.h> // for ULONG_MAX #if defined(__GNUC__) && (__GNUC__ == 3) && (__GNUC_MINOR__ < 2) #include <strstream> #else #include <sstream> // for basic_ostringstream( ) #endif static const int CACHE_REF_COUNT_MUTEX_POOL_SIZE = 256; static const int CACHE_MAX_DIR_ENTRIES = 256; static const char CACHE_ENTRY_FILE_EXTENSION[] = ".sbc"; // -----1=0-------2=0-------3=0-------4=0-------5=0-------6=0-------7=0-------8 // Shut down the manager SBcacheManager::~SBcacheManager( ) { if ( _cacheDir.length( ) > 0 ) { // Lock to be paranoid, makes sure everyone else is done if ( _entryTableMutex.Lock( ) != VXItrd_RESULT_SUCCESS ) { Error (110, L"%s%s", L"mutex", L"entry table mutex"); } else { // Write out the index file WriteIndex( ); // Clear the cache entries _entryTable.erase (_entryTable.begin( ), _entryTable.end( )); _entryLRUList.erase(_entryLRUList.begin(), _entryLRUList.end()); _curSizeBytes.Reset (0); // Clear the directory name _cacheDir = ""; if ( _entryTableMutex.Unlock( ) != VXItrd_RESULT_SUCCESS ) Error (111, L"%s%s", L"mutex", L"entry table mutex"); } } } // Create the manager VXIcacheResult SBcacheManager::Create(const SBcacheNString &cacheDir, VXIulong cacheMaxSizeBytes, VXIulong entryMaxSizeBytes, VXIulong entryExpTimeSec, VXIbool unlockEntries, VXIulong cacheLowWaterBytes) { VXIcacheResult rc = VXIcache_RESULT_SUCCESS; // Avoid double initialization if ( _cacheDir.length( ) > 0 ) { Error (112, NULL); rc = VXIcache_RESULT_FATAL_ERROR; } // Create the path sequence number if (( rc == VXIcache_RESULT_SUCCESS ) && ( _pathSeqNum.Create( ) != VXItrd_RESULT_SUCCESS )) { Error (109, L"%s%s", L"mutex", L"path seq num"); rc = VXIcache_RESULT_SYSTEM_ERROR; } // Create the entry table mutex if (( rc == VXIcache_RESULT_SUCCESS ) && ( _entryTableMutex.Create(L"SBcacheManager entry table mutex") != VXItrd_RESULT_SUCCESS )) { Error (109, L"%s%s", L"mutex", L"entry table mutex"); rc = VXIcache_RESULT_SYSTEM_ERROR; } // Create the mutex pool if (( rc == VXIcache_RESULT_SUCCESS ) && ( _refCntMutexPool.Create(L"SBcacheManager refCnt mutex pool", CACHE_REF_COUNT_MUTEX_POOL_SIZE) )) { Error (109, L"%s%s", L"mutex", L"entry table mutex"); rc = VXIcache_RESULT_SYSTEM_ERROR; } // Create the cache size if (( rc == VXIcache_RESULT_SUCCESS ) && ( _curSizeBytes.Create( ) != VXItrd_RESULT_SUCCESS )) { Error (109, L"%s%s", L"mutex", L"cache size mutex"); rc = VXIcache_RESULT_SYSTEM_ERROR; } // Create the cache directory if required if ( rc == VXIcache_RESULT_SUCCESS ) { SBcacheStatInfo statInfo; if ( SBcacheStat (cacheDir.c_str( ), &statInfo) ) { // Exists, make sure it is a file if ( ! SBcacheIsDir(statInfo) ) { Error (113, L"%s%S", L"cacheDirectory", cacheDir.c_str( )); rc = VXIcache_RESULT_FATAL_ERROR; } else { // Load the cache index file rc = ReadIndex (cacheDir); } } else if ( ! SBcacheMkdir (cacheDir.c_str( )) ) { Error (114, L"%s%S", L"cacheDirectory", cacheDir.c_str( )); rc = VXIcache_RESULT_FATAL_ERROR; } } // Update data members if ( rc == VXIcache_RESULT_SUCCESS ) { _cacheDir = cacheDir; _maxSizeBytes = cacheMaxSizeBytes; _entryMaxSizeBytes = entryMaxSizeBytes; _lowWaterBytes = cacheLowWaterBytes; } _entryReserve = 0; ReserveEntries(0); // Disable, for now. Diag (SBCACHE_MGR_TAGID, L"Create", L"rc = %d", rc); return rc; } // NOTE: This is disabled, see above, until SPR is fixed or NPFed. // // Interesting trick, we want to reserve a certain amount of memory for N // entries, but we only have an estimate as to the size of an entry, and we // can't make all the code use a special allocator. // Solution: Make an estimate(E) of how much an entry will take up, and // grab N * E bytes from the general allocator. Whenever we add an entry, // we give back E bytes (if we can), whenver we remove an entry we take E // bytes (unless we hit the maximum preallocation). // void SBcacheManager::ReserveEntries(int nentries) { for (int i = 0; i < nentries; ++i) { SBcacheEntryEstimate* es = new SBcacheEntryEstimate(); es->next = _entryReserve; _entryReserve = es; } _entriesReserved = nentries; _maxEntriesReserved = nentries; } void SBcacheManager::EntryAdded() { if (_entriesReserved > 0) { SBcacheEntryEstimate* es = _entryReserve; _entryReserve = es->next; delete es; _entriesReserved--; } else { static int once = 1; if (once) { once = 0; } } } void SBcacheManager::EntryRemoved() { if (_maxEntriesReserved > _entriesReserved) { SBcacheEntryEstimate* es = new SBcacheEntryEstimate(); es->next = _entryReserve; _entryReserve = es; _entriesReserved++; } } // Synchronized table and LRU list // bool SBcacheManager::InsertEntry(SBcacheEntry& entry) { SBcacheEntryTable::value_type tableEntry (entry.GetKey(), entry); if ( ! _entryTable.insert (tableEntry).second ) return false; _entryLRUList.push_back(entry); EntryAdded(); return true; } void SBcacheManager::RemoveEntry(const SBcacheEntry& entry) { SBcacheEntryTable::iterator te = _entryTable.find(entry.GetKey()); _entryTable.erase(te); SBcacheEntryList::iterator vi = _entryLRUList.begin(); for (; vi != _entryLRUList.end(); ++vi) if (entry.Equivalent(*vi)) { _entryLRUList.erase(vi); break; } EntryRemoved(); } void SBcacheManager::TouchEntry(const SBcacheEntry& entry) { SBcacheEntryList::iterator vi = _entryLRUList.begin(); for (; vi != _entryLRUList.end(); ++vi) if (entry.Equivalent(*vi)) { _entryLRUList.erase(vi); _entryLRUList.push_back(entry); break; } } //####################################################################### // Note about locking protocol. THere are three locks in this code: // A. The entrytable lock (_entryTableMutex) // B. a lock per entry (GetSizeBytes, Open, Close) // C. a lock of _curSizeBytes (atomic operations) // Currently, both locks A & C are held inside of lock B because an open // obtains a lock and holds it until the entry is closed. Therefore, // a B lock must never be obtained while an A or C lock is held! //####################################################################### // Open an entry VXIcacheResult SBcacheManager::Open(VXIlogInterface *log, const SBcacheString &moduleName, const SBcacheKey &key, VXIcacheOpenMode mode, VXIint32 flags, const VXIMap *properties, VXIMap *streamInfo, VXIcacheStream **stream) { VXIcacheResult rc = VXIcache_RESULT_SUCCESS; // Big loop where we attempt to open and re-open the cache entry for // as long as we get recoverable errors VXIcacheOpenMode finalMode; do { finalMode = mode; rc = VXIcache_RESULT_SUCCESS; if ( _entryTableMutex.StartRead( ) != VXItrd_RESULT_SUCCESS ) { Error (log, 110, L"%s%s", L"mutex", L"entry table mutex"); return VXIcache_RESULT_SYSTEM_ERROR; } // Find the entry and open it, only need read permission bool entryOpened = false; SBcacheEntry entry; SBcacheEntryTable::iterator vi = _entryTable.find (key); if ( vi == _entryTable.end( ) ) { rc = VXIcache_RESULT_NOT_FOUND; } else { entry = (*vi).second; finalMode = (mode == CACHE_MODE_READ_CREATE ? CACHE_MODE_READ : mode); } if ( _entryTableMutex.EndRead( ) != VXItrd_RESULT_SUCCESS ) { Error (log, 111, L"%s%s", L"mutex", L"entry table mutex"); rc = VXIcache_RESULT_SYSTEM_ERROR; } // For write and read/create mode, create the entry if not found if (( rc == VXIcache_RESULT_NOT_FOUND ) && ( mode != CACHE_MODE_READ )) { rc = VXIcache_RESULT_SUCCESS; // Get write permission if ( _entryTableMutex.Lock( ) != VXItrd_RESULT_SUCCESS ) { Error (log, 110, L"%s%s", L"mutex", L"entry table mutex"); rc = VXIcache_RESULT_SYSTEM_ERROR; } else { // Try to find the entry again, it may have been created by now vi = _entryTable.find (key); if ( vi != _entryTable.end( ) ) { // Found it this time entry = (*vi).second; finalMode = (mode == CACHE_MODE_READ_CREATE ? CACHE_MODE_READ : mode); } else { // Create and open the entry for write rc = entry.Create (GetLog( ), GetDiagBase( ), _refCntMutexPool.GetMutex( )); if ( rc == VXIcache_RESULT_SUCCESS ) { finalMode = (mode == CACHE_MODE_READ_CREATE ? CACHE_MODE_WRITE : mode); // Note entry lock obtained while entrytable lock is held! We // can get away with this, because the lock isn't visible to // anyone else yet. rc = entry.Open (log, moduleName, key, GetNewEntryPath (moduleName, key), finalMode, flags, _entryMaxSizeBytes, properties, streamInfo, stream); } // Insert the entry if ( rc == VXIcache_RESULT_SUCCESS ) { entryOpened = true; SBcacheEntryTable::value_type tableEntry (key, entry); if ( !InsertEntry(entry)) { Error (log, 100, NULL); (*stream)->Close (true); *stream = NULL; rc = VXIcache_RESULT_OUT_OF_MEMORY; } } } if ( _entryTableMutex.Unlock( ) != VXItrd_RESULT_SUCCESS ) { Error (log, 111, L"%s%s", L"mutex", L"entry table mutex"); rc = VXIcache_RESULT_SYSTEM_ERROR; } } } // Open pre-existing entry. Note that this may return // VXIcache_RESULT_FAILURE in some cases where we then need to // retry the entire process of opening the entry -- that occurs
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -