summaryrefslogtreecommitdiff
path: root/indra/newview/llvocache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/llvocache.cpp')
-rw-r--r--indra/newview/llvocache.cpp1504
1 files changed, 752 insertions, 752 deletions
diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp
index b3312db4a0..d0738a789f 100644
--- a/indra/newview/llvocache.cpp
+++ b/indra/newview/llvocache.cpp
@@ -1,752 +1,752 @@
-/**
- * @file llvocache.cpp
- * @brief Cache of objects on the viewer.
- *
- * $LicenseInfo:firstyear=2003&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "llviewerprecompiledheaders.h"
-#include "llvocache.h"
-#include "llerror.h"
-#include "llregionhandle.h"
-#include "llviewercontrol.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
-//---------------------------------------------------------------------------
-
-LLVOCacheEntry::LLVOCacheEntry(U32 local_id, U32 crc, LLDataPackerBinaryBuffer &dp)
- :
- mLocalID(local_id),
- mCRC(crc),
- mHitCount(0),
- mDupeCount(0),
- mCRCChangeCount(0)
-{
- mBuffer = new U8[dp.getBufferSize()];
- mDP.assignBuffer(mBuffer, dp.getBufferSize());
- mDP = dp;
-}
-
-LLVOCacheEntry::LLVOCacheEntry()
- :
- mLocalID(0),
- mCRC(0),
- mHitCount(0),
- mDupeCount(0),
- mCRCChangeCount(0),
- mBuffer(NULL)
-{
- mDP.assignBuffer(mBuffer, 0);
-}
-
-LLVOCacheEntry::LLVOCacheEntry(LLAPRFile* apr_file)
-{
- 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));
- }
- if(success)
- {
- 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))
- {
- // We've got a bogus size, skip reading it.
- // We won't bother seeking, because the rest of this file
- // is likely bogus, and will be tossed anyway.
- llwarns << "Bogus cache entry, size " << size << ", aborting!" << llendl;
- success = FALSE;
- }
- }
- if(success && size > 0)
- {
- mBuffer = new U8[size];
- success = check_read(apr_file, mBuffer, size);
-
- if(success)
- {
- mDP.assignBuffer(mBuffer, size);
- }
- else
- {
- delete[] mBuffer ;
- mBuffer = NULL ;
- }
- }
-
- if(!success)
- {
- mLocalID = 0;
- mCRC = 0;
- mHitCount = 0;
- mDupeCount = 0;
- mCRCChangeCount = 0;
- mBuffer = NULL;
- }
-}
-
-LLVOCacheEntry::~LLVOCacheEntry()
-{
- delete [] mBuffer;
-}
-
-
-// New CRC means the object has changed.
-void LLVOCacheEntry::assignCRC(U32 crc, LLDataPackerBinaryBuffer &dp)
-{
- if ( (mCRC != crc)
- ||(mDP.getBufferSize() == 0))
- {
- mCRC = crc;
- mHitCount = 0;
- mCRCChangeCount++;
-
- mDP.freeBuffer();
- mBuffer = new U8[dp.getBufferSize()];
- mDP.assignBuffer(mBuffer, dp.getBufferSize());
- mDP = dp;
- }
-}
-
-LLDataPackerBinaryBuffer *LLVOCacheEntry::getDP(U32 crc)
-{
- if ( (mCRC != crc)
- ||(mDP.getBufferSize() == 0))
- {
- //llinfos << "Not getting cache entry, invalid!" << llendl;
- return NULL;
- }
- mHitCount++;
- return &mDP;
-}
-
-
-void LLVOCacheEntry::recordHit()
-{
- mHitCount++;
-}
-
-
-void LLVOCacheEntry::dump() const
-{
- llinfos << "local " << mLocalID
- << " crc " << mCRC
- << " hits " << mHitCount
- << " dupes " << mDupeCount
- << " change " << mCRCChangeCount
- << llendl;
-}
-
-BOOL LLVOCacheEntry::writeToFile(LLAPRFile* apr_file) const
-{
- BOOL success;
- success = check_write(apr_file, (void*)&mLocalID, sizeof(U32));
- if(success)
- {
- 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);
- }
- }
-
- 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 MIN_ENTRIES_TO_PURGE = 16 ;
-const U32 INVALID_TIME = 0 ;
-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),
- mCacheSize(1)
-{
- mEnabled = gSavedSettings.getBOOL("ObjectCacheEnabled");
- mLocalAPRFilePoolp = new LLVolatileAPRPool() ;
-}
-
-LLVOCache::~LLVOCache()
-{
- if(mEnabled)
- {
- 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(!mEnabled)
- {
- llwarns << "Not initializing cache: Cache is currently disabled." << llendl;
- return ;
- }
-
- if(mInitialized)
- {
- llwarns << "Cache already initialized." << llendl;
- return ;
- }
- mInitialized = TRUE ;
-
- setDirNames(location);
- if (!mReadOnly)
- {
- LLFile::mkdir(mObjectCacheDirName);
- }
- mCacheSize = llclamp(size, MIN_ENTRIES_TO_PURGE, MAX_NUM_OBJECT_ENTRIES);
- mMetaInfo.mVersion = cache_version;
- readCacheHeader();
-
- 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)
- {
- llwarns << "Not removing cache at " << location << ": Cache is currently in read-only mode." << llendl;
- return ;
- }
-
- llinfos << "about to remove the object cache due to settings." << llendl ;
-
- std::string delem = gDirUtilp->getDirDelimiter();
- std::string mask = delem + "*";
- std::string cache_dir = gDirUtilp->getExpandedFilename(location, object_cache_dirname);
- llinfos << "Removing cache at " << cache_dir << llendl;
- gDirUtilp->deleteFilesInDir(cache_dir, mask); //delete all files
- LLFile::rmdir(cache_dir);
-
- clearCacheInMemory();
- mInitialized = FALSE ;
-}
-
-void LLVOCache::removeCache()
-{
- llassert_always(mInitialized) ;
- if(mReadOnly)
- {
- llwarns << "Not clearing object cache: Cache is currently in read-only mode." << llendl;
- return ;
- }
-
- llinfos << "about to remove the object cache due to some error." << llendl ;
-
- std::string delem = gDirUtilp->getDirDelimiter();
- std::string mask = delem + "*";
- llinfos << "Removing cache at " << mObjectCacheDirName << llendl;
- gDirUtilp->deleteFilesInDir(mObjectCacheDirName, mask);
-
- clearCacheInMemory() ;
- writeCacheHeader();
-}
-
-void LLVOCache::removeEntry(HeaderEntryInfo* entry)
-{
- llassert_always(mInitialized) ;
- if(mReadOnly)
- {
- return ;
- }
- if(!entry)
- {
- return ;
- }
-
- header_entry_queue_t::iterator iter = mHeaderEntryQueue.find(entry) ;
- if(iter != mHeaderEntryQueue.end())
- {
- mHandleEntryMap.erase(entry->mHandle) ;
- mHeaderEntryQueue.erase(iter) ;
- removeFromCache(entry) ;
- delete entry ;
-
- mNumEntries = mHandleEntryMap.size() ;
- }
-}
-
-void LLVOCache::removeEntry(U64 handle)
-{
- handle_entry_map_t::iterator iter = mHandleEntryMap.find(handle) ;
- if(iter == mHandleEntryMap.end()) //no cache
- {
- return ;
- }
- HeaderEntryInfo* entry = iter->second ;
- removeEntry(entry) ;
-}
-
-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(HeaderEntryInfo* entry)
-{
- if(mReadOnly)
- {
- llwarns << "Not removing cache for handle " << entry->mHandle << ": Cache is currently in read-only mode." << llendl;
- return ;
- }
-
- std::string filename;
- getObjectCacheFilename(entry->mHandle, filename);
- LLAPRFile::remove(filename, mLocalAPRFilePoolp);
- entry->mTime = INVALID_TIME ;
- updateEntry(entry) ; //update the head file.
-}
-
-void LLVOCache::readCacheHeader()
-{
- if(!mEnabled)
- {
- llwarns << "Not reading cache header: Cache is currently disabled." << llendl;
- return;
- }
-
- //clear stale info.
- clearCacheInMemory();
-
- bool success = true ;
- if (LLAPRFile::isExist(mHeaderFileName, mLocalAPRFilePoolp))
- {
- LLAPRFile apr_file(mHeaderFileName, APR_READ|APR_BINARY, mLocalAPRFilePoolp);
-
- //read the meta element
- success = check_read(&apr_file, &mMetaInfo, sizeof(HeaderMetaInfo)) ;
-
- if(success)
- {
- HeaderEntryInfo* entry = NULL ;
- mNumEntries = 0 ;
- U32 num_read = 0 ;
- while(num_read++ < MAX_NUM_OBJECT_ENTRIES)
- {
- if(!entry)
- {
- entry = new HeaderEntryInfo() ;
- }
- success = check_read(&apr_file, entry, sizeof(HeaderEntryInfo));
-
- if(!success) //failed
- {
- llwarns << "Error reading cache header entry. (entry_index=" << mNumEntries << ")" << llendl;
- delete entry ;
- entry = NULL ;
- break ;
- }
- else if(entry->mTime == INVALID_TIME)
- {
- continue ; //an empty entry
- }
-
- entry->mIndex = mNumEntries++ ;
- mHeaderEntryQueue.insert(entry) ;
- mHandleEntryMap[entry->mHandle] = entry ;
- entry = NULL ;
- }
- if(entry)
- {
- delete entry ;
- }
- }
-
- //---------
- //debug code
- //----------
- //std::string name ;
- //for(header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin() ; success && iter != mHeaderEntryQueue.end(); ++iter)
- //{
- // getObjectCacheFilename((*iter)->mHandle, name) ;
- // llinfos << name << llendl ;
- //}
- //-----------
- }
- else
- {
- writeCacheHeader() ;
- }
-
- if(!success)
- {
- removeCache() ; //failed to read header, clear the cache
- }
- else if(mNumEntries >= mCacheSize)
- {
- purgeEntries(mCacheSize) ;
- }
-
- return ;
-}
-
-void LLVOCache::writeCacheHeader()
-{
- if (!mEnabled)
- {
- llwarns << "Not writing cache header: Cache is currently disabled." << llendl;
- return;
- }
-
- if(mReadOnly)
- {
- llwarns << "Not writing cache header: Cache is currently in read-only mode." << llendl;
- return;
- }
-
- bool success = true ;
- {
- LLAPRFile apr_file(mHeaderFileName, APR_CREATE|APR_WRITE|APR_BINARY, mLocalAPRFilePoolp);
-
- //write the meta element
- success = check_write(&apr_file, &mMetaInfo, sizeof(HeaderMetaInfo)) ;
-
-
- mNumEntries = 0 ;
- for(header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin() ; success && iter != mHeaderEntryQueue.end(); ++iter)
- {
- (*iter)->mIndex = mNumEntries++ ;
- success = check_write(&apr_file, (void*)*iter, sizeof(HeaderEntryInfo));
- }
-
- mNumEntries = mHeaderEntryQueue.size() ;
- if(success && mNumEntries < MAX_NUM_OBJECT_ENTRIES)
- {
- HeaderEntryInfo* entry = new HeaderEntryInfo() ;
- entry->mTime = INVALID_TIME ;
- for(S32 i = mNumEntries ; success && i < MAX_NUM_OBJECT_ENTRIES ; i++)
- {
- //fill the cache with the default entry.
- success = check_write(&apr_file, entry, sizeof(HeaderEntryInfo)) ;
-
- }
- delete entry ;
- }
- }
-
- if(!success)
- {
- clearCacheInMemory() ;
- mReadOnly = TRUE ; //disable the cache.
- }
- return ;
-}
-
-BOOL LLVOCache::updateEntry(const HeaderEntryInfo* entry)
-{
- LLAPRFile apr_file(mHeaderFileName, APR_WRITE|APR_BINARY, mLocalAPRFilePoolp);
- apr_file.seek(APR_SET, entry->mIndex * sizeof(HeaderEntryInfo) + sizeof(HeaderMetaInfo)) ;
-
- return check_write(&apr_file, (void*)entry, sizeof(HeaderEntryInfo)) ;
-}
-
-void LLVOCache::readFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::vocache_entry_map_t& cache_entry_map)
-{
- if(!mEnabled)
- {
- llwarns << "Not reading cache for handle " << handle << "): Cache is currently disabled." << llendl;
- return ;
- }
- llassert_always(mInitialized);
-
- handle_entry_map_t::iterator iter = mHandleEntryMap.find(handle) ;
- if(iter == mHandleEntryMap.end()) //no cache
- {
- llwarns << "No handle map entry for " << handle << llendl;
- return ;
- }
-
- bool success = true ;
- {
- std::string filename;
- getObjectCacheFilename(handle, filename);
- LLAPRFile apr_file(filename, APR_READ|APR_BINARY, mLocalAPRFilePoolp);
-
- LLUUID cache_id ;
- success = check_read(&apr_file, cache_id.mData, UUID_BYTES) ;
-
- if(success)
- {
- if(cache_id != id)
- {
- llinfos << "Cache ID doesn't match for this region, discarding"<< llendl;
- success = false ;
- }
-
- if(success)
- {
- S32 num_entries;
- success = check_read(&apr_file, &num_entries, sizeof(S32)) ;
-
- for (S32 i = 0; success && 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 ;
- success = false ;
- }
- cache_entry_map[entry->getLocalID()] = entry;
- }
- }
- }
- }
-
- if(!success)
- {
- if(cache_entry_map.empty())
- {
- removeEntry(iter->second) ;
- }
- }
-
- return ;
-}
-
-void LLVOCache::purgeEntries(U32 size)
-{
- while(mHeaderEntryQueue.size() >= size)
- {
- header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin() ;
- HeaderEntryInfo* entry = *iter ;
- mHandleEntryMap.erase(entry->mHandle);
- mHeaderEntryQueue.erase(iter) ;
- removeFromCache(entry) ;
- delete entry;
- }
- mNumEntries = mHandleEntryMap.size() ;
-}
-
-void LLVOCache::writeToCache(U64 handle, const LLUUID& id, const LLVOCacheEntry::vocache_entry_map_t& cache_entry_map, BOOL dirty_cache)
-{
- if(!mEnabled)
- {
- llwarns << "Not writing cache for handle " << handle << "): Cache is currently disabled." << llendl;
- return ;
- }
- llassert_always(mInitialized);
-
- if(mReadOnly)
- {
- llwarns << "Not writing cache for handle " << handle << "): Cache is currently in read-only mode." << llendl;
- return ;
- }
-
- HeaderEntryInfo* entry;
- handle_entry_map_t::iterator iter = mHandleEntryMap.find(handle) ;
- if(iter == mHandleEntryMap.end()) //new entry
- {
- if(mNumEntries >= mCacheSize - 1)
- {
- purgeEntries(mCacheSize - 1) ;
- }
-
- entry = new HeaderEntryInfo();
- entry->mHandle = handle ;
- entry->mTime = time(NULL) ;
- entry->mIndex = mNumEntries++;
- mHeaderEntryQueue.insert(entry) ;
- mHandleEntryMap[handle] = entry ;
- }
- else
- {
- // Update access time.
- entry = iter->second ;
-
- //resort
- mHeaderEntryQueue.erase(entry) ;
-
- entry->mTime = time(NULL) ;
- mHeaderEntryQueue.insert(entry) ;
- }
-
- //update cache header
- if(!updateEntry(entry))
- {
- llwarns << "Failed to update cache header index " << entry->mIndex << ". handle = " << handle << llendl;
- return ; //update failed.
- }
-
- if(!dirty_cache)
- {
- llwarns << "Skipping write to cache for handle " << handle << ": cache not dirty" << llendl;
- return ; //nothing changed, no need to update.
- }
-
- //write to cache file
- bool success = true ;
- {
- std::string filename;
- getObjectCacheFilename(handle, filename);
- LLAPRFile apr_file(filename, APR_CREATE|APR_WRITE|APR_BINARY, mLocalAPRFilePoolp);
-
- success = check_write(&apr_file, (void*)id.mData, UUID_BYTES) ;
-
-
- if(success)
- {
- S32 num_entries = cache_entry_map.size() ;
- success = check_write(&apr_file, &num_entries, sizeof(S32));
-
- for (LLVOCacheEntry::vocache_entry_map_t::const_iterator iter = cache_entry_map.begin(); success && iter != cache_entry_map.end(); ++iter)
- {
- success = iter->second->writeToFile(&apr_file) ;
- }
- }
- }
-
- if(!success)
- {
- removeEntry(entry) ;
-
- }
-
- return ;
-}
-
+/**
+ * @file llvocache.cpp
+ * @brief Cache of objects on the viewer.
+ *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llvocache.h"
+#include "llerror.h"
+#include "llregionhandle.h"
+#include "llviewercontrol.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
+//---------------------------------------------------------------------------
+
+LLVOCacheEntry::LLVOCacheEntry(U32 local_id, U32 crc, LLDataPackerBinaryBuffer &dp)
+ :
+ mLocalID(local_id),
+ mCRC(crc),
+ mHitCount(0),
+ mDupeCount(0),
+ mCRCChangeCount(0)
+{
+ mBuffer = new U8[dp.getBufferSize()];
+ mDP.assignBuffer(mBuffer, dp.getBufferSize());
+ mDP = dp;
+}
+
+LLVOCacheEntry::LLVOCacheEntry()
+ :
+ mLocalID(0),
+ mCRC(0),
+ mHitCount(0),
+ mDupeCount(0),
+ mCRCChangeCount(0),
+ mBuffer(NULL)
+{
+ mDP.assignBuffer(mBuffer, 0);
+}
+
+LLVOCacheEntry::LLVOCacheEntry(LLAPRFile* apr_file)
+{
+ 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));
+ }
+ if(success)
+ {
+ 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))
+ {
+ // We've got a bogus size, skip reading it.
+ // We won't bother seeking, because the rest of this file
+ // is likely bogus, and will be tossed anyway.
+ llwarns << "Bogus cache entry, size " << size << ", aborting!" << llendl;
+ success = FALSE;
+ }
+ }
+ if(success && size > 0)
+ {
+ mBuffer = new U8[size];
+ success = check_read(apr_file, mBuffer, size);
+
+ if(success)
+ {
+ mDP.assignBuffer(mBuffer, size);
+ }
+ else
+ {
+ delete[] mBuffer ;
+ mBuffer = NULL ;
+ }
+ }
+
+ if(!success)
+ {
+ mLocalID = 0;
+ mCRC = 0;
+ mHitCount = 0;
+ mDupeCount = 0;
+ mCRCChangeCount = 0;
+ mBuffer = NULL;
+ }
+}
+
+LLVOCacheEntry::~LLVOCacheEntry()
+{
+ delete [] mBuffer;
+}
+
+
+// New CRC means the object has changed.
+void LLVOCacheEntry::assignCRC(U32 crc, LLDataPackerBinaryBuffer &dp)
+{
+ if ( (mCRC != crc)
+ ||(mDP.getBufferSize() == 0))
+ {
+ mCRC = crc;
+ mHitCount = 0;
+ mCRCChangeCount++;
+
+ mDP.freeBuffer();
+ mBuffer = new U8[dp.getBufferSize()];
+ mDP.assignBuffer(mBuffer, dp.getBufferSize());
+ mDP = dp;
+ }
+}
+
+LLDataPackerBinaryBuffer *LLVOCacheEntry::getDP(U32 crc)
+{
+ if ( (mCRC != crc)
+ ||(mDP.getBufferSize() == 0))
+ {
+ //llinfos << "Not getting cache entry, invalid!" << llendl;
+ return NULL;
+ }
+ mHitCount++;
+ return &mDP;
+}
+
+
+void LLVOCacheEntry::recordHit()
+{
+ mHitCount++;
+}
+
+
+void LLVOCacheEntry::dump() const
+{
+ llinfos << "local " << mLocalID
+ << " crc " << mCRC
+ << " hits " << mHitCount
+ << " dupes " << mDupeCount
+ << " change " << mCRCChangeCount
+ << llendl;
+}
+
+BOOL LLVOCacheEntry::writeToFile(LLAPRFile* apr_file) const
+{
+ BOOL success;
+ success = check_write(apr_file, (void*)&mLocalID, sizeof(U32));
+ if(success)
+ {
+ 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);
+ }
+ }
+
+ 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 MIN_ENTRIES_TO_PURGE = 16 ;
+const U32 INVALID_TIME = 0 ;
+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),
+ mCacheSize(1)
+{
+ mEnabled = gSavedSettings.getBOOL("ObjectCacheEnabled");
+ mLocalAPRFilePoolp = new LLVolatileAPRPool() ;
+}
+
+LLVOCache::~LLVOCache()
+{
+ if(mEnabled)
+ {
+ 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(!mEnabled)
+ {
+ llwarns << "Not initializing cache: Cache is currently disabled." << llendl;
+ return ;
+ }
+
+ if(mInitialized)
+ {
+ llwarns << "Cache already initialized." << llendl;
+ return ;
+ }
+ mInitialized = TRUE ;
+
+ setDirNames(location);
+ if (!mReadOnly)
+ {
+ LLFile::mkdir(mObjectCacheDirName);
+ }
+ mCacheSize = llclamp(size, MIN_ENTRIES_TO_PURGE, MAX_NUM_OBJECT_ENTRIES);
+ mMetaInfo.mVersion = cache_version;
+ readCacheHeader();
+
+ 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)
+ {
+ llwarns << "Not removing cache at " << location << ": Cache is currently in read-only mode." << llendl;
+ return ;
+ }
+
+ llinfos << "about to remove the object cache due to settings." << llendl ;
+
+ std::string delem = gDirUtilp->getDirDelimiter();
+ std::string mask = delem + "*";
+ std::string cache_dir = gDirUtilp->getExpandedFilename(location, object_cache_dirname);
+ llinfos << "Removing cache at " << cache_dir << llendl;
+ gDirUtilp->deleteFilesInDir(cache_dir, mask); //delete all files
+ LLFile::rmdir(cache_dir);
+
+ clearCacheInMemory();
+ mInitialized = FALSE ;
+}
+
+void LLVOCache::removeCache()
+{
+ llassert_always(mInitialized) ;
+ if(mReadOnly)
+ {
+ llwarns << "Not clearing object cache: Cache is currently in read-only mode." << llendl;
+ return ;
+ }
+
+ llinfos << "about to remove the object cache due to some error." << llendl ;
+
+ std::string delem = gDirUtilp->getDirDelimiter();
+ std::string mask = delem + "*";
+ llinfos << "Removing cache at " << mObjectCacheDirName << llendl;
+ gDirUtilp->deleteFilesInDir(mObjectCacheDirName, mask);
+
+ clearCacheInMemory() ;
+ writeCacheHeader();
+}
+
+void LLVOCache::removeEntry(HeaderEntryInfo* entry)
+{
+ llassert_always(mInitialized) ;
+ if(mReadOnly)
+ {
+ return ;
+ }
+ if(!entry)
+ {
+ return ;
+ }
+
+ header_entry_queue_t::iterator iter = mHeaderEntryQueue.find(entry) ;
+ if(iter != mHeaderEntryQueue.end())
+ {
+ mHandleEntryMap.erase(entry->mHandle) ;
+ mHeaderEntryQueue.erase(iter) ;
+ removeFromCache(entry) ;
+ delete entry ;
+
+ mNumEntries = mHandleEntryMap.size() ;
+ }
+}
+
+void LLVOCache::removeEntry(U64 handle)
+{
+ handle_entry_map_t::iterator iter = mHandleEntryMap.find(handle) ;
+ if(iter == mHandleEntryMap.end()) //no cache
+ {
+ return ;
+ }
+ HeaderEntryInfo* entry = iter->second ;
+ removeEntry(entry) ;
+}
+
+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(HeaderEntryInfo* entry)
+{
+ if(mReadOnly)
+ {
+ llwarns << "Not removing cache for handle " << entry->mHandle << ": Cache is currently in read-only mode." << llendl;
+ return ;
+ }
+
+ std::string filename;
+ getObjectCacheFilename(entry->mHandle, filename);
+ LLAPRFile::remove(filename, mLocalAPRFilePoolp);
+ entry->mTime = INVALID_TIME ;
+ updateEntry(entry) ; //update the head file.
+}
+
+void LLVOCache::readCacheHeader()
+{
+ if(!mEnabled)
+ {
+ llwarns << "Not reading cache header: Cache is currently disabled." << llendl;
+ return;
+ }
+
+ //clear stale info.
+ clearCacheInMemory();
+
+ bool success = true ;
+ if (LLAPRFile::isExist(mHeaderFileName, mLocalAPRFilePoolp))
+ {
+ LLAPRFile apr_file(mHeaderFileName, APR_READ|APR_BINARY, mLocalAPRFilePoolp);
+
+ //read the meta element
+ success = check_read(&apr_file, &mMetaInfo, sizeof(HeaderMetaInfo)) ;
+
+ if(success)
+ {
+ HeaderEntryInfo* entry = NULL ;
+ mNumEntries = 0 ;
+ U32 num_read = 0 ;
+ while(num_read++ < MAX_NUM_OBJECT_ENTRIES)
+ {
+ if(!entry)
+ {
+ entry = new HeaderEntryInfo() ;
+ }
+ success = check_read(&apr_file, entry, sizeof(HeaderEntryInfo));
+
+ if(!success) //failed
+ {
+ llwarns << "Error reading cache header entry. (entry_index=" << mNumEntries << ")" << llendl;
+ delete entry ;
+ entry = NULL ;
+ break ;
+ }
+ else if(entry->mTime == INVALID_TIME)
+ {
+ continue ; //an empty entry
+ }
+
+ entry->mIndex = mNumEntries++ ;
+ mHeaderEntryQueue.insert(entry) ;
+ mHandleEntryMap[entry->mHandle] = entry ;
+ entry = NULL ;
+ }
+ if(entry)
+ {
+ delete entry ;
+ }
+ }
+
+ //---------
+ //debug code
+ //----------
+ //std::string name ;
+ //for(header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin() ; success && iter != mHeaderEntryQueue.end(); ++iter)
+ //{
+ // getObjectCacheFilename((*iter)->mHandle, name) ;
+ // llinfos << name << llendl ;
+ //}
+ //-----------
+ }
+ else
+ {
+ writeCacheHeader() ;
+ }
+
+ if(!success)
+ {
+ removeCache() ; //failed to read header, clear the cache
+ }
+ else if(mNumEntries >= mCacheSize)
+ {
+ purgeEntries(mCacheSize) ;
+ }
+
+ return ;
+}
+
+void LLVOCache::writeCacheHeader()
+{
+ if (!mEnabled)
+ {
+ llwarns << "Not writing cache header: Cache is currently disabled." << llendl;
+ return;
+ }
+
+ if(mReadOnly)
+ {
+ llwarns << "Not writing cache header: Cache is currently in read-only mode." << llendl;
+ return;
+ }
+
+ bool success = true ;
+ {
+ LLAPRFile apr_file(mHeaderFileName, APR_CREATE|APR_WRITE|APR_BINARY, mLocalAPRFilePoolp);
+
+ //write the meta element
+ success = check_write(&apr_file, &mMetaInfo, sizeof(HeaderMetaInfo)) ;
+
+
+ mNumEntries = 0 ;
+ for(header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin() ; success && iter != mHeaderEntryQueue.end(); ++iter)
+ {
+ (*iter)->mIndex = mNumEntries++ ;
+ success = check_write(&apr_file, (void*)*iter, sizeof(HeaderEntryInfo));
+ }
+
+ mNumEntries = mHeaderEntryQueue.size() ;
+ if(success && mNumEntries < MAX_NUM_OBJECT_ENTRIES)
+ {
+ HeaderEntryInfo* entry = new HeaderEntryInfo() ;
+ entry->mTime = INVALID_TIME ;
+ for(S32 i = mNumEntries ; success && i < MAX_NUM_OBJECT_ENTRIES ; i++)
+ {
+ //fill the cache with the default entry.
+ success = check_write(&apr_file, entry, sizeof(HeaderEntryInfo)) ;
+
+ }
+ delete entry ;
+ }
+ }
+
+ if(!success)
+ {
+ clearCacheInMemory() ;
+ mReadOnly = TRUE ; //disable the cache.
+ }
+ return ;
+}
+
+BOOL LLVOCache::updateEntry(const HeaderEntryInfo* entry)
+{
+ LLAPRFile apr_file(mHeaderFileName, APR_WRITE|APR_BINARY, mLocalAPRFilePoolp);
+ apr_file.seek(APR_SET, entry->mIndex * sizeof(HeaderEntryInfo) + sizeof(HeaderMetaInfo)) ;
+
+ return check_write(&apr_file, (void*)entry, sizeof(HeaderEntryInfo)) ;
+}
+
+void LLVOCache::readFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::vocache_entry_map_t& cache_entry_map)
+{
+ if(!mEnabled)
+ {
+ llwarns << "Not reading cache for handle " << handle << "): Cache is currently disabled." << llendl;
+ return ;
+ }
+ llassert_always(mInitialized);
+
+ handle_entry_map_t::iterator iter = mHandleEntryMap.find(handle) ;
+ if(iter == mHandleEntryMap.end()) //no cache
+ {
+ llwarns << "No handle map entry for " << handle << llendl;
+ return ;
+ }
+
+ bool success = true ;
+ {
+ std::string filename;
+ getObjectCacheFilename(handle, filename);
+ LLAPRFile apr_file(filename, APR_READ|APR_BINARY, mLocalAPRFilePoolp);
+
+ LLUUID cache_id ;
+ success = check_read(&apr_file, cache_id.mData, UUID_BYTES) ;
+
+ if(success)
+ {
+ if(cache_id != id)
+ {
+ llinfos << "Cache ID doesn't match for this region, discarding"<< llendl;
+ success = false ;
+ }
+
+ if(success)
+ {
+ S32 num_entries;
+ success = check_read(&apr_file, &num_entries, sizeof(S32)) ;
+
+ for (S32 i = 0; success && 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 ;
+ success = false ;
+ }
+ cache_entry_map[entry->getLocalID()] = entry;
+ }
+ }
+ }
+ }
+
+ if(!success)
+ {
+ if(cache_entry_map.empty())
+ {
+ removeEntry(iter->second) ;
+ }
+ }
+
+ return ;
+}
+
+void LLVOCache::purgeEntries(U32 size)
+{
+ while(mHeaderEntryQueue.size() >= size)
+ {
+ header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin() ;
+ HeaderEntryInfo* entry = *iter ;
+ mHandleEntryMap.erase(entry->mHandle);
+ mHeaderEntryQueue.erase(iter) ;
+ removeFromCache(entry) ;
+ delete entry;
+ }
+ mNumEntries = mHandleEntryMap.size() ;
+}
+
+void LLVOCache::writeToCache(U64 handle, const LLUUID& id, const LLVOCacheEntry::vocache_entry_map_t& cache_entry_map, BOOL dirty_cache)
+{
+ if(!mEnabled)
+ {
+ llwarns << "Not writing cache for handle " << handle << "): Cache is currently disabled." << llendl;
+ return ;
+ }
+ llassert_always(mInitialized);
+
+ if(mReadOnly)
+ {
+ llwarns << "Not writing cache for handle " << handle << "): Cache is currently in read-only mode." << llendl;
+ return ;
+ }
+
+ HeaderEntryInfo* entry;
+ handle_entry_map_t::iterator iter = mHandleEntryMap.find(handle) ;
+ if(iter == mHandleEntryMap.end()) //new entry
+ {
+ if(mNumEntries >= mCacheSize - 1)
+ {
+ purgeEntries(mCacheSize - 1) ;
+ }
+
+ entry = new HeaderEntryInfo();
+ entry->mHandle = handle ;
+ entry->mTime = time(NULL) ;
+ entry->mIndex = mNumEntries++;
+ mHeaderEntryQueue.insert(entry) ;
+ mHandleEntryMap[handle] = entry ;
+ }
+ else
+ {
+ // Update access time.
+ entry = iter->second ;
+
+ //resort
+ mHeaderEntryQueue.erase(entry) ;
+
+ entry->mTime = time(NULL) ;
+ mHeaderEntryQueue.insert(entry) ;
+ }
+
+ //update cache header
+ if(!updateEntry(entry))
+ {
+ llwarns << "Failed to update cache header index " << entry->mIndex << ". handle = " << handle << llendl;
+ return ; //update failed.
+ }
+
+ if(!dirty_cache)
+ {
+ llwarns << "Skipping write to cache for handle " << handle << ": cache not dirty" << llendl;
+ return ; //nothing changed, no need to update.
+ }
+
+ //write to cache file
+ bool success = true ;
+ {
+ std::string filename;
+ getObjectCacheFilename(handle, filename);
+ LLAPRFile apr_file(filename, APR_CREATE|APR_WRITE|APR_BINARY, mLocalAPRFilePoolp);
+
+ success = check_write(&apr_file, (void*)id.mData, UUID_BYTES) ;
+
+
+ if(success)
+ {
+ S32 num_entries = cache_entry_map.size() ;
+ success = check_write(&apr_file, &num_entries, sizeof(S32));
+
+ for (LLVOCacheEntry::vocache_entry_map_t::const_iterator iter = cache_entry_map.begin(); success && iter != cache_entry_map.end(); ++iter)
+ {
+ success = iter->second->writeToFile(&apr_file) ;
+ }
+ }
+ }
+
+ if(!success)
+ {
+ removeEntry(entry) ;
+
+ }
+
+ return ;
+}
+