📄 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 <vxibuildopts.h>
#if P_VXI
#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
// when a writer opens the entry, we attempt to open for read
// access here before the writer finishes writing the entry, then
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -