PageRenderTime 79ms CodeModel.GetById 2ms app.highlight 69ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/newview/llvocache.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 753 lines | 591 code | 103 blank | 59 comment | 94 complexity | edba3597c6e24463bba78b382dd2f77a MD5 | raw file
  1/** 
  2 * @file llvocache.cpp
  3 * @brief Cache of objects on the viewer.
  4 *
  5 * $LicenseInfo:firstyear=2003&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27#include "llviewerprecompiledheaders.h"
 28#include "llvocache.h"
 29#include "llerror.h"
 30#include "llregionhandle.h"
 31#include "llviewercontrol.h"
 32
 33BOOL check_read(LLAPRFile* apr_file, void* src, S32 n_bytes) 
 34{
 35	return apr_file->read(src, n_bytes) == n_bytes ;
 36}
 37
 38BOOL check_write(LLAPRFile* apr_file, void* src, S32 n_bytes) 
 39{
 40	return apr_file->write(src, n_bytes) == n_bytes ;
 41}
 42
 43
 44//---------------------------------------------------------------------------
 45// LLVOCacheEntry
 46//---------------------------------------------------------------------------
 47
 48LLVOCacheEntry::LLVOCacheEntry(U32 local_id, U32 crc, LLDataPackerBinaryBuffer &dp)
 49	:
 50	mLocalID(local_id),
 51	mCRC(crc),
 52	mHitCount(0),
 53	mDupeCount(0),
 54	mCRCChangeCount(0)
 55{
 56	mBuffer = new U8[dp.getBufferSize()];
 57	mDP.assignBuffer(mBuffer, dp.getBufferSize());
 58	mDP = dp;
 59}
 60
 61LLVOCacheEntry::LLVOCacheEntry()
 62	:
 63	mLocalID(0),
 64	mCRC(0),
 65	mHitCount(0),
 66	mDupeCount(0),
 67	mCRCChangeCount(0),
 68	mBuffer(NULL)
 69{
 70	mDP.assignBuffer(mBuffer, 0);
 71}
 72
 73LLVOCacheEntry::LLVOCacheEntry(LLAPRFile* apr_file)
 74	: mBuffer(NULL)
 75{
 76	S32 size = -1;
 77	BOOL success;
 78
 79	mDP.assignBuffer(mBuffer, 0);
 80	success = check_read(apr_file, &mLocalID, sizeof(U32));
 81	if(success)
 82	{
 83		success = check_read(apr_file, &mCRC, sizeof(U32));
 84	}
 85	if(success)
 86	{
 87		success = check_read(apr_file, &mHitCount, sizeof(S32));
 88	}
 89	if(success)
 90	{
 91		success = check_read(apr_file, &mDupeCount, sizeof(S32));
 92	}
 93	if(success)
 94	{
 95		success = check_read(apr_file, &mCRCChangeCount, sizeof(S32));
 96	}
 97	if(success)
 98	{
 99		success = check_read(apr_file, &size, sizeof(S32));
100
101		// Corruption in the cache entries
102		if ((size > 10000) || (size < 1))
103		{
104			// We've got a bogus size, skip reading it.
105			// We won't bother seeking, because the rest of this file
106			// is likely bogus, and will be tossed anyway.
107			llwarns << "Bogus cache entry, size " << size << ", aborting!" << llendl;
108			success = FALSE;
109		}
110	}
111	if(success && size > 0)
112	{
113		mBuffer = new U8[size];
114		success = check_read(apr_file, mBuffer, size);
115
116		if(success)
117		{
118			mDP.assignBuffer(mBuffer, size);
119		}
120		else
121		{
122			delete[] mBuffer ;
123			mBuffer = NULL ;
124		}
125	}
126
127	if(!success)
128	{
129		mLocalID = 0;
130		mCRC = 0;
131		mHitCount = 0;
132		mDupeCount = 0;
133		mCRCChangeCount = 0;
134		mBuffer = NULL;
135	}
136}
137
138LLVOCacheEntry::~LLVOCacheEntry()
139{
140	mDP.freeBuffer();
141}
142
143
144// New CRC means the object has changed.
145void LLVOCacheEntry::assignCRC(U32 crc, LLDataPackerBinaryBuffer &dp)
146{
147	if (  (mCRC != crc)
148		||(mDP.getBufferSize() == 0))
149	{
150		mCRC = crc;
151		mHitCount = 0;
152		mCRCChangeCount++;
153
154		mDP.freeBuffer();
155		mBuffer = new U8[dp.getBufferSize()];
156		mDP.assignBuffer(mBuffer, dp.getBufferSize());
157		mDP = dp;
158	}
159}
160
161LLDataPackerBinaryBuffer *LLVOCacheEntry::getDP(U32 crc)
162{
163	if (  (mCRC != crc)
164		||(mDP.getBufferSize() == 0))
165	{
166		//llinfos << "Not getting cache entry, invalid!" << llendl;
167		return NULL;
168	}
169	mHitCount++;
170	return &mDP;
171}
172
173
174void LLVOCacheEntry::recordHit()
175{
176	mHitCount++;
177}
178
179
180void LLVOCacheEntry::dump() const
181{
182	llinfos << "local " << mLocalID
183		<< " crc " << mCRC
184		<< " hits " << mHitCount
185		<< " dupes " << mDupeCount
186		<< " change " << mCRCChangeCount
187		<< llendl;
188}
189
190BOOL LLVOCacheEntry::writeToFile(LLAPRFile* apr_file) const
191{
192	BOOL success;
193	success = check_write(apr_file, (void*)&mLocalID, sizeof(U32));
194	if(success)
195	{
196		success = check_write(apr_file, (void*)&mCRC, sizeof(U32));
197	}
198	if(success)
199	{
200		success = check_write(apr_file, (void*)&mHitCount, sizeof(S32));
201	}
202	if(success)
203	{
204		success = check_write(apr_file, (void*)&mDupeCount, sizeof(S32));
205	}
206	if(success)
207	{
208		success = check_write(apr_file, (void*)&mCRCChangeCount, sizeof(S32));
209	}
210	if(success)
211	{
212		S32 size = mDP.getBufferSize();
213		success = check_write(apr_file, (void*)&size, sizeof(S32));
214	
215		if(success)
216		{
217			success = check_write(apr_file, (void*)mBuffer, size);
218		}
219	}
220
221	return success ;
222}
223
224//-------------------------------------------------------------------
225//LLVOCache
226//-------------------------------------------------------------------
227// Format string used to construct filename for the object cache
228static const char OBJECT_CACHE_FILENAME[] = "objects_%d_%d.slc";
229
230const U32 MAX_NUM_OBJECT_ENTRIES = 128 ;
231const U32 MIN_ENTRIES_TO_PURGE = 16 ;
232const U32 INVALID_TIME = 0 ;
233const char* object_cache_dirname = "objectcache";
234const char* header_filename = "object.cache";
235
236LLVOCache* LLVOCache::sInstance = NULL;
237
238//static 
239LLVOCache* LLVOCache::getInstance() 
240{	
241	if(!sInstance)
242	{
243		sInstance = new LLVOCache() ;
244	}
245	return sInstance ;
246}
247
248//static 
249BOOL LLVOCache::hasInstance() 
250{
251	return sInstance != NULL ;
252}
253
254//static 
255void LLVOCache::destroyClass() 
256{
257	if(sInstance)
258	{
259		delete sInstance ;
260		sInstance = NULL ;
261	}
262}
263
264LLVOCache::LLVOCache():
265	mInitialized(FALSE),
266	mReadOnly(TRUE),
267	mNumEntries(0),
268	mCacheSize(1)
269{
270	mEnabled = gSavedSettings.getBOOL("ObjectCacheEnabled");
271	mLocalAPRFilePoolp = new LLVolatileAPRPool() ;
272}
273
274LLVOCache::~LLVOCache()
275{
276	if(mEnabled)
277	{
278		writeCacheHeader();
279		clearCacheInMemory();
280	}
281	delete mLocalAPRFilePoolp;
282}
283
284void LLVOCache::setDirNames(ELLPath location)
285{
286	mHeaderFileName = gDirUtilp->getExpandedFilename(location, object_cache_dirname, header_filename);
287	mObjectCacheDirName = gDirUtilp->getExpandedFilename(location, object_cache_dirname);
288}
289
290void LLVOCache::initCache(ELLPath location, U32 size, U32 cache_version)
291{
292	if(!mEnabled)
293	{
294		llwarns << "Not initializing cache: Cache is currently disabled." << llendl;
295		return ;
296	}
297
298	if(mInitialized)
299	{
300		llwarns << "Cache already initialized." << llendl;
301		return ;
302	}
303	mInitialized = TRUE ;
304
305	setDirNames(location);
306	if (!mReadOnly)
307	{
308		LLFile::mkdir(mObjectCacheDirName);
309	}
310	mCacheSize = llclamp(size, MIN_ENTRIES_TO_PURGE, MAX_NUM_OBJECT_ENTRIES);
311	mMetaInfo.mVersion = cache_version;
312	readCacheHeader();	
313
314	if(mMetaInfo.mVersion != cache_version) 
315	{
316		mMetaInfo.mVersion = cache_version ;
317		if(mReadOnly) //disable cache
318		{
319			clearCacheInMemory();
320		}
321		else //delete the current cache if the format does not match.
322		{			
323			removeCache();
324		}
325	}	
326}
327	
328void LLVOCache::removeCache(ELLPath location) 
329{
330	if(mReadOnly)
331	{
332		llwarns << "Not removing cache at " << location << ": Cache is currently in read-only mode." << llendl;
333		return ;
334	}
335
336	llinfos << "about to remove the object cache due to settings." << llendl ;
337
338	std::string mask = "*";
339	std::string cache_dir = gDirUtilp->getExpandedFilename(location, object_cache_dirname);
340	llinfos << "Removing cache at " << cache_dir << llendl;
341	gDirUtilp->deleteFilesInDir(cache_dir, mask); //delete all files
342	LLFile::rmdir(cache_dir);
343
344	clearCacheInMemory();
345	mInitialized = FALSE ;
346}
347
348void LLVOCache::removeCache() 
349{
350	llassert_always(mInitialized) ;
351	if(mReadOnly)
352	{
353		llwarns << "Not clearing object cache: Cache is currently in read-only mode." << llendl;
354		return ;
355	}
356
357	llinfos << "about to remove the object cache due to some error." << llendl ;
358
359	std::string mask = "*";
360	llinfos << "Removing cache at " << mObjectCacheDirName << llendl;
361	gDirUtilp->deleteFilesInDir(mObjectCacheDirName, mask); 
362
363	clearCacheInMemory() ;
364	writeCacheHeader();
365}
366
367void LLVOCache::removeEntry(HeaderEntryInfo* entry) 
368{
369	llassert_always(mInitialized) ;
370	if(mReadOnly)
371	{
372		return ;
373	}
374	if(!entry)
375	{
376		return ;
377	}
378
379	header_entry_queue_t::iterator iter = mHeaderEntryQueue.find(entry) ;
380	if(iter != mHeaderEntryQueue.end())
381	{		
382		mHandleEntryMap.erase(entry->mHandle) ;		
383		mHeaderEntryQueue.erase(iter) ;
384		removeFromCache(entry) ;
385		delete entry ;
386
387		mNumEntries = mHandleEntryMap.size() ;
388	}
389}
390
391void LLVOCache::removeEntry(U64 handle) 
392{
393	handle_entry_map_t::iterator iter = mHandleEntryMap.find(handle) ;
394	if(iter == mHandleEntryMap.end()) //no cache
395	{
396		return ;
397	}
398	HeaderEntryInfo* entry = iter->second ;
399	removeEntry(entry) ;
400}
401
402void LLVOCache::clearCacheInMemory()
403{
404	if(!mHeaderEntryQueue.empty()) 
405	{
406		for(header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin(); iter != mHeaderEntryQueue.end(); ++iter)
407		{
408			delete *iter ;
409		}
410		mHeaderEntryQueue.clear();
411		mHandleEntryMap.clear();
412		mNumEntries = 0 ;
413	}
414
415}
416
417void LLVOCache::getObjectCacheFilename(U64 handle, std::string& filename) 
418{
419	U32 region_x, region_y;
420
421	grid_from_region_handle(handle, &region_x, &region_y);
422	filename = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, object_cache_dirname,
423			   llformat(OBJECT_CACHE_FILENAME, region_x, region_y));
424
425	return ;
426}
427
428void LLVOCache::removeFromCache(HeaderEntryInfo* entry)
429{
430	if(mReadOnly)
431	{
432		llwarns << "Not removing cache for handle " << entry->mHandle << ": Cache is currently in read-only mode." << llendl;
433		return ;
434	}
435
436	std::string filename;
437	getObjectCacheFilename(entry->mHandle, filename);
438	LLAPRFile::remove(filename, mLocalAPRFilePoolp);
439	entry->mTime = INVALID_TIME ;
440	updateEntry(entry) ; //update the head file.
441}
442
443void LLVOCache::readCacheHeader()
444{
445	if(!mEnabled)
446	{
447		llwarns << "Not reading cache header: Cache is currently disabled." << llendl;
448		return;
449	}
450
451	//clear stale info.
452	clearCacheInMemory();	
453
454	bool success = true ;
455	if (LLAPRFile::isExist(mHeaderFileName, mLocalAPRFilePoolp))
456	{
457		LLAPRFile apr_file(mHeaderFileName, APR_READ|APR_BINARY, mLocalAPRFilePoolp);		
458		
459		//read the meta element
460		success = check_read(&apr_file, &mMetaInfo, sizeof(HeaderMetaInfo)) ;
461		
462		if(success)
463		{
464			HeaderEntryInfo* entry = NULL ;
465			mNumEntries = 0 ;
466			U32 num_read = 0 ;
467			while(num_read++ < MAX_NUM_OBJECT_ENTRIES)
468			{
469				if(!entry)
470				{
471					entry = new HeaderEntryInfo() ;
472				}
473				success = check_read(&apr_file, entry, sizeof(HeaderEntryInfo));
474								
475				if(!success) //failed
476				{
477					llwarns << "Error reading cache header entry. (entry_index=" << mNumEntries << ")" << llendl;
478					delete entry ;
479					entry = NULL ;
480					break ;
481				}
482				else if(entry->mTime == INVALID_TIME)
483				{
484					continue ; //an empty entry
485				}
486
487				entry->mIndex = mNumEntries++ ;
488				mHeaderEntryQueue.insert(entry) ;
489				mHandleEntryMap[entry->mHandle] = entry ;
490				entry = NULL ;
491			}
492			if(entry)
493			{
494				delete entry ;
495			}
496		}
497
498		//---------
499		//debug code
500		//----------
501		//std::string name ;
502		//for(header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin() ; success && iter != mHeaderEntryQueue.end(); ++iter)
503		//{
504		//	getObjectCacheFilename((*iter)->mHandle, name) ;
505		//	llinfos << name << llendl ;
506		//}
507		//-----------
508	}
509	else
510	{
511		writeCacheHeader() ;
512	}
513
514	if(!success)
515	{
516		removeCache() ; //failed to read header, clear the cache
517	}
518	else if(mNumEntries >= mCacheSize)
519	{
520		purgeEntries(mCacheSize) ;
521	}
522
523	return ;
524}
525
526void LLVOCache::writeCacheHeader()
527{
528	if (!mEnabled)
529	{
530		llwarns << "Not writing cache header: Cache is currently disabled." << llendl;
531		return;
532	}
533
534	if(mReadOnly)
535	{
536		llwarns << "Not writing cache header: Cache is currently in read-only mode." << llendl;
537		return;
538	}
539
540	bool success = true ;
541	{
542		LLAPRFile apr_file(mHeaderFileName, APR_CREATE|APR_WRITE|APR_BINARY, mLocalAPRFilePoolp);
543
544		//write the meta element
545		success = check_write(&apr_file, &mMetaInfo, sizeof(HeaderMetaInfo)) ;
546
547
548		mNumEntries = 0 ;	
549		for(header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin() ; success && iter != mHeaderEntryQueue.end(); ++iter)
550		{
551			(*iter)->mIndex = mNumEntries++ ;
552			success = check_write(&apr_file, (void*)*iter, sizeof(HeaderEntryInfo));
553		}
554	
555		mNumEntries = mHeaderEntryQueue.size() ;
556		if(success && mNumEntries < MAX_NUM_OBJECT_ENTRIES)
557		{
558			HeaderEntryInfo* entry = new HeaderEntryInfo() ;
559			entry->mTime = INVALID_TIME ;
560			for(S32 i = mNumEntries ; success && i < MAX_NUM_OBJECT_ENTRIES ; i++)
561			{
562				//fill the cache with the default entry.
563				success = check_write(&apr_file, entry, sizeof(HeaderEntryInfo)) ;			
564
565			}
566			delete entry ;
567		}
568	}
569
570	if(!success)
571	{
572		clearCacheInMemory() ;
573		mReadOnly = TRUE ; //disable the cache.
574	}
575	return ;
576}
577
578BOOL LLVOCache::updateEntry(const HeaderEntryInfo* entry)
579{
580	LLAPRFile apr_file(mHeaderFileName, APR_WRITE|APR_BINARY, mLocalAPRFilePoolp);
581	apr_file.seek(APR_SET, entry->mIndex * sizeof(HeaderEntryInfo) + sizeof(HeaderMetaInfo)) ;
582
583	return check_write(&apr_file, (void*)entry, sizeof(HeaderEntryInfo)) ;
584}
585
586void LLVOCache::readFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::vocache_entry_map_t& cache_entry_map) 
587{
588	if(!mEnabled)
589	{
590		llwarns << "Not reading cache for handle " << handle << "): Cache is currently disabled." << llendl;
591		return ;
592	}
593	llassert_always(mInitialized);
594
595	handle_entry_map_t::iterator iter = mHandleEntryMap.find(handle) ;
596	if(iter == mHandleEntryMap.end()) //no cache
597	{
598		llwarns << "No handle map entry for " << handle << llendl;
599		return ;
600	}
601
602	bool success = true ;
603	{
604		std::string filename;
605		getObjectCacheFilename(handle, filename);
606		LLAPRFile apr_file(filename, APR_READ|APR_BINARY, mLocalAPRFilePoolp);
607	
608		LLUUID cache_id ;
609		success = check_read(&apr_file, cache_id.mData, UUID_BYTES) ;
610	
611		if(success)
612		{		
613			if(cache_id != id)
614			{
615				llinfos << "Cache ID doesn't match for this region, discarding"<< llendl;
616				success = false ;
617			}
618
619			if(success)
620			{
621				S32 num_entries;
622				success = check_read(&apr_file, &num_entries, sizeof(S32)) ;
623	
624				if(success)
625				{
626					for (S32 i = 0; i < num_entries; i++)
627					{
628						LLVOCacheEntry* entry = new LLVOCacheEntry(&apr_file);
629						if (!entry->getLocalID())
630						{
631							llwarns << "Aborting cache file load for " << filename << ", cache file corruption!" << llendl;
632							delete entry ;
633							success = false ;
634							break ;
635						}
636						cache_entry_map[entry->getLocalID()] = entry;
637					}
638				}
639			}
640		}		
641	}
642	
643	if(!success)
644	{
645		if(cache_entry_map.empty())
646		{
647			removeEntry(iter->second) ;
648		}
649	}
650
651	return ;
652}
653	
654void LLVOCache::purgeEntries(U32 size)
655{
656	while(mHeaderEntryQueue.size() > size)
657	{
658		header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin() ;
659		HeaderEntryInfo* entry = *iter ;			
660		mHandleEntryMap.erase(entry->mHandle);
661		mHeaderEntryQueue.erase(iter) ;
662		removeFromCache(entry) ;
663		delete entry;
664	}
665	mNumEntries = mHandleEntryMap.size() ;
666}
667
668void LLVOCache::writeToCache(U64 handle, const LLUUID& id, const LLVOCacheEntry::vocache_entry_map_t& cache_entry_map, BOOL dirty_cache) 
669{
670	if(!mEnabled)
671	{
672		llwarns << "Not writing cache for handle " << handle << "): Cache is currently disabled." << llendl;
673		return ;
674	}
675	llassert_always(mInitialized);
676
677	if(mReadOnly)
678	{
679		llwarns << "Not writing cache for handle " << handle << "): Cache is currently in read-only mode." << llendl;
680		return ;
681	}	
682
683	HeaderEntryInfo* entry;
684	handle_entry_map_t::iterator iter = mHandleEntryMap.find(handle) ;
685	if(iter == mHandleEntryMap.end()) //new entry
686	{				
687		if(mNumEntries >= mCacheSize - 1)
688		{
689			purgeEntries(mCacheSize - 1) ;
690		}
691
692		entry = new HeaderEntryInfo();
693		entry->mHandle = handle ;
694		entry->mTime = time(NULL) ;
695		entry->mIndex = mNumEntries++;
696		mHeaderEntryQueue.insert(entry) ;
697		mHandleEntryMap[handle] = entry ;
698	}
699	else
700	{
701		// Update access time.
702		entry = iter->second ;		
703
704		//resort
705		mHeaderEntryQueue.erase(entry) ;
706		
707		entry->mTime = time(NULL) ;
708		mHeaderEntryQueue.insert(entry) ;
709	}
710
711	//update cache header
712	if(!updateEntry(entry))
713	{
714		llwarns << "Failed to update cache header index " << entry->mIndex << ". handle = " << handle << llendl;
715		return ; //update failed.
716	}
717
718	if(!dirty_cache)
719	{
720		llwarns << "Skipping write to cache for handle " << handle << ": cache not dirty" << llendl;
721		return ; //nothing changed, no need to update.
722	}
723
724	//write to cache file
725	bool success = true ;
726	{
727		std::string filename;
728		getObjectCacheFilename(handle, filename);
729		LLAPRFile apr_file(filename, APR_CREATE|APR_WRITE|APR_BINARY, mLocalAPRFilePoolp);
730	
731		success = check_write(&apr_file, (void*)id.mData, UUID_BYTES) ;
732
733	
734		if(success)
735		{
736			S32 num_entries = cache_entry_map.size() ;
737			success = check_write(&apr_file, &num_entries, sizeof(S32));
738	
739			for (LLVOCacheEntry::vocache_entry_map_t::const_iterator iter = cache_entry_map.begin(); success && iter != cache_entry_map.end(); ++iter)
740			{
741				success = iter->second->writeToFile(&apr_file) ;
742			}
743		}
744	}
745
746	if(!success)
747	{
748		removeEntry(entry) ;
749
750	}
751
752	return ;
753}