void LLTextureCache::purgeCache(ELLPath location) { if (!mReadOnly) { setDirNames(location); ll_apr_file_remove(mHeaderEntriesFileName, NULL); ll_apr_file_remove(mHeaderDataFileName, NULL); } purgeAllTextures(true); }
void LLTextureCache::purgeCache(ELLPath location) { LLMutexLock lock(&mHeaderMutex); if (!mReadOnly) { setDirNames(location); llassert_always(mHeaderAPRFile == NULL); LLAPRFile::remove(mHeaderEntriesFileName); LLAPRFile::remove(mHeaderDataFileName); } purgeAllTextures(true); }
U32 LLTextureCache::openAndReadEntries(std::vector<Entry>& entries) { U32 num_entries = mHeaderEntriesInfo.mEntries; mHeaderIDMap.clear(); mTexturesSizeMap.clear(); mFreeList.clear(); mTexturesSizeTotal = 0; LLAPRFile* aprfile = openHeaderEntriesFile(false, (S32)sizeof(EntriesInfo)); for (U32 idx=0; idx<num_entries; idx++) { Entry entry; S32 bytes_read = aprfile->read((void*)(&entry), (S32)sizeof(Entry)); if (bytes_read < sizeof(Entry)) { llwarns << "Corrupted header entries, failed at " << idx << " / " << num_entries << llendl; closeHeaderEntriesFile(); purgeAllTextures(false); return 0; } entries.push_back(entry); // llinfos << "ENTRY: " << entry.mTime << " TEX: " << entry.mID << " IDX: " << idx << " Size: " << entry.mImageSize << llendl; if (entry.mImageSize < 0) { mFreeList.insert(idx); } else { mHeaderIDMap[entry.mID] = idx; if (entry.mBodySize > 0) { mTexturesSizeMap[entry.mID] = entry.mBodySize; mTexturesSizeTotal += entry.mBodySize; } llassert_always(entry.mImageSize == 0 || entry.mImageSize > entry.mBodySize); } } closeHeaderEntriesFile(); return num_entries; }
void LLTextureCache::purgeTextures(bool validate) { if (mReadOnly) { return; } // *FIX:Mani - watchdog off. LLAppViewer::instance()->pauseMainloopTimeout(); LLMutexLock lock(&mHeaderMutex); S32 filesize = ll_apr_file_size(mTexturesDirEntriesFileName, NULL); S32 num_entries = filesize / sizeof(Entry); if (num_entries * (S32)sizeof(Entry) != filesize) { LL_WARNS("TextureCache") << "Bad cache file: " << mTexturesDirEntriesFileName << " Purging." << LL_ENDL; purgeAllTextures(false); return; } if (num_entries == 0) { return; // nothing to do } Entry* entries = new Entry[num_entries]; S32 bytes_read = ll_apr_file_read_ex(mTexturesDirEntriesFileName, NULL, (U8*)entries, 0, num_entries*sizeof(Entry)); if (bytes_read != filesize) { LL_WARNS("TextureCache") << "Bad cache file (2): " << mTexturesDirEntriesFileName << " Purging." << LL_ENDL; purgeAllTextures(false); return; } LL_DEBUGS("TextureCache") << "TEXTURE CACHE: Reading " << num_entries << " Entries from " << mTexturesDirEntriesFileName << LL_ENDL; std::map<LLUUID, S32> entry_idx_map; S64 total_size = 0; for (S32 idx=0; idx<num_entries; idx++) { const LLUUID& id = entries[idx].mID; LL_DEBUGS("TextureCache") << "Entry: " << id << " Size: " << entries[idx].mSize << " Time: " << entries[idx].mTime << LL_ENDL; std::map<LLUUID, S32>::iterator iter = entry_idx_map.find(id); if (iter != entry_idx_map.end()) { // Newer entry replacing older entry S32 pidx = iter->second; total_size -= entries[pidx].mSize; entries[pidx].mSize = 0; // flag: skip older entry } entry_idx_map[id] = idx; total_size += entries[idx].mSize; } U32 validate_idx = 0; if (validate) { validate_idx = gSavedSettings.getU32("CacheValidateCounter"); U32 next_idx = (++validate_idx) % 256; gSavedSettings.setU32("CacheValidateCounter", next_idx); LL_DEBUGS("TextureCache") << "TEXTURE CACHE: Validating: " << validate_idx << LL_ENDL; } S64 min_cache_size = sCacheMaxTexturesSize / 100 * 95; S32 purge_count = 0; S32 next_idx = 0; for (S32 idx=0; idx<num_entries; idx++) { if (entries[idx].mSize == 0) { continue; } bool purge_entry = false; std::string filename = getTextureFileName(entries[idx].mID); if (total_size >= min_cache_size) { purge_entry = true; } else if (validate) { // make sure file exists and is the correct size S32 uuididx = entries[idx].mID.mData[0]; if (uuididx == validate_idx) { LL_DEBUGS("TextureCache") << "Validating: " << filename << "Size: " << entries[idx].mSize << LL_ENDL; S32 bodysize = ll_apr_file_size(filename, NULL); if (bodysize != entries[idx].mSize) { LL_WARNS("TextureCache") << "TEXTURE CACHE BODY HAS BAD SIZE: " << bodysize << " != " << entries[idx].mSize << filename << LL_ENDL; purge_entry = true; } } } if (purge_entry) { purge_count++; LL_DEBUGS("TextureCache") << "PURGING: " << filename << LL_ENDL; mFilesToDelete.push_back(filename); total_size -= entries[idx].mSize; entries[idx].mSize = 0; } else { if (next_idx != idx) { entries[next_idx] = entries[idx]; } ++next_idx; } } num_entries = next_idx; mTimeLastFileDelete.reset(); LL_DEBUGS("TextureCache") << "TEXTURE CACHE: Writing Entries: " << num_entries << " (" << num_entries*sizeof(Entry)/1024 << "KB)" << LL_ENDL; ll_apr_file_remove(mTexturesDirEntriesFileName, NULL); ll_apr_file_write_ex(mTexturesDirEntriesFileName, NULL, (U8*)&entries[0], 0, num_entries*sizeof(Entry)); mTexturesSizeTotal = 0; mTexturesSizeMap.clear(); for (S32 idx=0; idx<num_entries; idx++) { mTexturesSizeMap[entries[idx].mID] = entries[idx].mSize; mTexturesSizeTotal += entries[idx].mSize; } llassert(mTexturesSizeTotal == total_size); delete[] entries; // *FIX:Mani - watchdog back on. LLAppViewer::instance()->resumeMainloopTimeout(); LL_INFOS("TextureCache") << "TEXTURE CACHE:" << " PURGED: " << purge_count << " ENTRIES: " << num_entries << " CACHE SIZE: " << total_size/1024/1024 << " MB" << llendl; }
// Called from either the main thread or the worker thread void LLTextureCache::readHeaderCache() { mHeaderMutex.lock(); mLRU.clear(); // always clear the LRU readEntriesHeader(); if (mHeaderEntriesInfo.mVersion != sHeaderCacheVersion) { if (!mReadOnly) { purgeAllTextures(false); } } else { std::vector<Entry> entries; U32 num_entries = openAndReadEntries(entries); if (num_entries) { U32 empty_entries = 0; typedef std::pair<U32, LLUUID> lru_data_t; std::set<lru_data_t> lru; std::set<LLUUID> purge_list; for (U32 i=0; i<num_entries; i++) { Entry& entry = entries[i]; const LLUUID& id = entry.mID; if (entry.mImageSize <= 0) { // This will be in the Free List, don't put it in the LRU ++empty_entries; } else { lru.insert(std::make_pair(entry.mTime, id)); if (entry.mBodySize > 0) { if (entry.mBodySize > entry.mImageSize) { // Shouldn't happen, failsafe only llwarns << "Bad entry: " << i << ": " << entry.mID << ": BodySize: " << entry.mBodySize << llendl; purge_list.insert(id); } } } } if (num_entries - empty_entries > sCacheMaxEntries) { // Special case: cache size was reduced, need to remove entries // Note: After we prune entries, we will call this again and create the LRU U32 entries_to_purge = (num_entries - empty_entries) - sCacheMaxEntries; llinfos << "Texture Cache Entries: " << num_entries << " Max: " << sCacheMaxEntries << " Empty: " << empty_entries << " Purging: " << entries_to_purge << llendl; // We can exit the following loop with the given condition, since if we'd reach the end of the lru set we'd have: // purge_list.size() = lru.size() = num_entries - empty_entries = entries_to_purge + sCacheMaxEntries >= entries_to_purge for (std::set<lru_data_t>::iterator iter = lru.begin(); purge_list.size() < entries_to_purge; ++iter) { purge_list.insert(iter->second); } llassert_always(purge_list.size() >= entries_to_purge); } else { S32 lru_entries = (S32)((F32)sCacheMaxEntries * TEXTURE_CACHE_LRU_SIZE); for (std::set<lru_data_t>::iterator iter = lru.begin(); iter != lru.end(); ++iter) { mLRU.insert(iter->second); // llinfos << "LRU: " << iter->first << " : " << iter->second << llendl; if (--lru_entries <= 0) break; } } if (purge_list.size() > 0) { for (std::set<LLUUID>::iterator iter = purge_list.begin(); iter != purge_list.end(); ++iter) { removeFromCacheLocked(*iter); } // If we removed any entries, we need to rebuild the entries list, // write the header, and call this again std::vector<Entry> new_entries; for (U32 i=0; i<num_entries; i++) { const Entry& entry = entries[i]; if (entry.mImageSize > 0) { new_entries.push_back(entry); } } llassert_always(new_entries.size() <= sCacheMaxEntries); mHeaderEntriesInfo.mEntries = new_entries.size(); writeEntriesHeader(); writeEntriesAndClose(new_entries); mHeaderMutex.unlock(); // unlock the mutex before calling again readHeaderCache(); // repeat with new entries file return; } else { //entries are not changed, nothing here. } } } mHeaderMutex.unlock(); }