diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 577b646ff..d360f0392 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -294,6 +294,7 @@ set(viewer_SOURCE_FILES llinventoryfunctions.cpp llinventoryicon.cpp llinventorymodel.cpp + llinventorymodelbackgroundfetch.cpp llinventoryobserver.cpp llinventoryview.cpp lljoystickbutton.cpp @@ -768,6 +769,7 @@ set(viewer_HEADER_FILES llinventoryfunctions.h llinventoryicon.h llinventorymodel.h + llinventorymodelbackgroundfetch.h llinventoryobserver.h llinventoryview.h lljoystickbutton.h diff --git a/indra/newview/floaterao.cpp b/indra/newview/floaterao.cpp index 3d3ee5847..d15b20e92 100644 --- a/indra/newview/floaterao.cpp +++ b/indra/newview/floaterao.cpp @@ -26,6 +26,7 @@ #include "llinventory.h" #include "llinventoryfunctions.h" +#include "llinventorymodelbackgroundfetch.h" #include "llinventoryview.h" #include "roles_constants.h" #include "llviewerregion.h" @@ -126,7 +127,7 @@ BOOL AOInvTimer::tick() if (!(gSavedSettings.getBOOL("AOEnabled"))) return TRUE; if(LLStartUp::getStartupState() >= STATE_INVENTORY_SEND) { - if(gInventory.isEverythingFetched()) + if(LLInventoryModelBackgroundFetch::instance().isEverythingFetched()) { // cmdline_printchat("Inventory fetched, loading AO."); LLFloaterAO::init(); @@ -667,7 +668,7 @@ void LLFloaterAO::init() if(LLStartUp::getStartupState() >= STATE_INVENTORY_SEND) { - if(gInventory.isEverythingFetched()) + if(LLInventoryModelBackgroundFetch::instance().isEverythingFetched()) { LLUUID configncitem = (LLUUID)gSavedPerAccountSettings.getString("AOConfigNotecardID"); if (configncitem.notNull()) @@ -964,7 +965,7 @@ BOOL LLFloaterAO::stopMotion(const LLUUID& id, BOOL stop_immediate, BOOL stand) void LLFloaterAO::onClickReloadCard(void* user_data) { - if(gInventory.isEverythingFetched()) + if(LLInventoryModelBackgroundFetch::instance().isEverythingFetched()) { LLFloaterAO::init(); } @@ -972,7 +973,7 @@ void LLFloaterAO::onClickReloadCard(void* user_data) void LLFloaterAO::onClickOpenCard(void* user_data) { - if(gInventory.isEverythingFetched()) + if(LLInventoryModelBackgroundFetch::instance().isEverythingFetched()) { LLUUID configncitem = (LLUUID)gSavedPerAccountSettings.getString("AOConfigNotecardID"); if (configncitem.notNull()) @@ -1419,7 +1420,7 @@ private: const LLUUID& LLFloaterAO::getAssetIDByName(const std::string& name) { - if (name.empty() || !(gInventory.isEverythingFetched())) return LLUUID::null; + if (name.empty() || !(LLInventoryModelBackgroundFetch::instance().isEverythingFetched())) return LLUUID::null; LLViewerInventoryCategory::cat_array_t cats; LLViewerInventoryItem::item_array_t items; diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp index 6e1db8a0a..62f55dd65 100644 --- a/indra/newview/llfloaterworldmap.cpp +++ b/indra/newview/llfloaterworldmap.cpp @@ -52,6 +52,10 @@ #include "lldraghandle.h" #include "llfirstuse.h" #include "llfocusmgr.h" +#include "llinventoryfunctions.h" +#include "llinventorymodel.h" +#include "llinventorymodelbackgroundfetch.h" +#include "llinventoryobserver.h" #include "lllandmarklist.h" #include "llnotificationsutil.h" #include "lllineeditor.h" @@ -70,7 +74,7 @@ #include "llappviewer.h" #include "llmapimagetype.h" #include "llweb.h" -#include "llinventoryfunctions.h" + #include "llglheaders.h" @@ -314,7 +318,7 @@ void LLFloaterWorldMap::show(void*, BOOL center_on_target) // Start speculative download of landmarks LLUUID landmark_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK); - gInventory.startBackgroundFetch(landmark_folder_id); + LLInventoryModelBackgroundFetch::instance().startBackgroundFetch(landmark_folder_id); gFloaterWorldMap->childSetFocus("location", TRUE); gFocusMgr.triggerFocusFlash(); diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index 16b26bb26..ad0dead06 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -49,6 +49,7 @@ #include "llinventoryclipboard.h" // *TODO: remove this once hack below gone. #include "llinventoryview.h"// hacked in for the bonus context menu items. #include "llinventoryfunctions.h" +#include "llinventorymodelbackgroundfetch.h" #include "llkeyboard.h" #include "lllineeditor.h" #include "llmenugl.h" @@ -1308,7 +1309,7 @@ void LLFolderViewFolder::filter( LLInventoryFilter& filter) // when applying a filter, matching folders get their contents downloaded first if (filter.isNotDefault() && getFiltered(filter.getMinRequiredGeneration()) && (mListener && !gInventory.isCategoryComplete(mListener->getUUID()))) { - gInventory.startBackgroundFetch(mListener->getUUID()); + LLInventoryModelBackgroundFetch::instance().startBackgroundFetch(mListener->getUUID()); } // now query children @@ -3319,7 +3320,7 @@ void LLFolderView::draw() } else { - if (gInventory.backgroundFetchActive() || mCompletedFilterGeneration < mFilter.getMinRequiredGeneration()) + if (LLInventoryModelBackgroundFetch::instance().backgroundFetchActive() || mCompletedFilterGeneration < mFilter.getMinRequiredGeneration()) { mStatusText = std::string("Searching..."); // *TODO:translate sFont->renderUTF8(mStatusText, 0, 2, 1, sSearchStatusColor, LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE ); @@ -5123,7 +5124,7 @@ std::string LLInventoryFilter::getFilterText() filtered_by_all_types = FALSE; } - if (!gInventory.backgroundFetchActive() && filtered_by_type && !filtered_by_all_types) + if (!LLInventoryModelBackgroundFetch::instance().backgroundFetchActive() && filtered_by_type && !filtered_by_all_types) { mFilterText += " - "; if (num_filter_types < 5) diff --git a/indra/newview/llinventoryactions.cpp b/indra/newview/llinventoryactions.cpp index 14cad7cc6..1ff9d6709 100644 --- a/indra/newview/llinventoryactions.cpp +++ b/indra/newview/llinventoryactions.cpp @@ -63,6 +63,7 @@ #include "lliconctrl.h" #include "llinventoryfunctions.h" #include "llinventoryclipboard.h" +#include "llinventorymodelbackgroundfetch.h" #include "lllineeditor.h" #include "llmenugl.h" #include "llpreviewanim.h" @@ -525,7 +526,7 @@ class LLRefreshInvModel : public inventory_listener_t LLInventoryModel* model = mPtr->getPanel()->getModel(); if(!model) return false; model->empty(); - model->startBackgroundFetch(); + LLInventoryModelBackgroundFetch::instance().startBackgroundFetch(); return true; } }; diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index baa318313..9d9110eb2 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -39,6 +39,7 @@ #include "llinventorydefines.h" #include "llinventoryfunctions.h" #include "llinventoryicon.h" +#include "llinventorymodelbackgroundfetch.h" #include "message.h" @@ -802,7 +803,7 @@ BOOL LLInvFVBridge::startDrag(EDragAndDropType* type, LLUUID* id) const if (*type == DAD_CATEGORY) { - gInventory.startBackgroundFetch(obj->getUUID()); + LLInventoryModelBackgroundFetch::instance().startBackgroundFetch(obj->getUUID()); } rv = TRUE; diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 5745b5704..32e0fc6c2 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -78,22 +78,6 @@ #include "process.h" #endif -const F32 MAX_TIME_FOR_SINGLE_FETCH = 10.f; -const S32 MAX_FETCH_RETRIES = 10; -BOOL LLInventoryModel::sBackgroundFetchActive = FALSE; -BOOL LLInventoryModel::sAllFoldersFetched = FALSE; -BOOL LLInventoryModel::sFullFetchStarted = FALSE; -S32 LLInventoryModel::sNumFetchRetries = 0; -F32 LLInventoryModel::sMinTimeBetweenFetches = 0.3f; -F32 LLInventoryModel::sMaxTimeBetweenFetches = 10.f; -BOOL LLInventoryModel::sTimelyFetchPending = FALSE; -LLFrameTimer LLInventoryModel::sFetchTimer; -S16 LLInventoryModel::sBulkFetchCount = 0; - -// RN: for some reason, using std::queue in the header file confuses the compiler which things it's an xmlrpc_queue -static std::deque sFetchQueue; - - // Increment this if the inventory contents change in a non-backwards-compatible way. // For viewers with link items support, former caches are incorrect. const S32 LLInventoryModel::sCurrentInvCacheVersion = 2; @@ -1384,523 +1368,6 @@ bool LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id) const return cat->fetchDescendents(); } -//Initialize statics. -bool LLInventoryModel::isBulkFetchProcessingComplete() -{ - return ( (sFetchQueue.empty() - && sBulkFetchCount<=0) ? TRUE : FALSE ) ; -} - -class fetchDescendentsResponder: public LLHTTPClient::Responder -{ - public: - fetchDescendentsResponder(const LLSD& request_sd) : mRequestSD(request_sd) {}; - //fetchDescendentsResponder() {}; - void result(const LLSD& content); - void error(U32 status, const std::string& reason); - public: - typedef std::vector folder_ref_t; - protected: - LLSD mRequestSD; -}; - -//If we get back a normal response, handle it here -// Note: this is the handler for WebFetchInventoryDescendents and agent/inventory caps -void fetchDescendentsResponder::result(const LLSD& content) -{ - if (content.has("folders")) - { - - for(LLSD::array_const_iterator folder_it = content["folders"].beginArray(); - folder_it != content["folders"].endArray(); - ++folder_it) - { - LLSD folder_sd = *folder_it; - - - //LLUUID agent_id = folder_sd["agent_id"]; - - //if(agent_id != gAgent.getID()) //This should never happen. - //{ - // llwarns << "Got a UpdateInventoryItem for the wrong agent." - // << llendl; - // break; - //} - - LLUUID parent_id = folder_sd["folder_id"]; - LLUUID owner_id = folder_sd["owner_id"]; - S32 version = (S32)folder_sd["version"].asInteger(); - S32 descendents = (S32)folder_sd["descendents"].asInteger(); - LLPointer tcategory = new LLViewerInventoryCategory(owner_id); - - if (parent_id.isNull()) - { - LLPointer titem = new LLViewerInventoryItem; - for(LLSD::array_const_iterator item_it = folder_sd["items"].beginArray(); - item_it != folder_sd["items"].endArray(); - ++item_it) - { - LLUUID lost_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); - if (lost_uuid.notNull()) - { - LLSD item = *item_it; - titem->unpackMessage(item); - - LLInventoryModel::update_list_t update; - LLInventoryModel::LLCategoryUpdate new_folder(lost_uuid, 1); - update.push_back(new_folder); - gInventory.accountForUpdate(update); - - titem->setParent(lost_uuid); - titem->updateParentOnServer(FALSE); - gInventory.updateItem(titem); - gInventory.notifyObservers(); - - } - } - } - - LLViewerInventoryCategory* pcat = gInventory.getCategory(parent_id); - if (!pcat) - { - continue; - } - - for(LLSD::array_const_iterator category_it = folder_sd["categories"].beginArray(); - category_it != folder_sd["categories"].endArray(); - ++category_it) - { - LLSD category = *category_it; - tcategory->fromLLSD(category); - - if (LLInventoryModel::sFullFetchStarted) - { - sFetchQueue.push_back(tcategory->getUUID()); - } - else if ( !gInventory.isCategoryComplete(tcategory->getUUID()) ) - { - gInventory.updateCategory(tcategory); - } - - } - LLPointer titem = new LLViewerInventoryItem; - for(LLSD::array_const_iterator item_it = folder_sd["items"].beginArray(); - item_it != folder_sd["items"].endArray(); - ++item_it) - { - LLSD item = *item_it; - titem->unpackMessage(item); - - gInventory.updateItem(titem); - } - - // set version and descendentcount according to message. - LLViewerInventoryCategory* cat = gInventory.getCategory(parent_id); - if(cat) - { - cat->setVersion(version); - cat->setDescendentCount(descendents); - } - - } - } - - if (content.has("bad_folders")) - { - for(LLSD::array_const_iterator folder_it = content["bad_folders"].beginArray(); - folder_it != content["bad_folders"].endArray(); - ++folder_it) - { - LLSD folder_sd = *folder_it; - - //These folders failed on the dataserver. We probably don't want to retry them. - LL_INFOS("Inventory") << "Folder " << folder_sd["folder_id"].asString() - << "Error: " << folder_sd["error"].asString() << LL_ENDL; - } - } - - LLInventoryModel::incrBulkFetch(-1); - - if (LLInventoryModel::isBulkFetchProcessingComplete()) - { - LL_DEBUGS("Inventory") << "Inventory fetch completed" << LL_ENDL; - if (LLInventoryModel::sFullFetchStarted) - { - LLInventoryModel::sAllFoldersFetched = TRUE; - } - LLInventoryModel::stopBackgroundFetch(); - } - - gInventory.notifyObservers(); -} - -//If we get back an error (not found, etc...), handle it here -void fetchDescendentsResponder::error(U32 status, const std::string& reason) -{ - LL_INFOS("Inventory") << "fetchDescendentsResponder::error " - << status << ": " << reason << LL_ENDL; - - LLInventoryModel::incrBulkFetch(-1); - - if (status==499) //timed out. Let's be awesome! - { - for(LLSD::array_const_iterator folder_it = mRequestSD["folders"].beginArray(); - folder_it != mRequestSD["folders"].endArray(); - ++folder_it) - { - LLSD folder_sd = *folder_it; - LLUUID folder_id = folder_sd["folder_id"]; - sFetchQueue.push_front(folder_id); - } - } - else - { - if (LLInventoryModel::isBulkFetchProcessingComplete()) - { - if (LLInventoryModel::sFullFetchStarted) - { - LLInventoryModel::sAllFoldersFetched = TRUE; - } - LLInventoryModel::stopBackgroundFetch(); - } - } - gInventory.notifyObservers(); -} - -//static Bundle up a bunch of requests to send all at once. -void LLInventoryModel::bulkFetch(std::string url) -{ - //Background fetch is called from gIdleCallbacks in a loop until background fetch is stopped. - //If there are items in sFetchQueue, we want to check the time since the last bulkFetch was - //sent. If it exceeds our retry time, go ahead and fire off another batch. - //Stopbackgroundfetch will be run from the Responder instead of here. - - S16 max_concurrent_fetches=8; - F32 new_min_time = 0.5f; //HACK! Clean this up when old code goes away entirely. - if (sMinTimeBetweenFetches < new_min_time) sMinTimeBetweenFetches=new_min_time; //HACK! See above. - - if(gDisconnected - || sBulkFetchCount > max_concurrent_fetches - || sFetchTimer.getElapsedTimeF32() < sMinTimeBetweenFetches) - { - return; // just bail if we are disconnected. - } - - U32 folder_count=0; - U32 max_batch_size=5; - - U32 sort_order = gSavedSettings.getU32("InventorySortOrder") & 0x1; - - LLSD body; - LLSD body_lib; - while( !(sFetchQueue.empty() ) && (folder_count < max_batch_size) ) - { - if (sFetchQueue.front().isNull()) //DEV-17797 - { - LLSD folder_sd; - folder_sd["folder_id"] = LLUUID::null.asString(); - folder_sd["owner_id"] = gAgent.getID(); - folder_sd["sort_order"] = (LLSD::Integer)sort_order; - folder_sd["fetch_folders"] = (LLSD::Boolean)FALSE; - folder_sd["fetch_items"] = (LLSD::Boolean)TRUE; - body["folders"].append(folder_sd); - folder_count++; - } - else - { - - - LLViewerInventoryCategory* cat = gInventory.getCategory(sFetchQueue.front()); - - if (cat) - { - // Pre-emptive strike - //if(!(gInventory.isObjectDescendentOf(cat->getUUID(), gSystemFolderRoot))) - if(true) - { - // - if ( LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion()) - { - LLSD folder_sd; - folder_sd["folder_id"] = cat->getUUID(); - folder_sd["owner_id"] = cat->getOwnerID(); - folder_sd["sort_order"] = (LLSD::Integer)sort_order; - folder_sd["fetch_folders"] = TRUE; //(LLSD::Boolean)sFullFetchStarted; - folder_sd["fetch_items"] = (LLSD::Boolean)TRUE; - - LL_DEBUGS("Inventory") << " fetching "<getUUID()<<" with cat owner "<getOwnerID()<<" and agent" << gAgent.getID() << LL_ENDL; - if (ALEXANDRIA_LINDEN_ID == cat->getOwnerID()) - body_lib["folders"].append(folder_sd); - else - body["folders"].append(folder_sd); - folder_count++; - } - if (sFullFetchStarted) - { //Already have this folder but append child folders to list. - // add all children to queue - parent_cat_map_t::iterator cat_it = gInventory.mParentChildCategoryTree.find(cat->getUUID()); - if (cat_it != gInventory.mParentChildCategoryTree.end()) - { - cat_array_t* child_categories = cat_it->second; - - for (S32 child_num = 0; child_num < child_categories->count(); child_num++) - { - sFetchQueue.push_back(child_categories->get(child_num)->getUUID()); - } - } - - } - // - } - // - } - } - sFetchQueue.pop_front(); - } - - if (folder_count > 0) - { - sBulkFetchCount++; - if (body["folders"].size()) - { - LLHTTPClient::post(url, body, new fetchDescendentsResponder(body),300.0); - } - if (body_lib["folders"].size()) - { - std::string url_lib; - url_lib = gAgent.getRegion()->getCapability("FetchLibDescendents"); - LLHTTPClient::post(url_lib, body_lib, new fetchDescendentsResponder(body_lib),300.0); - } - sFetchTimer.reset(); - } - else if (isBulkFetchProcessingComplete()) - { - if (sFullFetchStarted) - { - sAllFoldersFetched = TRUE; - } - stopBackgroundFetch(); - } -} - -// static -bool LLInventoryModel::isEverythingFetched() -{ - return (sAllFoldersFetched ? true : false); -} - -//static -BOOL LLInventoryModel::backgroundFetchActive() -{ - return sBackgroundFetchActive; -} - -//static -void LLInventoryModel::startBackgroundFetch(const LLUUID& cat_id) -{ - if (!sAllFoldersFetched) - { - if (cat_id.isNull()) - { - if (!sFullFetchStarted) - { - sFullFetchStarted = TRUE; - sFetchQueue.push_back(gInventory.getLibraryRootFolderID()); - sFetchQueue.push_back(gInventory.getRootFolderID()); - if (!sBackgroundFetchActive) - { - sBackgroundFetchActive = TRUE; - gIdleCallbacks.addFunction(&LLInventoryModel::backgroundFetch, NULL); - } - } - } - else - { - // specific folder requests go to front of queue - // Remove it from the queue first, to avoid getting it twice. - if (!sFetchQueue.empty() && sFetchQueue.front() != cat_id) - { - std::deque::iterator old_entry = std::find(sFetchQueue.begin(), sFetchQueue.end(), cat_id); - if (old_entry != sFetchQueue.end()) - { - sFetchQueue.erase(old_entry); - } - } - sFetchQueue.push_front(cat_id); - if (!sBackgroundFetchActive) - { - sBackgroundFetchActive = TRUE; - gIdleCallbacks.addFunction(&LLInventoryModel::backgroundFetch, NULL); - } - } - } -} - -//static -void LLInventoryModel::findLostItems() -{ - sFetchQueue.push_back(LLUUID::null); - if (!sBackgroundFetchActive) - { - sBackgroundFetchActive = TRUE; - gIdleCallbacks.addFunction(&LLInventoryModel::backgroundFetch, NULL); - } -} - -//static -void LLInventoryModel::stopBackgroundFetch() -{ - if (sBackgroundFetchActive) - { - sBackgroundFetchActive = FALSE; - gIdleCallbacks.deleteFunction(&LLInventoryModel::backgroundFetch, NULL); - sBulkFetchCount=0; - sMinTimeBetweenFetches=0.0f; - if (!sAllFoldersFetched) - { - // We didn't finish this, so set it to FALSE in order to be able to start it again. - sFullFetchStarted=FALSE; - } - } -} - -//static -void LLInventoryModel::backgroundFetch(void*) -{ - if (sBackgroundFetchActive && gAgent.getRegion()) - { - // If we'll be using the capability, we'll be sending batches and the background thing isn't as important. - std::string url = gAgent.getRegion()->getCapability("FetchInventoryDescendents"); - if (false /*gSavedSettings.getBOOL("UseHTTPInventory")*/ && !url.empty()) - { - bulkFetch(url); - return; - } - -#if 1 - //DEPRECATED OLD CODE FOLLOWS. - // no more categories to fetch, stop fetch process - if (sFetchQueue.empty()) - { - llinfos << "Inventory fetch completed" << llendl; - if (sFullFetchStarted) - { - sAllFoldersFetched = TRUE; - } - stopBackgroundFetch(); - return; - } - - F32 fast_fetch_time = lerp(sMinTimeBetweenFetches, sMaxTimeBetweenFetches, 0.1f); - F32 slow_fetch_time = lerp(sMinTimeBetweenFetches, sMaxTimeBetweenFetches, 0.5f); - if (sTimelyFetchPending && sFetchTimer.getElapsedTimeF32() > slow_fetch_time) - { - // double timeouts on failure - sMinTimeBetweenFetches = llmin(sMinTimeBetweenFetches * 2.f, 10.f); - sMaxTimeBetweenFetches = llmin(sMaxTimeBetweenFetches * 2.f, 120.f); - llinfos << "Inventory fetch times grown to (" << sMinTimeBetweenFetches << ", " << sMaxTimeBetweenFetches << ")" << llendl; - // fetch is no longer considered "timely" although we will wait for full time-out - sTimelyFetchPending = FALSE; - } - - while(1) - { - if (sFetchQueue.empty()) - { - break; - } - - if(gDisconnected) - { - // just bail if we are disconnected. - break; - } - - LLViewerInventoryCategory* cat = gInventory.getCategory(sFetchQueue.front()); - - // category has been deleted, remove from queue. - if (!cat) - { - sFetchQueue.pop_front(); - continue; - } - - if (sFetchTimer.getElapsedTimeF32() > sMinTimeBetweenFetches && - LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion()) - { - // category exists but has no children yet, fetch the descendants - // for now, just request every time and rely on retry timer to throttle - if (cat->fetchDescendents()) - { - sFetchTimer.reset(); - sTimelyFetchPending = TRUE; - } - else - { - // The catagory also tracks if it has expired and here it says it hasn't - // yet. Get out of here because nothing is going to happen until we - // update the timers. - break; - } - } - // do I have all my children? - else if (gInventory.isCategoryComplete(sFetchQueue.front())) - { - // finished with this category, remove from queue - sFetchQueue.pop_front(); - - // add all children to queue - parent_cat_map_t::iterator cat_it = gInventory.mParentChildCategoryTree.find(cat->getUUID()); - if (cat_it != gInventory.mParentChildCategoryTree.end()) - { - cat_array_t* child_categories = cat_it->second; - - for (S32 child_num = 0; child_num < child_categories->count(); child_num++) - { - sFetchQueue.push_back(child_categories->get(child_num)->getUUID()); - } - } - - // we received a response in less than the fast time - if (sTimelyFetchPending && sFetchTimer.getElapsedTimeF32() < fast_fetch_time) - { - // shrink timeouts based on success - sMinTimeBetweenFetches = llmax(sMinTimeBetweenFetches * 0.8f, 0.3f); - sMaxTimeBetweenFetches = llmax(sMaxTimeBetweenFetches * 0.8f, 10.f); - //llinfos << "Inventory fetch times shrunk to (" << sMinTimeBetweenFetches << ", " << sMaxTimeBetweenFetches << ")" << llendl; - } - - sTimelyFetchPending = FALSE; - continue; - } - else if (sFetchTimer.getElapsedTimeF32() > sMaxTimeBetweenFetches) - { - // received first packet, but our num descendants does not match db's num descendants - // so try again later - LLUUID fetch_id = sFetchQueue.front(); - sFetchQueue.pop_front(); - - if (sNumFetchRetries++ < MAX_FETCH_RETRIES) - { - // push on back of queue - sFetchQueue.push_back(fetch_id); - } - sTimelyFetchPending = FALSE; - sFetchTimer.reset(); - break; - } - - // not enough time has elapsed to do a new fetch - break; - } - - // - // DEPRECATED OLD CODE - //-------------------------------------------------------------------------------- -#endif - } -} - void LLInventoryModel::cache( const LLUUID& parent_folder_id, const LLUUID& agent_id) diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index 95983ffc8..ae4e9451c 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -353,8 +353,6 @@ public: //void mock(const LLUUID& root_id); - // Add categories to a list to be fetched in bulk. - static void bulkFetch(std::string url); // call this method to request the inventory. //void requestFromServer(const LLUUID& agent_id); @@ -368,10 +366,10 @@ public: // name based on type, pass in a NULL to the 'name' parameter. LLUUID createNewCategory(const LLUUID& parent_id, LLFolderType::EType preferred_type, - const std::string& namevoid, + const std::string& name, void (*callback)(const LLSD&, void*) = NULL, void* user_data = NULL); - +public: // Internal methods that add inventory and make sure that all of // the internal data structures are consistent. These methods // should be passed pointers of newly created objects, and the @@ -384,6 +382,7 @@ public: typedef std::map response_t; typedef std::vector options_t; + //OGPX really screwed with the login process. This is needed until it's all sorted out. bool loadSkeleton(const options_t& options, const LLUUID& owner_id); /** Mutators @@ -477,14 +476,6 @@ private: typedef std::set observer_list_t; observer_list_t mObservers; -public: - // this gets triggered when performing a filter-search - static void startBackgroundFetch(const LLUUID& cat_id = LLUUID::null); // start fetch process - static void findLostItems(); - static BOOL backgroundFetchActive(); - static bool isEverythingFetched(); - static void backgroundFetch(void*); // background fetch idle function - static void incrBulkFetch(S16 fetching) { sBulkFetchCount+=fetching; if (sBulkFetchCount<0) sBulkFetchCount=0; } /** Notifications ** ** *******************************************************************************/ @@ -551,25 +542,11 @@ private: std::map mCategoryLock; std::map mItemLock; - // completing the fetch once per session should be sufficient - static BOOL sBackgroundFetchActive; - static BOOL sTimelyFetchPending; - static S32 sNumFetchRetries; - static LLFrameTimer sFetchTimer; - static F32 sMinTimeBetweenFetches; - static F32 sMaxTimeBetweenFetches; - static S16 sBulkFetchCount; public: // *NOTE: DEBUG functionality void dumpInventory() const; - static bool isBulkFetchProcessingComplete(); - static void stopBackgroundFetch(); // stop fetch process - - static BOOL sFullFetchStarted; - static BOOL sAllFoldersFetched; - /** Miscellaneous ** ** *******************************************************************************/ diff --git a/indra/newview/llinventorymodelbackgroundfetch.cpp b/indra/newview/llinventorymodelbackgroundfetch.cpp new file mode 100644 index 000000000..41a75d727 --- /dev/null +++ b/indra/newview/llinventorymodelbackgroundfetch.cpp @@ -0,0 +1,600 @@ +/** + * @file llinventorymodel.cpp + * @brief Implementation of the inventory model used to track agent inventory. + * + * $LicenseInfo:firstyear=2002&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 "llinventorymodelbackgroundfetch.h" + +#include "llagent.h" +#include "llappviewer.h" +#include "llcallbacklist.h" +#include "llinventoryview.h" +#include "llinventorymodel.h" +#include "llviewercontrol.h" +#include "llviewerinventory.h" +#include "llviewermessage.h" +#include "llviewerregion.h" +#include "llviewerwindow.h" + +const F32 MAX_TIME_FOR_SINGLE_FETCH = 10.f; +const S32 MAX_FETCH_RETRIES = 10; + +LLInventoryModelBackgroundFetch::LLInventoryModelBackgroundFetch() : + mBackgroundFetchActive(FALSE), + mAllFoldersFetched(FALSE), + mFullFetchStarted(FALSE), + mNumFetchRetries(0), + mMinTimeBetweenFetches(0.3f), + mMaxTimeBetweenFetches(10.f), + mTimelyFetchPending(FALSE), + mBulkFetchCount(0) +{ +} + +LLInventoryModelBackgroundFetch::~LLInventoryModelBackgroundFetch() +{ +} + +bool LLInventoryModelBackgroundFetch::isBulkFetchProcessingComplete() const +{ + return mFetchQueue.empty() && mBulkFetchCount<=0; +} + +// static +bool LLInventoryModelBackgroundFetch::isEverythingFetched() const +{ + return mAllFoldersFetched; +} + +BOOL LLInventoryModelBackgroundFetch::backgroundFetchActive() const +{ + return mBackgroundFetchActive; +} + +//static +void LLInventoryModelBackgroundFetch::startBackgroundFetch(const LLUUID& cat_id) +{ + if (!mAllFoldersFetched) + { + if (cat_id.isNull()) + { + if (!mFullFetchStarted) + { + mFullFetchStarted = TRUE; + mFetchQueue.push_back(gInventory.getLibraryRootFolderID()); + mFetchQueue.push_back(gInventory.getRootFolderID()); + if (!mBackgroundFetchActive) + { + mBackgroundFetchActive = TRUE; + gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); + } + } + } + else + { + // specific folder requests go to front of queue + // Remove it from the queue first, to avoid getting it twice. + if (!mFetchQueue.empty() && mFetchQueue.front().mCatUUID != cat_id) + { + std::deque::iterator old_entry = std::find(mFetchQueue.begin(), mFetchQueue.end(), FetchQueueInfo(cat_id)); + if (old_entry != mFetchQueue.end()) + { + mFetchQueue.erase(old_entry); + } + } + mFetchQueue.push_front(cat_id); + if (!mBackgroundFetchActive) + { + mBackgroundFetchActive = TRUE; + gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); + } + } + } +} + +//static +void LLInventoryModelBackgroundFetch::findLostItems() +{ + mFetchQueue.push_back(LLUUID::null); + if (!mBackgroundFetchActive) + { + mBackgroundFetchActive = TRUE; + gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); + } +} + +//static +void LLInventoryModelBackgroundFetch::stopBackgroundFetch() +{ + if (mBackgroundFetchActive) + { + mBackgroundFetchActive = FALSE; + gIdleCallbacks.deleteFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); + mBulkFetchCount=0; + mMinTimeBetweenFetches=0.0f; + if (!mAllFoldersFetched) + { + // We didn't finish this, so set it to FALSE in order to be able to start it again. + mFullFetchStarted=FALSE; + } + } +} + +void LLInventoryModelBackgroundFetch::setAllFoldersFetched() +{ + if (mFullFetchStarted) + { + mAllFoldersFetched = TRUE; + } + stopBackgroundFetch(); +} + +void LLInventoryModelBackgroundFetch::backgroundFetchCB(void *) +{ + LLInventoryModelBackgroundFetch::instance().backgroundFetch(); +} + +//static +void LLInventoryModelBackgroundFetch::backgroundFetch() +{ + if (mBackgroundFetchActive && gAgent.getRegion()) + { + // If we'll be using the capability, we'll be sending batches and the background thing isn't as important. + std::string url = gAgent.getRegion()->getCapability("FetchInventoryDescendents"); + if (false /*gSavedSettings.getBOOL("UseHTTPInventory")*/ && !url.empty()) + { + bulkFetch(url); + return; + } + +#if 1 + //DEPRECATED OLD CODE FOLLOWS. + // no more categories to fetch, stop fetch process + if (mFetchQueue.empty()) + { + llinfos << "Inventory fetch completed" << llendl; + + setAllFoldersFetched(); + return; + } + + F32 fast_fetch_time = lerp(mMinTimeBetweenFetches, mMaxTimeBetweenFetches, 0.1f); + F32 slow_fetch_time = lerp(mMinTimeBetweenFetches, mMaxTimeBetweenFetches, 0.5f); + if (mTimelyFetchPending && mFetchTimer.getElapsedTimeF32() > slow_fetch_time) + { + // double timeouts on failure + mMinTimeBetweenFetches = llmin(mMinTimeBetweenFetches * 2.f, 10.f); + mMaxTimeBetweenFetches = llmin(mMaxTimeBetweenFetches * 2.f, 120.f); + lldebugs << "Inventory fetch times grown to (" << mMinTimeBetweenFetches << ", " << mMaxTimeBetweenFetches << ")" << llendl; + // fetch is no longer considered "timely" although we will wait for full time-out. + mTimelyFetchPending = FALSE; + } + + while(1) + { + if (mFetchQueue.empty()) + { + break; + } + + if(gDisconnected) + { + // just bail if we are disconnected. + break; + } + + const FetchQueueInfo info = mFetchQueue.front(); + LLViewerInventoryCategory* cat = gInventory.getCategory(info.mCatUUID); + + // category has been deleted, remove from queue. + if (!cat) + { + mFetchQueue.pop_front(); + continue; + } + + if (mFetchTimer.getElapsedTimeF32() > mMinTimeBetweenFetches && + LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion()) + { + // category exists but has no children yet, fetch the descendants + // for now, just request every time and rely on retry timer to throttle + if (cat->fetchDescendents()) + { + mFetchTimer.reset(); + mTimelyFetchPending = TRUE; + } + else + { + // The catagory also tracks if it has expired and here it says it hasn't + // yet. Get out of here because nothing is going to happen until we + // update the timers. + break; + } + } + // do I have all my children? + else if (gInventory.isCategoryComplete(info.mCatUUID)) + { + // finished with this category, remove from queue + mFetchQueue.pop_front(); + + // Add all children to queue. + LLInventoryModel::cat_array_t* categories; + gInventory.getDirectDescendentsOf(cat->getUUID(), categories); + for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin(); + it != categories->end(); + ++it) + { + mFetchQueue.push_back(FetchQueueInfo((*it)->getUUID())); + } + + // we received a response in less than the fast time + if (mTimelyFetchPending && mFetchTimer.getElapsedTimeF32() < fast_fetch_time) + { + // shrink timeouts based on success + mMinTimeBetweenFetches = llmax(mMinTimeBetweenFetches * 0.8f, 0.3f); + mMaxTimeBetweenFetches = llmax(mMaxTimeBetweenFetches * 0.8f, 10.f); + lldebugs << "Inventory fetch times shrunk to (" << mMinTimeBetweenFetches << ", " << mMaxTimeBetweenFetches << ")" << llendl; + } + + mTimelyFetchPending = FALSE; + continue; + } + else if (mFetchTimer.getElapsedTimeF32() > mMaxTimeBetweenFetches) + { + // received first packet, but our num descendants does not match db's num descendants + // so try again later. + mFetchQueue.pop_front(); + + if (mNumFetchRetries++ < MAX_FETCH_RETRIES) + { + // push on back of queue + mFetchQueue.push_back(info); + } + mTimelyFetchPending = FALSE; + mFetchTimer.reset(); + break; + } + + // not enough time has elapsed to do a new fetch + break; + } + + // + // DEPRECATED OLD CODE + //-------------------------------------------------------------------------------- +#endif + } +} + +void LLInventoryModelBackgroundFetch::incrBulkFetch(S16 fetching) +{ + mBulkFetchCount += fetching; + if (mBulkFetchCount < 0) + { + mBulkFetchCount = 0; + } +} + + +class LLInventoryModelFetchDescendentsResponder: public LLHTTPClient::Responder +{ + public: + LLInventoryModelFetchDescendentsResponder(const LLSD& request_sd) : + mRequestSD(request_sd) + {}; + //LLInventoryModelFetchDescendentsResponder() {}; + void result(const LLSD& content); + void error(U32 status, const std::string& reason); +private: + LLSD mRequestSD; +}; + +//If we get back a normal response, handle it here +// Note: this is the handler for WebFetchInventoryDescendents and agent/inventory caps +void LLInventoryModelFetchDescendentsResponder::result(const LLSD& content) +{ + LLInventoryModelBackgroundFetch *fetcher = LLInventoryModelBackgroundFetch::getInstance(); + if (content.has("folders")) + { + + for(LLSD::array_const_iterator folder_it = content["folders"].beginArray(); + folder_it != content["folders"].endArray(); + ++folder_it) + { + LLSD folder_sd = *folder_it; + + + //LLUUID agent_id = folder_sd["agent_id"]; + + //if(agent_id != gAgent.getID()) //This should never happen. + //{ + // llwarns << "Got a UpdateInventoryItem for the wrong agent." + // << llendl; + // break; + //} + + LLUUID parent_id = folder_sd["folder_id"]; + LLUUID owner_id = folder_sd["owner_id"]; + S32 version = (S32)folder_sd["version"].asInteger(); + S32 descendents = (S32)folder_sd["descendents"].asInteger(); + LLPointer tcategory = new LLViewerInventoryCategory(owner_id); + + if (parent_id.isNull()) + { + LLPointer titem = new LLViewerInventoryItem; + for(LLSD::array_const_iterator item_it = folder_sd["items"].beginArray(); + item_it != folder_sd["items"].endArray(); + ++item_it) + { + LLUUID lost_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); + if (lost_uuid.notNull()) + { + LLSD item = *item_it; + titem->unpackMessage(item); + + LLInventoryModel::update_list_t update; + LLInventoryModel::LLCategoryUpdate new_folder(lost_uuid, 1); + update.push_back(new_folder); + gInventory.accountForUpdate(update); + + titem->setParent(lost_uuid); + titem->updateParentOnServer(FALSE); + gInventory.updateItem(titem); + gInventory.notifyObservers(); + + } + } + } + + LLViewerInventoryCategory* pcat = gInventory.getCategory(parent_id); + if (!pcat) + { + continue; + } + + for(LLSD::array_const_iterator category_it = folder_sd["categories"].beginArray(); + category_it != folder_sd["categories"].endArray(); + ++category_it) + { + LLSD category = *category_it; + tcategory->fromLLSD(category); + + if (fetcher->mFullFetchStarted) + { + fetcher->mFetchQueue.push_back(LLInventoryModelBackgroundFetch::FetchQueueInfo(tcategory->getUUID())); + } + else if ( !gInventory.isCategoryComplete(tcategory->getUUID()) ) + { + gInventory.updateCategory(tcategory); + } + + } + LLPointer titem = new LLViewerInventoryItem; + for(LLSD::array_const_iterator item_it = folder_sd["items"].beginArray(); + item_it != folder_sd["items"].endArray(); + ++item_it) + { + LLSD item = *item_it; + titem->unpackMessage(item); + + gInventory.updateItem(titem); + } + + // set version and descendentcount according to message. + LLViewerInventoryCategory* cat = gInventory.getCategory(parent_id); + if(cat) + { + cat->setVersion(version); + cat->setDescendentCount(descendents); + } + + } + } + + if (content.has("bad_folders")) + { + for(LLSD::array_const_iterator folder_it = content["bad_folders"].beginArray(); + folder_it != content["bad_folders"].endArray(); + ++folder_it) + { + LLSD folder_sd = *folder_it; + + //These folders failed on the dataserver. We probably don't want to retry them. + llinfos << "Folder " << folder_sd["folder_id"].asString() + << "Error: " << folder_sd["error"].asString() << llendl; + } + } + + fetcher->incrBulkFetch(-1); + + if (fetcher->isBulkFetchProcessingComplete()) + { + llinfos << "Inventory fetch completed" << llendl; + fetcher->setAllFoldersFetched(); + } + + gInventory.notifyObservers(); +} + +//If we get back an error (not found, etc...), handle it here +void LLInventoryModelFetchDescendentsResponder::error(U32 status, const std::string& reason) +{ + LLInventoryModelBackgroundFetch *fetcher = LLInventoryModelBackgroundFetch::getInstance(); + + llinfos << "LLInventoryModelFetchDescendentsResponder::error " + << status << ": " << reason << llendl; + + fetcher->incrBulkFetch(-1); + + if (status==499) //timed out. Let's be awesome! + { + for(LLSD::array_const_iterator folder_it = mRequestSD["folders"].beginArray(); + folder_it != mRequestSD["folders"].endArray(); + ++folder_it) + { + LLSD folder_sd = *folder_it; + LLUUID folder_id = folder_sd["folder_id"]; + fetcher->mFetchQueue.push_front(LLInventoryModelBackgroundFetch::FetchQueueInfo(folder_id)); + } + } + else + { + if (fetcher->isBulkFetchProcessingComplete()) + { + fetcher->setAllFoldersFetched(); + } + } + gInventory.notifyObservers(); +} + + +// Bundle up a bunch of requests to send all at once. +// static +void LLInventoryModelBackgroundFetch::bulkFetch(std::string url) +{ + //Background fetch is called from gIdleCallbacks in a loop until background fetch is stopped. + //If there are items in sFetchQueue, we want to check the time since the last bulkFetch was + //sent. If it exceeds our retry time, go ahead and fire off another batch. + //Stopbackgroundfetch will be run from the Responder instead of here. + + S16 max_concurrent_fetches=8; + F32 new_min_time = 0.5f; //HACK! Clean this up when old code goes away entirely. + if (mMinTimeBetweenFetches < new_min_time) + { + mMinTimeBetweenFetches=new_min_time; //HACK! See above. + } + + if (gDisconnected || + (mBulkFetchCount > max_concurrent_fetches) || + (mFetchTimer.getElapsedTimeF32() < mMinTimeBetweenFetches)) + { + return; // just bail if we are disconnected. + } + + U32 folder_count=0; + U32 max_batch_size=5; + + U32 sort_order = gSavedSettings.getU32("InventorySortOrder") & 0x1; + + LLSD body; + LLSD body_lib; + + while( !(mFetchQueue.empty() ) && (folder_count < max_batch_size) ) + { + const FetchQueueInfo& fetch_info = mFetchQueue.front(); + const LLUUID &cat_id = fetch_info.mCatUUID; + if (cat_id.isNull()) //DEV-17797 + { + LLSD folder_sd; + folder_sd["folder_id"] = LLUUID::null.asString(); + folder_sd["owner_id"] = gAgent.getID(); + folder_sd["sort_order"] = (LLSD::Integer)sort_order; + folder_sd["fetch_folders"] = (LLSD::Boolean)FALSE; + folder_sd["fetch_items"] = (LLSD::Boolean)TRUE; + body["folders"].append(folder_sd); + folder_count++; + } + else + { + LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id); + + if (cat) + { + // Pre-emptive strike + //if(!(gInventory.isObjectDescendentOf(cat->getUUID(), gSystemFolderRoot))) + if(true) + { + // + if ( LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion()) + { + LLSD folder_sd; + folder_sd["folder_id"] = cat->getUUID(); + folder_sd["owner_id"] = cat->getOwnerID(); + folder_sd["sort_order"] = (LLSD::Integer)sort_order; + folder_sd["fetch_folders"] = TRUE; //(LLSD::Boolean)sFullFetchStarted; + folder_sd["fetch_items"] = (LLSD::Boolean)TRUE; + + LL_DEBUGS("Inventory") << " fetching "<getUUID()<<" with cat owner "<getOwnerID()<<" and agent" << gAgent.getID() << LL_ENDL; + if (ALEXANDRIA_LINDEN_ID == cat->getOwnerID()) + body_lib["folders"].append(folder_sd); + else + body["folders"].append(folder_sd); + folder_count++; + } + // May already have this folder, but append child folders to list. + if (mFullFetchStarted) + { //Already have this folder but append child folders to list. + LLInventoryModel::cat_array_t* categories; + gInventory.getDirectDescendentsOf(cat->getUUID(), categories); + for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin(); + it != categories->end(); + ++it) + { + mFetchQueue.push_back(FetchQueueInfo((*it)->getUUID())); + } + } + // + } + // + } + } + mFetchQueue.pop_front(); + } + + if (folder_count > 0) + { + mBulkFetchCount++; + if (body["folders"].size()) + { + LLInventoryModelFetchDescendentsResponder *fetcher = new LLInventoryModelFetchDescendentsResponder(body); + LLHTTPClient::post(url, body, fetcher, 300.0); + } + if (body_lib["folders"].size()) + { + std::string url_lib = gAgent.getRegion()->getCapability("FetchLibDescendents"); + + LLInventoryModelFetchDescendentsResponder *fetcher = new LLInventoryModelFetchDescendentsResponder(body_lib); + LLHTTPClient::post(url_lib, body_lib, fetcher, 300.0); + } + mFetchTimer.reset(); + } + else if (isBulkFetchProcessingComplete()) + { + setAllFoldersFetched(); + } +} + +bool LLInventoryModelBackgroundFetch::fetchQueueContainsNoDescendentsOf(const LLUUID& cat_id) const +{ + for (fetch_queue_t::const_iterator it = mFetchQueue.begin(); + it != mFetchQueue.end(); ++it) + { + const LLUUID& fetch_id = (*it).mCatUUID; + if (gInventory.isObjectDescendentOf(fetch_id, cat_id)) + return false; + } + return true; +} + diff --git a/indra/newview/llinventorymodelbackgroundfetch.h b/indra/newview/llinventorymodelbackgroundfetch.h new file mode 100644 index 000000000..157cce420 --- /dev/null +++ b/indra/newview/llinventorymodelbackgroundfetch.h @@ -0,0 +1,94 @@ +/** + * @file llinventorymodelbackgroundfetch.h + * @brief LLInventoryModelBackgroundFetch class header file + * + * $LicenseInfo:firstyear=2002&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$ + */ + +#ifndef LL_LLINVENTORYMODELBACKGROUNDFETCH_H +#define LL_LLINVENTORYMODELBACKGROUNDFETCH_H + +#include "llsingleton.h" +#include "lluuid.h" + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInventoryModelBackgroundFetch +// +// This class handles background fetches, which are fetches of +// inventory folder. Fetches can be recursive or not. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLInventoryModelBackgroundFetch : public LLSingleton +{ + friend class LLInventoryModelFetchDescendentsResponder; + +public: + LLInventoryModelBackgroundFetch(); + ~LLInventoryModelBackgroundFetch(); + + void startBackgroundFetch(const LLUUID& cat_id = LLUUID::null); // start fetch process + BOOL backgroundFetchActive() const; + bool isEverythingFetched() const; // completing the fetch once per session should be sufficient + void findLostItems(); +protected: + void incrBulkFetch(S16 fetching); + bool isBulkFetchProcessingComplete() const; + void bulkFetch(std::string url); + + void backgroundFetch(); + static void backgroundFetchCB(void*); // background fetch idle function + void stopBackgroundFetch(); // stop fetch process + + void setAllFoldersFetched(); + bool fetchQueueContainsNoDescendentsOf(const LLUUID& cat_id) const; +private: + BOOL mFullFetchStarted; + BOOL mAllFoldersFetched; + + // completing the fetch once per session should be sufficient + BOOL mBackgroundFetchActive; + S16 mBulkFetchCount; + BOOL mTimelyFetchPending; + S32 mNumFetchRetries; + + LLFrameTimer mFetchTimer; + F32 mMinTimeBetweenFetches; + F32 mMaxTimeBetweenFetches; + + struct FetchQueueInfo + { + FetchQueueInfo(const LLUUID& id/*, BOOL recursive*/) : + mCatUUID(id)/*, mRecursive(recursive)*/ + { + } + LLUUID mCatUUID; + //BOOL mRecursive; + bool operator ==(const FetchQueueInfo& b) const + { + return mCatUUID == b.mCatUUID; + } + }; + typedef std::deque fetch_queue_t; + fetch_queue_t mFetchQueue; +}; + +#endif // LL_LLINVENTORYMODELBACKGROUNDFETCH_H + diff --git a/indra/newview/llinventoryview.cpp b/indra/newview/llinventoryview.cpp index 7cb0d5a90..86f1e40cb 100644 --- a/indra/newview/llinventoryview.cpp +++ b/indra/newview/llinventoryview.cpp @@ -59,6 +59,7 @@ #include "lliconctrl.h" #include "llinventoryfunctions.h" #include "llinventoryclipboard.h" +#include "llinventorymodelbackgroundfetch.h" #include "lllineeditor.h" #include "llmenugl.h" #include "llpreviewanim.h" @@ -619,7 +620,7 @@ LLInventoryView::~LLInventoryView( void ) void LLInventoryView::draw() { - if (LLInventoryModel::isEverythingFetched()) + if (LLInventoryModelBackgroundFetch::instance().isEverythingFetched()) { LLLocale locale(LLLocale::USER_LOCALE); std::ostringstream title; @@ -729,7 +730,7 @@ void LLInventoryView::changed(U32 mask) { std::ostringstream title; title << "Inventory"; - if (LLInventoryModel::backgroundFetchActive()) + if (LLInventoryModelBackgroundFetch::instance().backgroundFetchActive()) { LLLocale locale(LLLocale::USER_LOCALE); std::string item_count_string; @@ -870,7 +871,7 @@ void LLInventoryView::toggleFindOptions() addDependentFloater(mFinderHandle); // start background fetch of folders - gInventory.startBackgroundFetch(); + LLInventoryModelBackgroundFetch::instance().startBackgroundFetch(); mFloaterControls[std::string("Inventory.ShowFilters")]->setValue(TRUE); } @@ -946,7 +947,7 @@ void LLInventoryView::onSearchEdit(const std::string& search_string, void* user_ return; } - gInventory.startBackgroundFetch(); + LLInventoryModelBackgroundFetch::instance().startBackgroundFetch(); std::string filter_text = search_string; std::string uppercase_search_string = filter_text; @@ -1301,7 +1302,7 @@ void LLInventoryView::onFilterSelected(void* userdata, bool from_click) if (filter->isActive()) { // If our filter is active we may be the first thing requiring a fetch so we better start it here. - gInventory.startBackgroundFetch(); + LLInventoryModelBackgroundFetch::instance().startBackgroundFetch(); } self->setFilterTextFromFilter(); self->updateSortControls(); @@ -1907,7 +1908,7 @@ BOOL LLInventoryPanel::handleHover(S32 x, S32 y, MASK mask) if(handled) { ECursorType cursor = getWindow()->getCursor(); - if (LLInventoryModel::backgroundFetchActive() && cursor == UI_CURSOR_ARROW) + if (LLInventoryModelBackgroundFetch::instance().backgroundFetchActive() && cursor == UI_CURSOR_ARROW) { // replace arrow cursor with arrow and hourglass cursor getWindow()->setCursor(UI_CURSOR_WORKING); diff --git a/indra/newview/llpreviewgesture.cpp b/indra/newview/llpreviewgesture.cpp index ad22dce4c..c81024939 100644 --- a/indra/newview/llpreviewgesture.cpp +++ b/indra/newview/llpreviewgesture.cpp @@ -56,6 +56,7 @@ #include "llfloatergesture.h" // for some label constants #include "llgesturemgr.h" #include "llinventoryfunctions.h" +#include "llinventorymodelbackgroundfetch.h" #include "llkeyboard.h" #include "lllineeditor.h" #include "llnotificationsutil.h" @@ -158,10 +159,10 @@ LLPreviewGesture* LLPreviewGesture::show(const std::string& title, const LLUUID& // Start speculative download of sounds and animations LLUUID animation_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_ANIMATION); - gInventory.startBackgroundFetch(animation_folder_id); + LLInventoryModelBackgroundFetch::instance().startBackgroundFetch(animation_folder_id); LLUUID sound_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_SOUND); - gInventory.startBackgroundFetch(sound_folder_id); + LLInventoryModelBackgroundFetch::instance().startBackgroundFetch(sound_folder_id); // this will call refresh when we have everything. LLViewerInventoryItem* item = (LLViewerInventoryItem*)self->getItem(); diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 4aca35710..80e25e725 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -129,6 +129,7 @@ #include "llimagebmp.h" #include "llimview.h" // for gIMMgr #include "llinventoryfunctions.h" +#include "llinventorymodelbackgroundfetch.h" #include "llinventoryview.h" #include "llkeyboard.h" #include "llloginhandler.h" // gLoginHandler, SLURL support @@ -2712,7 +2713,7 @@ bool idle_startup() ) { // Fetch inventory in the background - gInventory.startBackgroundFetch(); + LLInventoryModelBackgroundFetch::instance().startBackgroundFetch(); } // HACK: Inform simulator of window size. @@ -2777,7 +2778,7 @@ bool idle_startup() } //DEV-17797. get null folder. Any items found here moved to Lost and Found - LLInventoryModel::findLostItems(); + LLInventoryModelBackgroundFetch::instance().findLostItems(); LLStartUp::setStartupState( STATE_PRECACHE ); timeout.reset(); diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index d09dbdb4c..5a88df2db 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -47,6 +47,7 @@ #include "llfoldervieweventlistener.h" #include "llinventory.h" #include "llinventoryfunctions.h" +#include "llinventorymodelbackgroundfetch.h" #include "llinventoryview.h" #include "lllineeditor.h" #include "llui.h" @@ -1344,7 +1345,7 @@ public: { // We need to find textures in all folders, so get the main // background download going. - gInventory.startBackgroundFetch(); + LLInventoryModelBackgroundFetch::instance().startBackgroundFetch(); gInventory.removeObserver(this); delete this; } @@ -1368,9 +1369,9 @@ BOOL LLTextureCtrl::handleMouseDown(S32 x, S32 y, MASK mask) showPicker(FALSE); //grab textures first... - gInventory.startBackgroundFetch(gInventory.findCategoryUUIDForType(LLFolderType::FT_TEXTURE)); + LLInventoryModelBackgroundFetch::instance().startBackgroundFetch(gInventory.findCategoryUUIDForType(LLFolderType::FT_TEXTURE)); //...then start full inventory fetch. - gInventory.startBackgroundFetch(); + LLInventoryModelBackgroundFetch::instance().startBackgroundFetch(); } return handled; } diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index b640b34e7..889b7a3d2 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -48,6 +48,7 @@ #include "llinventorybridge.h" #include "llinventorydefines.h" +#include "llinventorymodelbackgroundfetch.h" #include "llinventoryview.h" #include "llviewerregion.h" @@ -507,7 +508,7 @@ bool LLViewerInventoryCategory::fetchDescendents() } if (!url.empty()) //Capability found. Build up LLSD and use it. { - LLInventoryModel::startBackgroundFetch(mUUID); + LLInventoryModelBackgroundFetch::instance().startBackgroundFetch(mUUID); } else { //Deprecated, but if we don't have a capability, use the old system. diff --git a/indra/newview/statemachine/aifetchinventoryfolder.cpp b/indra/newview/statemachine/aifetchinventoryfolder.cpp index 4e8e16456..03ea78ed5 100644 --- a/indra/newview/statemachine/aifetchinventoryfolder.cpp +++ b/indra/newview/statemachine/aifetchinventoryfolder.cpp @@ -33,6 +33,7 @@ #include "aievent.h" #include "llagent.h" #include "llinventoryobserver.h" +#include "llinventorymodelbackgroundfetch.h" enum fetchinventoryfolder_state_type { AIFetchInventoryFolder_checkFolderExists = AIStateMachine::max_state, @@ -154,7 +155,7 @@ void AIFetchInventoryFolder::multiplex_impl(void) // mFolderUUID is now valid. mExists = true; if (!mFetchContents || // No request to fetch contents. - LLInventoryModel::isEverythingFetched()) // No need to fetch contents. + LLInventoryModelBackgroundFetch::instance().isEverythingFetched()) // No need to fetch contents. { // We're done. finish();