diff options
| author | Erik Kundiman <erik@megapahit.org> | 2026-09-01 18:35:23 +0800 |
|---|---|---|
| committer | Erik Kundiman <erik@megapahit.org> | 2026-09-04 21:58:53 +0800 |
| commit | 00bb3bb6f07660bd914d29a1389342bb3060d254 (patch) | |
| tree | 431f574922494d2201718f13085f17bc6bd7fb42 /indra/newview/llinventorymodel.cpp | |
| parent | a8636720129952c10cf49ab80da21bf8b340b96c (diff) | |
| parent | 4ef9f8f14b35ed388c294d53767a0dca0e890924 (diff) | |
Merge remote-tracking branch 'secondlife/release/26.4' into 26.4
Diffstat (limited to 'indra/newview/llinventorymodel.cpp')
| -rw-r--r-- | indra/newview/llinventorymodel.cpp | 241 |
1 files changed, 172 insertions, 69 deletions
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 2657f11d0e..b818a66b30 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -28,6 +28,7 @@ #include <typeinfo> #include <random> +#include <thread> #include "llinventorymodel.h" @@ -82,6 +83,7 @@ const S32 LLInventoryModel::sCurrentInvCacheVersion = 5; bool LLInventoryModel::sFirstTimeInViewer2 = true; S32 LLInventoryModel::sPendingSystemFolders = 0; +static std::vector<std::thread> sPendingCacheThreads; ///---------------------------------------------------------------------------- /// Local function declarations, constants, enums, and typedefs @@ -100,45 +102,6 @@ struct InventoryIDPtrLess } }; -class LLCanCache : public LLInventoryCollectFunctor -{ -public: - LLCanCache(LLInventoryModel* model) : mModel(model) {} - virtual ~LLCanCache() {} - virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item); -protected: - LLInventoryModel* mModel; - std::set<LLUUID> mCachedCatIDs; -}; - -bool LLCanCache::operator()(LLInventoryCategory* cat, LLInventoryItem* item) -{ - bool rv = false; - if(item) - { - if(mCachedCatIDs.find(item->getParentUUID()) != mCachedCatIDs.end()) - { - rv = true; - } - } - else if(cat) - { - // HACK: downcast - LLViewerInventoryCategory* c = (LLViewerInventoryCategory*)cat; - if(c->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN) - { - S32 descendents_server = c->getDescendentCount(); - S32 descendents_actual = c->getViewerDescendentCount(); - if(descendents_server == descendents_actual) - { - mCachedCatIDs.insert(c->getUUID()); - rv = true; - } - } - } - return rv; -} - struct InventoryCallbackInfo { InventoryCallbackInfo(U32 callback, const LLUUID& inv_id) : @@ -467,30 +430,33 @@ LLInventoryModel::~LLInventoryModel() void LLInventoryModel::cleanupInventory() { + LL_PROFILE_ZONE_SCOPED; empty(); - // Deleting one observer might erase others from the list, so always pop off the front - while (!mObservers.empty()) + // Deleting one observer might trigger removeObserver, so use a local copy + if (!mObservers.empty()) { - observer_list_t::iterator iter = mObservers.begin(); - LLInventoryObserver* observer = *iter; - mObservers.erase(iter); - delete observer; + observer_list_t observers_to_delete; + observers_to_delete.swap(mObservers); + + for (LLInventoryObserver* observer : observers_to_delete) + { + delete observer; + } } if (mBulkFecthCallbackSlot.connected()) { mBulkFecthCallbackSlot.disconnect(); } - mObservers.clear(); // Run down HTTP transport mHttpHeaders.reset(); mHttpOptions.reset(); delete mHttpRequestFG; - mHttpRequestFG = NULL; + mHttpRequestFG = nullptr; delete mHttpRequestBG; - mHttpRequestBG = NULL; + mHttpRequestBG = nullptr; } // This is a convenience function to check if one object has a parent @@ -2369,22 +2335,101 @@ void LLInventoryModel::cache( const LLUUID& parent_folder_id, const LLUUID& agent_id) { + LL_PROFILE_ZONE_SCOPED; LL_DEBUGS(LOG_INV) << "Caching " << parent_folder_id << " for " << agent_id << LL_ENDL; + LLViewerInventoryCategory* root_cat = getCategory(parent_folder_id); - if(!root_cat) return; + if (!root_cat) + { + LL_WARNS(LOG_INV) << "Root category not found for " << parent_folder_id << LL_ENDL; + return; + } + cat_array_t categories; categories.push_back(root_cat); item_array_t items; - LLCanCache can_cache(this); - can_cache(root_cat, NULL); - collectDescendentsIf( - parent_folder_id, - categories, - items, - INCLUDE_TRASH, - can_cache); + // Lambda to check if a category should be cached + // Only cache if it has known version and matching descendent counts + auto should_cache_category = [](LLViewerInventoryCategory* cat) -> bool { + if (!cat || cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN) + { + return false; + } + S32 descendents_server = cat->getDescendentCount(); + S32 descendents_actual = cat->getViewerDescendentCount(); + return (descendents_server == descendents_actual); + }; + + // Track which folders we've verified as cacheable descendants + std::unordered_set<LLUUID> processed_folders; + processed_folders.insert(parent_folder_id); + + // First pass: identify all cacheable descendant folders + // Use pair of (folder_id, should_save_children) + std::deque<std::pair<LLUUID, bool>> folders_to_check; + folders_to_check.push_back(std::make_pair(parent_folder_id, should_cache_category(root_cat))); + + while (!folders_to_check.empty()) + { + auto [current_id, save_children] = folders_to_check.front(); + folders_to_check.pop_front(); + + if (save_children) // else incorrect count or version + { + auto item_it = mParentChildItemTree.find(current_id); + if (item_it != mParentChildItemTree.end() && item_it->second) + { + for (LLViewerInventoryItem* item : *(item_it->second)) + { + if (item) + { + items.push_back(item); + } + } + } + } + + // Get child categories directly from the parent-child tree + auto cat_it = mParentChildCategoryTree.find(current_id); + if (cat_it != mParentChildCategoryTree.end() && cat_it->second) + { + for (LLViewerInventoryCategory* child_cat : *(cat_it->second)) + { + if (!child_cat) + { + continue; + } + + // Verify ownership matches (library vs agent inventory) + if (child_cat->getOwnerID() != root_cat->getOwnerID()) + { + LL_WARNS(LOG_INV) << "Owner mismatch in category tree: expected " + << root_cat->getOwnerID() << " got " + << child_cat->getOwnerID() << " for category " + << child_cat->getName() << LL_ENDL; + continue; + } + + const LLUUID& child_id = child_cat->getUUID(); + + // Only process each folder once + if (processed_folders.insert(child_id).second) + { + if (should_cache_category(child_cat)) + { + categories.push_back(child_cat); + folders_to_check.push_back(std::make_pair(child_id, true)); + } + else + { + folders_to_check.push_back(std::make_pair(child_id, false)); + } + } + } + } + } if (categories.empty() && items.empty()) { @@ -2403,17 +2448,70 @@ void LLInventoryModel::cache( } std::string gzip_filename = getInvCacheAddres(agent_id); gzip_filename.append(".gz"); - if(gzip_file(temp_file, gzip_filename)) + + if (sPendingCacheThreads.empty()) { - LL_DEBUGS(LOG_INV) << "Successfully compressed " << temp_file << " to " << gzip_filename << LL_ENDL; - LLFile::remove(temp_file); + LL_INFOS(LOG_INV) << "Inventory cache compression started" << LL_ENDL; } - else + + // Launch background packing thread + // Main thread is the only one modifying sPendingCacheThreads + auto compress_cache = [temp_file, gzip_filename]() { - LL_WARNS(LOG_INV) << "Unable to compress " << temp_file << " into " << gzip_filename << LL_ENDL; + LL_PROFILE_ZONE_NAMED("inv cache compression"); + LLTimer gzip_timer; + + if (gzip_file(temp_file, gzip_filename)) + { + F32 gzip_time = gzip_timer.getElapsedTimeF32(); + LL_DEBUGS(LOG_INV) << "Successfully compressed " << temp_file + << " to " << gzip_filename + << " in " << gzip_time << "s" << LL_ENDL; + LLFile::remove(temp_file); + } + else + { + LL_WARNS(LOG_INV) << "Unable to compress " << temp_file + << " into " << gzip_filename << LL_ENDL; + } + }; + + try + { + sPendingCacheThreads.emplace_back(compress_cache); + } + catch (...) + { + LL_WARNS(LOG_INV) << "Failed to start inventory cache compression thread; running compression synchronously" << LL_ENDL; + compress_cache(); } } +void LLInventoryModel::waitForPendingCacheWrites() +{ + if (sPendingCacheThreads.empty()) + { + return; + } + LL_PROFILE_ZONE_SCOPED; + + // By this point all threads should have already been added, + // viewer is shutting down, main thread is the only one to + // modify sPendingCacheThreads + LL_DEBUGS(LOG_INV) << "Waiting for " << sPendingCacheThreads.size() + << " inventory cache compression thread(s) to complete..." << LL_ENDL; + + for (auto& thread : sPendingCacheThreads) + { + if (thread.joinable()) + { + thread.join(); + } + } + sPendingCacheThreads.clear(); + + LL_INFOS(LOG_INV) << "Inventory cache compression completed" << LL_ENDL; +} void LLInventoryModel::addCategory(LLViewerInventoryCategory* category) { @@ -2721,10 +2819,11 @@ bool LLInventoryModel::loadSkeleton( for(LLSD::array_const_iterator it = options.beginArray(), end = options.endArray(); it != end; ++it) { - LLSD name = (*it)["name"]; - LLSD folder_id = (*it)["folder_id"]; - LLSD parent_id = (*it)["parent_id"]; - LLSD version = (*it)["version"]; + const LLSD &folder = *it; + const LLSD &name = folder["name"]; + const LLSD &folder_id = folder["folder_id"]; + const LLSD &parent_id = folder["parent_id"]; + const LLSD &version = folder["version"]; if(name.isDefined() && folder_id.isDefined() && parent_id.isDefined() @@ -2738,7 +2837,7 @@ bool LLInventoryModel::loadSkeleton( cat->setParent(parent_id.asUUID()); LLFolderType::EType preferred_type = LLFolderType::FT_NONE; - LLSD type_default = (*it)["type_default"]; + const LLSD &type_default = folder["type_default"]; if(type_default.isDefined()) { preferred_type = (LLFolderType::EType)type_default.asInteger(); @@ -2768,7 +2867,7 @@ bool LLInventoryModel::loadSkeleton( const S32 NO_VERSION = LLViewerInventoryCategory::VERSION_UNKNOWN; std::string gzip_filename(inventory_filename); gzip_filename.append(".gz"); - LLFILE* fp = LLFile::fopen(gzip_filename, "rb"); + LLFILE* fp = LLFile::fopen(gzip_filename, LLFILE_MODE("rb")); bool remove_inventory_file = false; if (LLAppViewer::instance()->isSecondInstance()) { @@ -3466,6 +3565,8 @@ bool LLInventoryModel::loadFromFile(const std::string& filename, const LLSD& llsd_cats = inventory["categories"]; if (llsd_cats.isArray()) { + size_t cats_count = llsd_cats.size(); + categories.reserve(cats_count); LLSD::array_const_iterator iter = llsd_cats.beginArray(); LLSD::array_const_iterator end = llsd_cats.endArray(); for (; iter != end; ++iter) @@ -3484,6 +3585,8 @@ bool LLInventoryModel::loadFromFile(const std::string& filename, const LLSD& llsd_items = inventory["items"]; if (llsd_items.isArray()) { + size_t items_count = llsd_items.size(); + items.reserve(items_count); LLSD::array_const_iterator iter = llsd_items.beginArray(); LLSD::array_const_iterator end = llsd_items.endArray(); for (; iter != end; ++iter) @@ -4963,7 +5066,7 @@ bool decompress_file(const char* src_filename, const char* dst_filename) // open the files src = gzopen(src_filename, "rb"); if(!src) goto err_decompress; - dst = LLFile::fopen(dst_filename, "wb"); + dst = LLFile::fopen(dst_filename, LLFILE_MODE("wb")); if(!dst) goto err_decompress; // decompress. |
