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.cpp574
1 files changed, 523 insertions, 51 deletions
diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp
index 3f1c132e77..0b903e62b1 100644
--- a/indra/newview/llvocache.cpp
+++ b/indra/newview/llvocache.cpp
@@ -2,39 +2,42 @@
* @file llvocache.cpp
* @brief Cache of objects on the viewer.
*
- * $LicenseInfo:firstyear=2003&license=viewergpl$
- *
- * Copyright (c) 2003-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 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.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * 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.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * 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"
+
+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
@@ -63,26 +66,31 @@ LLVOCacheEntry::LLVOCacheEntry()
mDP.assignBuffer(mBuffer, 0);
}
+LLVOCacheEntry::LLVOCacheEntry(LLAPRFile* apr_file)
+{
+ S32 size = -1;
+ BOOL success;
-static inline void checkedRead(LLFILE *fp, void *data, size_t nbytes)
+ success = check_read(apr_file, &mLocalID, sizeof(U32));
+ if(success)
{
- if (fread(data, 1, nbytes, fp) != nbytes)
+ success = check_read(apr_file, &mCRC, sizeof(U32));
+ }
+ if(success)
{
- llwarns << "Short read" << llendl;
- memset(data, 0, nbytes);
+ 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))
@@ -96,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()
{
@@ -154,22 +181,467 @@ 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)
+ {
+ 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)
{
- llwarns << "Short write" << llendl;
+ 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 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 ;
}
-void LLVOCacheEntry::writeToFile(LLFILE *fp) const
+//static
+BOOL LLVOCache::hasInstance()
{
- 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 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 ;
+}
+