/indra/newview/llvocache.cpp
C++ | 753 lines | 591 code | 103 blank | 59 comment | 94 complexity | edba3597c6e24463bba78b382dd2f77a MD5 | raw file
Possible License(s): LGPL-2.1
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, ®ion_x, ®ion_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}