diff options
Diffstat (limited to 'indra/newview/llvocache.cpp')
-rw-r--r-- | indra/newview/llvocache.cpp | 574 |
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, ®ion_x, ®ion_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 ; +} + |