code for DEV-52939: viewer's object geometry cache files are not limited in number, and can also be incorrectly cleared with the VFS cache.

reviewed by andrew.
master
Xiaohong Bao 2010-09-01 13:40:10 -06:00
parent 2969599880
commit e29f811d56
10 changed files with 671 additions and 222 deletions

View File

@ -1147,6 +1147,17 @@
<key>Value</key>
<string />
</map>
<key>CacheNumberOfRegionsForObjects</key>
<map>
<key>Comment</key>
<string>Controls number of regions to be cached for objects, ranges from 16 to 128.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>U32</string>
<key>Value</key>
<integer>128</integer>
</map>
<key>CacheSize</key>
<map>
<key>Comment</key>

View File

@ -3025,14 +3025,6 @@ void LLAppViewer::migrateCacheDirectory()
#endif // LL_WINDOWS || LL_DARWIN
}
//static
S32 LLAppViewer::getCacheVersion()
{
static const S32 cache_version = 7;
return cache_version ;
}
void dumpVFSCaches()
{
llinfos << "======= Static VFS ========" << llendl;
@ -3071,23 +3063,40 @@ void dumpVFSCaches()
SetCurrentDirectory(w_str);
#endif
}
//static
U32 LLAppViewer::getTextureCacheVersion()
{
//viewer texture cache version, change if the texture cache format changes.
const U32 TEXTURE_CACHE_VERSION = 7;
return TEXTURE_CACHE_VERSION ;
}
//static
U32 LLAppViewer::getObjectCacheVersion()
{
// Viewer object cache version, change if object update
// format changes. JC
const U32 INDRA_OBJECT_CACHE_VERSION = 14;
return INDRA_OBJECT_CACHE_VERSION;
}
bool LLAppViewer::initCache()
{
mPurgeCache = false;
BOOL disable_texture_cache = FALSE ;
BOOL read_only = mSecondInstance ? TRUE : FALSE;
LLAppViewer::getTextureCache()->setReadOnly(read_only) ;
LLVOCache::getInstance()->setReadOnly(read_only);
if (gSavedSettings.getS32("LocalCacheVersion") != LLAppViewer::getCacheVersion())
BOOL texture_cache_mismatch = FALSE ;
if (gSavedSettings.getS32("LocalCacheVersion") != LLAppViewer::getTextureCacheVersion())
{
if(read_only)
texture_cache_mismatch = TRUE ;
if(!read_only)
{
disable_texture_cache = TRUE ; //if the cache version of this viewer is different from the running one, this viewer can not use the texture cache.
}
else
{
mPurgeCache = true; // Purge cache if the version number is different.
gSavedSettings.setS32("LocalCacheVersion", LLAppViewer::getCacheVersion());
gSavedSettings.setS32("LocalCacheVersion", LLAppViewer::getTextureCacheVersion());
}
}
@ -3138,9 +3147,11 @@ bool LLAppViewer::initCache()
const S64 MAX_CACHE_SIZE = 1024*MB;
cache_size = llmin(cache_size, MAX_CACHE_SIZE);
S64 texture_cache_size = ((cache_size * 8)/10);
S64 extra = LLAppViewer::getTextureCache()->initCache(LL_PATH_CACHE, texture_cache_size, disable_texture_cache);
S64 extra = LLAppViewer::getTextureCache()->initCache(LL_PATH_CACHE, texture_cache_size, texture_cache_mismatch);
texture_cache_size -= extra;
LLVOCache::getInstance()->initCache(LL_PATH_CACHE, gSavedSettings.getU32("CacheNumberOfRegionsForObjects"), getObjectCacheVersion()) ;
LLSplashScreen::update(LLTrans::getString("StartupInitializingVFS"));
// Init the VFS
@ -3303,6 +3314,7 @@ void LLAppViewer::purgeCache()
{
LL_INFOS("AppCache") << "Purging Cache and Texture Cache..." << llendl;
LLAppViewer::getTextureCache()->purgeCache(LL_PATH_CACHE);
LLVOCache::getInstance()->removeCache(LL_PATH_CACHE);
std::string mask = gDirUtilp->getDirDelimiter() + "*.*";
gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""),mask);
}

View File

@ -94,7 +94,8 @@ public:
static LLImageDecodeThread* getImageDecodeThread() { return sImageDecodeThread; }
static LLTextureFetch* getTextureFetch() { return sTextureFetch; }
static S32 getCacheVersion() ;
static U32 getTextureCacheVersion() ;
static U32 getObjectCacheVersion() ;
const std::string& getSerialNumber() { return mSerialNumber; }

View File

@ -927,7 +927,7 @@ void LLTextureCache::setReadOnly(BOOL read_only)
}
//called in the main thread.
S64 LLTextureCache::initCache(ELLPath location, S64 max_size, BOOL disable_texture_cache)
S64 LLTextureCache::initCache(ELLPath location, S64 max_size, BOOL texture_cache_mismatch)
{
llassert_always(getPending() == 0) ; //should not start accessing the texture cache before initialized.
@ -942,20 +942,23 @@ S64 LLTextureCache::initCache(ELLPath location, S64 max_size, BOOL disable_textu
sCacheMaxTexturesSize = max_size;
max_size -= sCacheMaxTexturesSize;
if(disable_texture_cache) //the texture cache is disabled
{
llinfos << "The texture cache is disabled!" << llendl ;
setReadOnly(TRUE) ;
purgeAllTextures(true);
return max_size ;
}
LL_INFOS("TextureCache") << "Headers: " << sCacheMaxEntries
<< " Textures size: " << sCacheMaxTexturesSize/(1024*1024) << " MB" << LL_ENDL;
setDirNames(location);
if(texture_cache_mismatch)
{
//if readonly, disable the texture cache,
//otherwise wipe out the texture cache.
purgeAllTextures(true);
if(mReadOnly)
{
return max_size ;
}
}
if (!mReadOnly)
{
LLFile::mkdir(mTexturesDirName);

View File

@ -105,7 +105,7 @@ public:
void purgeCache(ELLPath location);
void setReadOnly(BOOL read_only) ;
S64 initCache(ELLPath location, S64 maxsize, BOOL disable_texture_cache);
S64 initCache(ELLPath location, S64 maxsize, BOOL texture_cache_mismatch);
handle_t readFromCache(const std::string& local_filename, const LLUUID& id, U32 priority, S32 offset, S32 size,
ReadResponder* responder);

View File

@ -69,13 +69,6 @@
#pragma warning(disable:4355)
#endif
// Viewer object cache version, change if object update
// format changes. JC
const U32 INDRA_OBJECT_CACHE_VERSION = 14;
// Format string used to construct filename for the object cache
static const char OBJECT_CACHE_FILENAME[] = "objects_%d_%d.slc";
extern BOOL gNoRender;
const F32 WATER_TEXTURE_SCALE = 8.f; // Number of times to repeat the water texture across a region
@ -214,7 +207,7 @@ LLViewerRegion::LLViewerRegion(const U64 &handle,
mProductName("unknown"),
mHttpUrl(""),
mCacheLoaded(FALSE),
mCacheEntriesCount(0),
mCacheDirty(FALSE),
mCacheID(),
mEventPoll(NULL),
mReleaseNotesRequested(FALSE),
@ -264,8 +257,6 @@ LLViewerRegion::LLViewerRegion(const U64 &handle,
// Create the object lists
initStats();
mCacheStart.append(mCacheEnd);
//create object partitions
//MUST MATCH declaration of eObjectPartitions
mObjectPartition.push_back(new LLHUDPartition()); //PARTITION_HUD
@ -324,19 +315,6 @@ LLViewerRegion::~LLViewerRegion()
std::for_each(mObjectPartition.begin(), mObjectPartition.end(), DeletePointer());
}
const std::string LLViewerRegion::getObjectCacheFilename(U64 mHandle) const
{
std::string filename;
U32 region_x, region_y;
grid_from_region_handle(mHandle, &region_x, &region_y);
filename = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,
llformat(OBJECT_CACHE_FILENAME, region_x, region_y));
return filename;
}
void LLViewerRegion::loadObjectCache()
{
if (mCacheLoaded)
@ -347,77 +325,10 @@ void LLViewerRegion::loadObjectCache()
// Presume success. If it fails, we don't want to try again.
mCacheLoaded = TRUE;
LLVOCacheEntry *entry;
std::string filename = getObjectCacheFilename(mHandle);
LL_DEBUGS("ObjectCache") << filename << LL_ENDL;
LLFILE* fp = LLFile::fopen(filename, "rb"); /* Flawfinder: ignore */
if (!fp)
if(LLVOCache::hasInstance())
{
// might not have a file, which is normal
return;
LLVOCache::getInstance()->readFromCache(mHandle, mCacheID, mCacheMap) ;
}
U32 zero;
size_t nread;
nread = fread(&zero, sizeof(U32), 1, fp);
if (nread != 1 || zero)
{
// a non-zero value here means bad things!
// skip reading the cached values
llinfos << "Cache file invalid" << llendl;
fclose(fp);
return;
}
U32 version;
nread = fread(&version, sizeof(U32), 1, fp);
if (nread != 1 || version != INDRA_OBJECT_CACHE_VERSION)
{
// a version mismatch here means we've changed the binary format!
// skip reading the cached values
llinfos << "Cache version changed, discarding" << llendl;
fclose(fp);
return;
}
LLUUID cache_id;
nread = fread(&cache_id.mData, 1, UUID_BYTES, fp);
if (nread != (size_t)UUID_BYTES || mCacheID != cache_id)
{
llinfos << "Cache ID doesn't match for this region, discarding"
<< llendl;
fclose(fp);
return;
}
S32 num_entries;
nread = fread(&num_entries, sizeof(S32), 1, fp);
if (nread != 1)
{
llinfos << "Short read, discarding" << llendl;
fclose(fp);
return;
}
S32 i;
for (i = 0; i < num_entries; i++)
{
entry = new LLVOCacheEntry(fp);
if (!entry->getLocalID())
{
llwarns << "Aborting cache file load for " << filename << ", cache file corruption!" << llendl;
delete entry;
entry = NULL;
break;
}
mCacheEnd.insert(*entry);
mCacheMap[entry->getLocalID()] = entry;
mCacheEntriesCount++;
}
fclose(fp);
}
@ -428,61 +339,22 @@ void LLViewerRegion::saveObjectCache()
return;
}
S32 num_entries = mCacheEntriesCount;
if (0 == num_entries)
if (mCacheMap.empty())
{
return;
}
std::string filename = getObjectCacheFilename(mHandle);
LL_DEBUGS("ObjectCache") << filename << LL_ENDL;
LLFILE* fp = LLFile::fopen(filename, "wb"); /* Flawfinder: ignore */
if (!fp)
if(LLVOCache::hasInstance())
{
llwarns << "Unable to write cache file " << filename << llendl;
return;
LLVOCache::getInstance()->writeToCache(mHandle, mCacheID, mCacheMap, mCacheDirty) ;
mCacheDirty = FALSE;
}
// write out zero to indicate a version cache file
U32 zero = 0;
if (fwrite(&zero, sizeof(U32), 1, fp) != 1)
for(LLVOCacheEntry::vocache_entry_map_t::iterator iter = mCacheMap.begin(); iter != mCacheMap.end(); ++iter)
{
llwarns << "Short write" << llendl;
delete iter->second;
}
// write out version number
U32 version = INDRA_OBJECT_CACHE_VERSION;
if (fwrite(&version, sizeof(U32), 1, fp) != 1)
{
llwarns << "Short write" << llendl;
}
// write the cache id for this sim
if (fwrite(&mCacheID.mData, 1, UUID_BYTES, fp) != (size_t)UUID_BYTES)
{
llwarns << "Short write" << llendl;
}
if (fwrite(&num_entries, sizeof(S32), 1, fp) != 1)
{
llwarns << "Short write" << llendl;
}
LLVOCacheEntry *entry;
for (entry = mCacheStart.getNext(); entry && (entry != &mCacheEnd); entry = entry->getNext())
{
entry->writeToFile(fp);
}
mCacheMap.clear();
mCacheEnd.unlink();
mCacheEnd.init();
mCacheStart.deleteAll();
mCacheStart.init();
fclose(fp);
}
void LLViewerRegion::sendMessage()
@ -1175,7 +1047,6 @@ void LLViewerRegion::cacheFullUpdate(LLViewerObject* objectp, LLDataPackerBinary
mCacheMap.erase(local_id);
delete entry;
entry = new LLVOCacheEntry(local_id, crc, dp);
mCacheEnd.insert(*entry);
mCacheMap[local_id] = entry;
}
}
@ -1184,18 +1055,13 @@ void LLViewerRegion::cacheFullUpdate(LLViewerObject* objectp, LLDataPackerBinary
// we haven't seen this object before
// Create new entry and add to map
if (mCacheEntriesCount > MAX_OBJECT_CACHE_ENTRIES)
if (mCacheMap.size() > MAX_OBJECT_CACHE_ENTRIES)
{
entry = mCacheStart.getNext();
mCacheMap.erase(entry->getLocalID());
delete entry;
mCacheEntriesCount--;
mCacheMap.erase(mCacheMap.begin());
}
entry = new LLVOCacheEntry(local_id, crc, dp);
mCacheEnd.insert(*entry);
mCacheMap[local_id] = entry;
mCacheEntriesCount++;
}
return ;
}
@ -1310,6 +1176,7 @@ void LLViewerRegion::requestCacheMisses()
mCacheMissFull.reset();
mCacheMissCRC.reset();
mCacheDirty = TRUE ;
// llinfos << "KILLDEBUG Sent cache miss full " << full_count << " crc " << crc_count << llendl;
}
@ -1327,9 +1194,10 @@ void LLViewerRegion::dumpCache()
}
LLVOCacheEntry *entry;
for (entry = mCacheStart.getNext(); entry && (entry != &mCacheEnd); entry = entry->getNext())
for(LLVOCacheEntry::vocache_entry_map_t::iterator iter = mCacheMap.begin(); iter != mCacheMap.end(); ++iter)
{
entry = iter->second ;
S32 hits = entry->getHitCount();
S32 changes = entry->getCRCChangeCount();
@ -1340,7 +1208,7 @@ void LLViewerRegion::dumpCache()
change_bin[changes]++;
}
llinfos << "Count " << mCacheEntriesCount << llendl;
llinfos << "Count " << mCacheMap.size() << llendl;
for (i = 0; i < BINS; i++)
{
llinfos << "Hits " << i << " " << hit_bin[i] << llendl;

View File

@ -323,9 +323,6 @@ public:
LLDynamicArray<LLUUID> mMapAvatarIDs;
private:
// determine the cache filename for the region from the region handle
const std::string getObjectCacheFilename(U64 mHandle) const;
// The surfaces and other layers
LLSurface* mLandp;
@ -387,11 +384,8 @@ private:
// Regions can have order 10,000 objects, so assume
// a structure of size 2^14 = 16,000
BOOL mCacheLoaded;
typedef std::map<U32, LLVOCacheEntry *> cache_map_t;
cache_map_t mCacheMap;
LLVOCacheEntry mCacheStart;
LLVOCacheEntry mCacheEnd;
U32 mCacheEntriesCount;
BOOL mCacheDirty;
LLVOCacheEntry::vocache_entry_map_t mCacheMap;
LLDynamicArray<U32> mCacheMissFull;
LLDynamicArray<U32> mCacheMissCRC;
// time?

View File

@ -25,10 +25,19 @@
*/
#include "llviewerprecompiledheaders.h"
#include "llvocache.h"
#include "llerror.h"
#include "llregionhandle.h"
BOOL check_read(LLAPRFile* apr_file, void* src, S32 n_bytes)
{
return apr_file->read(src, n_bytes) == n_bytes ;
}
BOOL check_write(LLAPRFile* apr_file, void* src, S32 n_bytes)
{
return apr_file->write(src, n_bytes) == n_bytes ;
}
//---------------------------------------------------------------------------
// LLVOCacheEntry
@ -57,26 +66,31 @@ LLVOCacheEntry::LLVOCacheEntry()
mDP.assignBuffer(mBuffer, 0);
}
static inline void checkedRead(LLFILE *fp, void *data, size_t nbytes)
LLVOCacheEntry::LLVOCacheEntry(LLAPRFile* apr_file)
{
if (fread(data, 1, nbytes, fp) != nbytes)
{
llwarns << "Short read" << llendl;
memset(data, 0, nbytes);
S32 size = -1;
BOOL success;
success = check_read(apr_file, &mLocalID, sizeof(U32));
if(success)
{
success = check_read(apr_file, &mCRC, sizeof(U32));
}
if(success)
{
success = check_read(apr_file, &mHitCount, sizeof(S32));
}
if(success)
{
success = check_read(apr_file, &mDupeCount, sizeof(S32));
}
LLVOCacheEntry::LLVOCacheEntry(LLFILE *fp)
if(success)
{
S32 size;
checkedRead(fp, &mLocalID, sizeof(U32));
checkedRead(fp, &mCRC, sizeof(U32));
checkedRead(fp, &mHitCount, sizeof(S32));
checkedRead(fp, &mDupeCount, sizeof(S32));
checkedRead(fp, &mCRCChangeCount, sizeof(S32));
checkedRead(fp, &size, sizeof(S32));
success = check_read(apr_file, &mCRCChangeCount, sizeof(S32));
}
if(success)
{
success = check_read(apr_file, &size, sizeof(S32));
// Corruption in the cache entries
if ((size > 10000) || (size < 1))
@ -90,11 +104,30 @@ LLVOCacheEntry::LLVOCacheEntry(LLFILE *fp)
mBuffer = NULL;
return;
}
}
if(success && size > 0)
{
mBuffer = new U8[size];
success = check_read(apr_file, mBuffer, size);
mBuffer = new U8[size];
checkedRead(fp, mBuffer, size);
if(success)
{
mDP.assignBuffer(mBuffer, size);
}
else
{
delete[] mBuffer ;
mBuffer = NULL ;
}
}
if(!success)
{
mLocalID = 0;
mCRC = 0;
mBuffer = NULL;
}
}
LLVOCacheEntry::~LLVOCacheEntry()
{
@ -148,22 +181,466 @@ void LLVOCacheEntry::dump() const
<< llendl;
}
static inline void checkedWrite(LLFILE *fp, const void *data, size_t nbytes)
BOOL LLVOCacheEntry::writeToFile(LLAPRFile* apr_file) const
{
if (fwrite(data, 1, nbytes, fp) != nbytes)
BOOL success;
success = check_write(apr_file, (void*)&mLocalID, sizeof(U32));
if(success)
{
llwarns << "Short write" << llendl;
success = check_write(apr_file, (void*)&mCRC, sizeof(U32));
}
if(success)
{
success = check_write(apr_file, (void*)&mHitCount, sizeof(S32));
}
if(success)
{
success = check_write(apr_file, (void*)&mDupeCount, sizeof(S32));
}
if(success)
{
success = check_write(apr_file, (void*)&mCRCChangeCount, sizeof(S32));
}
if(success)
{
S32 size = mDP.getBufferSize();
success = check_write(apr_file, (void*)&size, sizeof(S32));
if(success)
{
success = check_write(apr_file, (void*)mBuffer, size);
}
}
void LLVOCacheEntry::writeToFile(LLFILE *fp) const
{
checkedWrite(fp, &mLocalID, sizeof(U32));
checkedWrite(fp, &mCRC, sizeof(U32));
checkedWrite(fp, &mHitCount, sizeof(S32));
checkedWrite(fp, &mDupeCount, sizeof(S32));
checkedWrite(fp, &mCRCChangeCount, sizeof(S32));
S32 size = mDP.getBufferSize();
checkedWrite(fp, &size, sizeof(S32));
checkedWrite(fp, mBuffer, size);
return success ;
}
//-------------------------------------------------------------------
//LLVOCache
//-------------------------------------------------------------------
// Format string used to construct filename for the object cache
static const char OBJECT_CACHE_FILENAME[] = "objects_%d_%d.slc";
const U32 MAX_NUM_OBJECT_ENTRIES = 128 ;
const U32 NUM_ENTRIES_TO_PURGE = 16 ;
const char* object_cache_dirname = "objectcache";
const char* header_filename = "object.cache";
LLVOCache* LLVOCache::sInstance = NULL;
//static
LLVOCache* LLVOCache::getInstance()
{
if(!sInstance)
{
sInstance = new LLVOCache() ;
}
return sInstance ;
}
//static
BOOL LLVOCache::hasInstance()
{
return sInstance != NULL ;
}
//static
void LLVOCache::destroyClass()
{
if(sInstance)
{
delete sInstance ;
sInstance = NULL ;
}
}
LLVOCache::LLVOCache():
mInitialized(FALSE),
mReadOnly(TRUE),
mNumEntries(0)
{
mLocalAPRFilePoolp = new LLVolatileAPRPool() ;
}
LLVOCache::~LLVOCache()
{
writeCacheHeader();
clearCacheInMemory();
delete mLocalAPRFilePoolp;
}
void LLVOCache::setDirNames(ELLPath location)
{
std::string delem = gDirUtilp->getDirDelimiter();
mHeaderFileName = gDirUtilp->getExpandedFilename(location, object_cache_dirname, header_filename);
mObjectCacheDirName = gDirUtilp->getExpandedFilename(location, object_cache_dirname);
}
void LLVOCache::initCache(ELLPath location, U32 size, U32 cache_version)
{
if(mInitialized)
{
return ;
}
setDirNames(location);
if (!mReadOnly)
{
LLFile::mkdir(mObjectCacheDirName);
}
mCacheSize = llmin(size, MAX_NUM_OBJECT_ENTRIES) ;
mCacheSize = llmax(mCacheSize, NUM_ENTRIES_TO_PURGE);
mMetaInfo.mVersion = cache_version;
readCacheHeader();
mInitialized = TRUE ;
if(mMetaInfo.mVersion != cache_version)
{
mMetaInfo.mVersion = cache_version ;
if(mReadOnly) //disable cache
{
clearCacheInMemory();
}
else //delete the current cache if the format does not match.
{
removeCache();
}
}
}
void LLVOCache::removeCache(ELLPath location)
{
if(mReadOnly)
{
return ;
}
std::string delem = gDirUtilp->getDirDelimiter();
std::string mask = delem + "*";
std::string cache_dir = gDirUtilp->getExpandedFilename(location, object_cache_dirname);
gDirUtilp->deleteFilesInDir(cache_dir, mask); //delete all files
LLFile::rmdir(cache_dir);
clearCacheInMemory();
mInitialized = FALSE ;
}
void LLVOCache::removeCache()
{
llassert_always(mInitialized) ;
if(mReadOnly)
{
return ;
}
std::string delem = gDirUtilp->getDirDelimiter();
std::string mask = delem + "*";
gDirUtilp->deleteFilesInDir(mObjectCacheDirName, mask);
clearCacheInMemory() ;
writeCacheHeader();
}
void LLVOCache::clearCacheInMemory()
{
if(!mHeaderEntryQueue.empty())
{
for(header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin(); iter != mHeaderEntryQueue.end(); ++iter)
{
delete *iter ;
}
mHeaderEntryQueue.clear();
mHandleEntryMap.clear();
mNumEntries = 0 ;
}
}
void LLVOCache::getObjectCacheFilename(U64 handle, std::string& filename)
{
U32 region_x, region_y;
grid_from_region_handle(handle, &region_x, &region_y);
filename = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, object_cache_dirname,
llformat(OBJECT_CACHE_FILENAME, region_x, region_y));
return ;
}
void LLVOCache::removeFromCache(U64 handle)
{
if(mReadOnly)
{
return ;
}
std::string filename;
getObjectCacheFilename(handle, filename);
LLAPRFile::remove(filename, mLocalAPRFilePoolp);
}
BOOL LLVOCache::checkRead(LLAPRFile* apr_file, void* src, S32 n_bytes)
{
if(!check_read(apr_file, src, n_bytes))
{
delete apr_file ;
removeCache() ;
return FALSE ;
}
return TRUE ;
}
BOOL LLVOCache::checkWrite(LLAPRFile* apr_file, void* src, S32 n_bytes)
{
if(!check_write(apr_file, src, n_bytes))
{
delete apr_file ;
removeCache() ;
return FALSE ;
}
return TRUE ;
}
void LLVOCache::readCacheHeader()
{
//clear stale info.
clearCacheInMemory();
if (LLAPRFile::isExist(mHeaderFileName, mLocalAPRFilePoolp))
{
LLAPRFile* apr_file = new LLAPRFile(mHeaderFileName, APR_READ|APR_BINARY, mLocalAPRFilePoolp);
//read the meta element
if(!checkRead(apr_file, &mMetaInfo, sizeof(HeaderMetaInfo)))
{
return ;
}
HeaderEntryInfo* entry ;
mNumEntries = 0 ;
while(mNumEntries < MAX_NUM_OBJECT_ENTRIES)
{
entry = new HeaderEntryInfo() ;
if(!checkRead(apr_file, entry, sizeof(HeaderEntryInfo)))
{
delete entry ;
return ;
}
else if(!entry->mTime) //end of the cache.
{
delete entry ;
return ;
}
entry->mIndex = mNumEntries++ ;
mHeaderEntryQueue.insert(entry) ;
mHandleEntryMap[entry->mHandle] = entry ;
}
delete apr_file ;
}
else
{
writeCacheHeader() ;
}
}
void LLVOCache::writeCacheHeader()
{
if(mReadOnly)
{
return ;
}
LLAPRFile* apr_file = new LLAPRFile(mHeaderFileName, APR_CREATE|APR_WRITE|APR_BINARY, mLocalAPRFilePoolp);
//write the meta element
if(!checkWrite(apr_file, &mMetaInfo, sizeof(HeaderMetaInfo)))
{
return ;
}
mNumEntries = 0 ;
for(header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin() ; iter != mHeaderEntryQueue.end(); ++iter)
{
(*iter)->mIndex = mNumEntries++ ;
if(!checkWrite(apr_file, (void*)*iter, sizeof(HeaderEntryInfo)))
{
return ;
}
}
mNumEntries = mHeaderEntryQueue.size() ;
if(mNumEntries < MAX_NUM_OBJECT_ENTRIES)
{
HeaderEntryInfo* entry = new HeaderEntryInfo() ;
for(S32 i = mNumEntries ; i < MAX_NUM_OBJECT_ENTRIES ; i++)
{
//fill the cache with the default entry.
if(!checkWrite(apr_file, entry, sizeof(HeaderEntryInfo)))
{
mReadOnly = TRUE ; //disable the cache.
return ;
}
}
delete entry ;
}
delete apr_file ;
}
BOOL LLVOCache::updateEntry(const HeaderEntryInfo* entry)
{
LLAPRFile* apr_file = new LLAPRFile(mHeaderFileName, APR_WRITE|APR_BINARY, mLocalAPRFilePoolp);
apr_file->seek(APR_SET, entry->mIndex * sizeof(HeaderEntryInfo) + sizeof(HeaderMetaInfo)) ;
return checkWrite(apr_file, (void*)entry, sizeof(HeaderEntryInfo)) ;
}
void LLVOCache::readFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::vocache_entry_map_t& cache_entry_map)
{
llassert_always(mInitialized);
handle_entry_map_t::iterator iter = mHandleEntryMap.find(handle) ;
if(iter == mHandleEntryMap.end()) //no cache
{
return ;
}
std::string filename;
getObjectCacheFilename(handle, filename);
LLAPRFile* apr_file = new LLAPRFile(filename, APR_READ|APR_BINARY, mLocalAPRFilePoolp);
LLUUID cache_id ;
if(!checkRead(apr_file, cache_id.mData, UUID_BYTES))
{
return ;
}
if(cache_id != id)
{
llinfos << "Cache ID doesn't match for this region, discarding"<< llendl;
delete apr_file ;
return ;
}
S32 num_entries;
if(!checkRead(apr_file, &num_entries, sizeof(S32)))
{
return ;
}
for (S32 i = 0; i < num_entries; i++)
{
LLVOCacheEntry* entry = new LLVOCacheEntry(apr_file);
if (!entry->getLocalID())
{
llwarns << "Aborting cache file load for " << filename << ", cache file corruption!" << llendl;
delete entry ;
break;
}
cache_entry_map[entry->getLocalID()] = entry;
}
num_entries = cache_entry_map.size() ;
delete apr_file ;
return ;
}
void LLVOCache::purgeEntries()
{
U32 limit = mCacheSize - NUM_ENTRIES_TO_PURGE ;
while(mHeaderEntryQueue.size() > limit)
{
header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin() ;
HeaderEntryInfo* entry = *iter ;
removeFromCache(entry->mHandle) ;
mHandleEntryMap.erase(entry->mHandle) ;
mHeaderEntryQueue.erase(iter) ;
delete entry ;
}
writeCacheHeader() ;
readCacheHeader() ;
mNumEntries = mHandleEntryMap.size() ;
}
void LLVOCache::writeToCache(U64 handle, const LLUUID& id, const LLVOCacheEntry::vocache_entry_map_t& cache_entry_map, BOOL dirty_cache)
{
llassert_always(mInitialized);
if(mReadOnly)
{
return ;
}
HeaderEntryInfo* entry;
handle_entry_map_t::iterator iter = mHandleEntryMap.find(handle) ;
if(iter == mHandleEntryMap.end()) //new entry
{
if(mNumEntries >= mCacheSize)
{
purgeEntries() ;
}
entry = new HeaderEntryInfo();
entry->mHandle = handle ;
entry->mTime = time(NULL) ;
entry->mIndex = mNumEntries++ ;
mHeaderEntryQueue.insert(entry) ;
mHandleEntryMap[handle] = entry ;
}
else
{
entry = iter->second ;
entry->mTime = time(NULL) ;
//resort
mHeaderEntryQueue.erase(entry) ;
mHeaderEntryQueue.insert(entry) ;
}
//update cache header
if(!updateEntry(entry))
{
return ; //update failed.
}
if(!dirty_cache)
{
return ; //nothing changed, no need to update.
}
//write to cache file
std::string filename;
getObjectCacheFilename(handle, filename);
LLAPRFile* apr_file = new LLAPRFile(filename, APR_CREATE|APR_WRITE|APR_BINARY, mLocalAPRFilePoolp);
if(!checkWrite(apr_file, (void*)id.mData, UUID_BYTES))
{
return ;
}
S32 num_entries = cache_entry_map.size() ;
if(!checkWrite(apr_file, &num_entries, sizeof(S32)))
{
return ;
}
for (LLVOCacheEntry::vocache_entry_map_t::const_iterator iter = cache_entry_map.begin(); iter != cache_entry_map.end(); ++iter)
{
if(!iter->second->writeToFile(apr_file))
{
//failed
delete apr_file ;
removeCache() ;
return ;
}
}
delete apr_file ;
return ;
}

View File

@ -36,11 +36,11 @@
// Cache entries
class LLVOCacheEntry;
class LLVOCacheEntry : public LLDLinked<LLVOCacheEntry>
class LLVOCacheEntry
{
public:
LLVOCacheEntry(U32 local_id, U32 crc, LLDataPackerBinaryBuffer &dp);
LLVOCacheEntry(LLFILE *fp);
LLVOCacheEntry(LLAPRFile* apr_file);
LLVOCacheEntry();
~LLVOCacheEntry();
@ -50,12 +50,15 @@ public:
S32 getCRCChangeCount() const { return mCRCChangeCount; }
void dump() const;
void writeToFile(LLFILE *fp) const;
BOOL writeToFile(LLAPRFile* apr_file) const;
void assignCRC(U32 crc, LLDataPackerBinaryBuffer &dp);
LLDataPackerBinaryBuffer *getDP(U32 crc);
void recordHit();
void recordDupe() { mDupeCount++; }
public:
typedef std::map<U32, LLVOCacheEntry*> vocache_entry_map_t;
protected:
U32 mLocalID;
U32 mCRC;
@ -66,4 +69,81 @@ protected:
U8 *mBuffer;
};
//
//Note: LLVOCache is not thread-safe
//
class LLVOCache
{
private:
struct HeaderEntryInfo
{
HeaderEntryInfo() : mIndex(0), mHandle(0), mTime(0) {}
S32 mIndex;
U64 mHandle ;
U32 mTime ;
};
struct HeaderMetaInfo
{
HeaderMetaInfo() : mVersion(0){}
U32 mVersion;
};
struct header_entry_less
{
bool operator()(const HeaderEntryInfo* lhs, const HeaderEntryInfo* rhs) const
{
return lhs->mTime < rhs->mTime; // older entry in front of queue (set)
}
};
typedef std::set<HeaderEntryInfo*, header_entry_less> header_entry_queue_t;
typedef std::map<U64, HeaderEntryInfo*> handle_entry_map_t;
private:
LLVOCache() ;
public:
~LLVOCache() ;
void initCache(ELLPath location, U32 size, U32 cache_version) ;
void removeCache(ELLPath location) ;
void readFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::vocache_entry_map_t& cache_entry_map) ;
void writeToCache(U64 handle, const LLUUID& id, const LLVOCacheEntry::vocache_entry_map_t& cache_entry_map, BOOL dirty_cache) ;
void setReadOnly(BOOL read_only) {mReadOnly = read_only;}
private:
void setDirNames(ELLPath location);
// determine the cache filename for the region from the region handle
void getObjectCacheFilename(U64 handle, std::string& filename);
void removeFromCache(U64 handle);
void readCacheHeader();
void writeCacheHeader();
void clearCacheInMemory();
void removeCache() ;
void purgeEntries();
BOOL updateEntry(const HeaderEntryInfo* entry);
BOOL checkRead(LLAPRFile* apr_file, void* src, S32 n_bytes) ;
BOOL checkWrite(LLAPRFile* apr_file, void* src, S32 n_bytes) ;
private:
BOOL mInitialized ;
BOOL mReadOnly ;
HeaderMetaInfo mMetaInfo;
U32 mCacheSize;
U32 mNumEntries;
std::string mHeaderFileName ;
std::string mObjectCacheDirName;
LLVolatileAPRPool* mLocalAPRFilePoolp ;
header_entry_queue_t mHeaderEntryQueue;
handle_entry_map_t mHandleEntryMap;
static LLVOCache* sInstance ;
public:
static LLVOCache* getInstance() ;
static BOOL hasInstance() ;
static void destroyClass() ;
};
#endif

View File

@ -121,6 +121,7 @@ void LLWorld::destroyClass()
LLViewerRegion* region_to_delete = *region_it++;
removeRegion(region_to_delete->getHost());
}
LLVOCache::getInstance()->destroyClass() ;
LLViewerPartSim::getInstance()->destroyClass();
}
@ -256,6 +257,8 @@ void LLWorld::removeRegion(const LLHost &host)
llwarns << "Disabling region " << regionp->getName() << " that agent is in!" << llendl;
LLAppViewer::instance()->forceDisconnect(LLTrans::getString("YouHaveBeenDisconnected"));
regionp->saveObjectCache() ; //force to save objects here in case that the object cache is about to be destroyed.
return;
}