720 lines
18 KiB
C++
720 lines
18 KiB
C++
/**
|
|
* @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";
|
|
|
|
// Throw out 1/20 (5%) of our cache entries if we run out of room.
|
|
const U32 ENTRIES_PURGE_FACTOR = 20;
|
|
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),
|
|
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 ;
|
|
}
|
|
|
|
setDirNames(location);
|
|
if (!mReadOnly)
|
|
{
|
|
LLFile::mkdir(mObjectCacheDirName);
|
|
}
|
|
|
|
mCacheSize = size;
|
|
|
|
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)
|
|
{
|
|
llwarns << "Not removing cache at " << location << ": Cache is currently in read-only mode." << llendl;
|
|
return ;
|
|
}
|
|
|
|
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 ;
|
|
}
|
|
|
|
std::string delem = gDirUtilp->getDirDelimiter();
|
|
std::string mask = delem + "*";
|
|
llinfos << "Removing cache at " << mObjectCacheDirName << llendl;
|
|
gDirUtilp->deleteFilesInDir(mObjectCacheDirName, mask);
|
|
|
|
clearCacheInMemory() ;
|
|
writeCacheHeader();
|
|
}
|
|
|
|
void LLVOCache::clearCacheInMemory()
|
|
{
|
|
std::for_each(mHandleEntryMap.begin(), mHandleEntryMap.end(), DeletePairedPointer());
|
|
mHandleEntryMap.clear();
|
|
}
|
|
|
|
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)
|
|
{
|
|
llwarns << "Not removing cache for handle " << handle << ": Cache is currently in read-only mode." << llendl;
|
|
return ;
|
|
}
|
|
|
|
std::string filename;
|
|
getObjectCacheFilename(handle, filename);
|
|
LLAPRFile::remove(filename, mLocalAPRFilePoolp);
|
|
}
|
|
|
|
BOOL LLVOCache::checkRead(LLAPRFile* apr_file, void* src, S32 n_bytes, bool remove_cache_on_error)
|
|
{
|
|
if(!check_read(apr_file, src, n_bytes))
|
|
{
|
|
if (remove_cache_on_error)
|
|
{
|
|
removeCache() ;
|
|
}
|
|
return FALSE ;
|
|
}
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
BOOL LLVOCache::checkWrite(LLAPRFile* apr_file, void* src, S32 n_bytes, bool remove_cache_on_error)
|
|
{
|
|
if(!check_write(apr_file, src, n_bytes))
|
|
{
|
|
if (remove_cache_on_error)
|
|
{
|
|
removeCache() ;
|
|
}
|
|
return FALSE ;
|
|
}
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
void LLVOCache::readCacheHeader()
|
|
{
|
|
if(!mEnabled)
|
|
{
|
|
llwarns << "Not reading cache header: Cache is currently disabled." << llendl;
|
|
return;
|
|
}
|
|
|
|
//clear stale info.
|
|
clearCacheInMemory();
|
|
|
|
if (LLAPRFile::isExist(mHeaderFileName, mLocalAPRFilePoolp))
|
|
{
|
|
LLAPRFile* apr_file = new LLAPRFile(mHeaderFileName, APR_FOPEN_READ|APR_FOPEN_BINARY, mLocalAPRFilePoolp);
|
|
|
|
//read the meta element
|
|
bool remove_cache_on_error = false;
|
|
if(!checkRead(apr_file, &mMetaInfo, sizeof(HeaderMetaInfo), remove_cache_on_error))
|
|
{
|
|
llwarns << "Error reading meta information from cache header." << llendl;
|
|
delete apr_file;
|
|
return;
|
|
}
|
|
|
|
HeaderEntryInfo* entry ;
|
|
for(U32 entry_index = 0; entry_index < mCacheSize; ++entry_index)
|
|
{
|
|
entry = new HeaderEntryInfo() ;
|
|
if(!checkRead(apr_file, entry, sizeof(HeaderEntryInfo), remove_cache_on_error))
|
|
{
|
|
llwarns << "Error reading cache header entry. (entry_index=" << entry_index << ")" << llendl;
|
|
delete entry ;
|
|
break;
|
|
}
|
|
else if(!entry->mTime) //end of the cache.
|
|
{
|
|
delete entry ;
|
|
break;
|
|
}
|
|
|
|
entry->mIndex = entry_index;
|
|
mHandleEntryMap[entry->mHandle] = entry;
|
|
}
|
|
|
|
delete apr_file ;
|
|
}
|
|
else
|
|
{
|
|
writeCacheHeader() ;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
LLAPRFile* apr_file = new LLAPRFile(mHeaderFileName, APR_FOPEN_CREATE|APR_FOPEN_WRITE|APR_FOPEN_BINARY|APR_FOPEN_TRUNCATE, mLocalAPRFilePoolp);
|
|
|
|
//write the meta element
|
|
if(!checkWrite(apr_file, &mMetaInfo, sizeof(HeaderMetaInfo)))
|
|
{
|
|
llwarns << "Error writing meta information to cache header." << llendl;
|
|
delete apr_file;
|
|
return;
|
|
}
|
|
|
|
U32 entry_index = 0;
|
|
handle_entry_map_t::iterator iter_end = mHandleEntryMap.end();
|
|
for(handle_entry_map_t::iterator iter = mHandleEntryMap.begin();
|
|
iter != iter_end;
|
|
++iter)
|
|
{
|
|
HeaderEntryInfo* entry = iter->second;
|
|
entry->mIndex = entry_index++;
|
|
if(!checkWrite(apr_file, (void*)entry, sizeof(HeaderEntryInfo)))
|
|
{
|
|
llwarns << "Failed to write cache header for entry " << entry->mHandle << " (entry_index = " << entry_index << ")" << llendl;
|
|
delete apr_file;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Why do we need to fill the cache header with default entries? DK 2010-12-14
|
|
// It looks like we currently rely on the file being pre-allocated so we can seek during updateEntry().
|
|
if(entry_index < mCacheSize)
|
|
{
|
|
HeaderEntryInfo* entry = new HeaderEntryInfo() ;
|
|
for(; entry_index < mCacheSize; ++entry_index)
|
|
{
|
|
//fill the cache with the default entry.
|
|
if(!checkWrite(apr_file, entry, sizeof(HeaderEntryInfo)))
|
|
{
|
|
llwarns << "Failed to fill cache header with default entries (entry_index = " << entry_index << "). Switching to read-only mode." << llendl;
|
|
mReadOnly = TRUE ; //disable the cache.
|
|
break;
|
|
}
|
|
}
|
|
delete entry ;
|
|
}
|
|
delete apr_file ;
|
|
}
|
|
|
|
BOOL LLVOCache::updateEntry(const HeaderEntryInfo* entry)
|
|
{
|
|
LLAPRFile* apr_file = new LLAPRFile(mHeaderFileName, APR_FOPEN_WRITE|APR_FOPEN_BINARY, mLocalAPRFilePoolp);
|
|
apr_file->seek(APR_SET, entry->mIndex * sizeof(HeaderEntryInfo) + sizeof(HeaderMetaInfo)) ;
|
|
|
|
BOOL result = checkWrite(apr_file, (void*)entry, sizeof(HeaderEntryInfo)) ;
|
|
delete apr_file;
|
|
return result;
|
|
}
|
|
|
|
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 ;
|
|
}
|
|
|
|
std::string filename;
|
|
getObjectCacheFilename(handle, filename);
|
|
LLAPRFile* apr_file = new LLAPRFile(filename, APR_FOPEN_READ|APR_FOPEN_BINARY, mLocalAPRFilePoolp);
|
|
|
|
LLUUID cache_id ;
|
|
if(!checkRead(apr_file, cache_id.mData, UUID_BYTES))
|
|
{
|
|
llwarns << "Error reading cache_id from " << filename << llendl;
|
|
delete apr_file;
|
|
return ;
|
|
}
|
|
if(cache_id != id)
|
|
{
|
|
llwarns << "Cache ID (" << cache_id << ") doesn't match id for this region (" << id << "), discarding. handle = " << handle << llendl;
|
|
delete apr_file ;
|
|
return ;
|
|
}
|
|
|
|
S32 num_entries;
|
|
if(!checkRead(apr_file, &num_entries, sizeof(S32)))
|
|
{
|
|
llwarns << "Error reading num_entries from " << filename << llendl;
|
|
delete apr_file;
|
|
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! (entry number = " << i << ")" << llendl;
|
|
delete entry ;
|
|
break;
|
|
}
|
|
cache_entry_map[entry->getLocalID()] = entry;
|
|
}
|
|
|
|
delete apr_file ;
|
|
return ;
|
|
}
|
|
|
|
void LLVOCache::purgeEntries()
|
|
{
|
|
U32 limit = mCacheSize - (mCacheSize / ENTRIES_PURGE_FACTOR);
|
|
limit = llclamp(limit, (U32)1, mCacheSize);
|
|
// Construct a vector of entries out of the map so we can sort by time.
|
|
std::vector<HeaderEntryInfo*> header_vector;
|
|
handle_entry_map_t::iterator iter_end = mHandleEntryMap.end();
|
|
for (handle_entry_map_t::iterator iter = mHandleEntryMap.begin();
|
|
iter != iter_end;
|
|
++iter)
|
|
{
|
|
header_vector.push_back(iter->second);
|
|
}
|
|
// Sort by time, oldest first.
|
|
std::sort(header_vector.begin(), header_vector.end(), header_entry_less());
|
|
while(header_vector.size() > limit)
|
|
{
|
|
HeaderEntryInfo* entry = header_vector.front();
|
|
|
|
removeFromCache(entry->mHandle);
|
|
mHandleEntryMap.erase(entry->mHandle);
|
|
header_vector.erase(header_vector.begin());
|
|
delete entry;
|
|
}
|
|
|
|
writeCacheHeader() ;
|
|
// *TODO: Verify that we can avoid re-reading the cache header. DK 2010-12-14
|
|
readCacheHeader() ;
|
|
}
|
|
|
|
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 ;
|
|
}
|
|
|
|
U32 num_handle_entries = mHandleEntryMap.size();
|
|
|
|
HeaderEntryInfo* entry;
|
|
handle_entry_map_t::iterator iter = mHandleEntryMap.find(handle) ;
|
|
if(iter == mHandleEntryMap.end()) //new entry
|
|
{
|
|
if(num_handle_entries >= mCacheSize)
|
|
{
|
|
purgeEntries() ;
|
|
num_handle_entries = mHandleEntryMap.size();
|
|
}
|
|
|
|
entry = new HeaderEntryInfo();
|
|
entry->mHandle = handle ;
|
|
entry->mTime = time(NULL) ;
|
|
entry->mIndex = num_handle_entries++;
|
|
mHandleEntryMap[handle] = entry ;
|
|
}
|
|
else
|
|
{
|
|
// Update access time.
|
|
entry = iter->second ;
|
|
entry->mTime = time(NULL) ;
|
|
}
|
|
|
|
//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
|
|
std::string filename;
|
|
getObjectCacheFilename(handle, filename);
|
|
LLAPRFile* apr_file = new LLAPRFile(filename, APR_FOPEN_CREATE|APR_FOPEN_WRITE|APR_FOPEN_BINARY|APR_FOPEN_TRUNCATE, mLocalAPRFilePoolp);
|
|
|
|
if(!checkWrite(apr_file, (void*)id.mData, UUID_BYTES))
|
|
{
|
|
llwarns << "Error writing id to " << filename << llendl;
|
|
delete apr_file;
|
|
return ;
|
|
}
|
|
|
|
S32 num_entries = cache_entry_map.size() ;
|
|
if(!checkWrite(apr_file, &num_entries, sizeof(S32)))
|
|
{
|
|
llwarns << "Error writing num_entries to " << filename << llendl;
|
|
delete apr_file;
|
|
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))
|
|
{
|
|
llwarns << "Aborting cache file write for " << filename << ", error writing to file!" << llendl;
|
|
//failed
|
|
removeCache() ;
|
|
break;
|
|
}
|
|
}
|
|
|
|
delete apr_file ;
|
|
return ;
|
|
}
|
|
|