Updated LLInventoryModel a bit. Safer cleanup, prevention of recursive notifyObservers calls, more sanity checks, added idleNotifyObservers that will call notifyObservers if stale flag(s) found.

This commit is contained in:
Shyotl
2011-10-10 00:19:01 -05:00
parent fc3ba78aed
commit d40dcfa1cd
5 changed files with 175 additions and 97 deletions

View File

@@ -3743,6 +3743,7 @@ void LLAppViewer::idle()
gEventNotifier.update();
gIdleCallbacks.callFunctions();
gInventory.idleNotifyObservers();
}
if (gDisconnected)

View File

@@ -221,22 +221,40 @@ bool LLCanCache::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
LLInventoryModel gInventory;
// Default constructor
LLInventoryModel::LLInventoryModel() :
mModifyMask(LLInventoryObserver::ALL),
LLInventoryModel::LLInventoryModel()
: mModifyMask(LLInventoryObserver::ALL),
mChangedItemIDs(),
mCategoryMap(),
mItemMap(),
mCategoryLock(),
mItemLock(),
mLastItem(NULL),
mParentChildCategoryTree(),
mParentChildItemTree(),
mObservers(),
mIsNotifyObservers(FALSE),
mIsAgentInvUsable(false)
{
}
// Destroys the object
LLInventoryModel::~LLInventoryModel()
{
cleanupInventory();
}
void LLInventoryModel::cleanupInventory()
{
empty();
for (observer_list_t::iterator iter = mObservers.begin();
iter != mObservers.end(); ++iter)
// Deleting one observer might erase others from the list, so always pop off the front
while (!mObservers.empty())
{
delete *iter;
observer_list_t::iterator iter = mObservers.begin();
LLInventoryObserver* observer = *iter;
mObservers.erase(iter);
delete observer;
}
mObservers.clear();
}
// This is a convenience function to check if one object has a parent
@@ -245,6 +263,7 @@ BOOL LLInventoryModel::isObjectDescendentOf(const LLUUID& obj_id,
const LLUUID& cat_id,
const BOOL break_on_recursion) const
{
if (obj_id == cat_id) return TRUE;
LLInventoryObject* obj = getObject(obj_id);
int depthCounter = 0;
while(obj)
@@ -758,12 +777,23 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item)
return mask;
}
// We're hiding mesh types
#if 0
if (item->getType() == LLAssetType::AT_MESH)
{
return mask;
}
#endif
LLViewerInventoryItem* old_item = getItem(item->getUUID());
LLPointer<LLViewerInventoryItem> new_item;
if(old_item)
{
// We already have an old item, modify it's values
// We already have an old item, modify its values
new_item = old_item;
LLUUID old_parent_id = old_item->getParentUUID();
LLUUID new_parent_id = item->getParentUUID();
if(old_parent_id != new_parent_id)
{
// need to update the parent-child tree
@@ -790,7 +820,7 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item)
else
{
// Simply add this item
LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
new_item = new LLViewerInventoryItem(item);
addItem(new_item);
if(item->getParentUUID().isNull())
@@ -850,11 +880,40 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item)
}
mask |= LLInventoryObserver::ADD;
}
if(item->getType() == LLAssetType::AT_CALLINGCARD)
if(new_item->getType() == LLAssetType::AT_CALLINGCARD)
{
mask |= LLInventoryObserver::CALLING_CARD;
// Handle user created calling cards.
// Target ID is stored in the description field of the card.
LLUUID id;
std::string desc = new_item->getDescription();
BOOL isId = desc.empty() ? FALSE : id.set(desc, FALSE);
if (isId)
{
// Valid UUID; set the item UUID and rename it
new_item->setCreator(id);
std::string avatar_name;
if (gCacheName->getFullName(id, avatar_name))
{
new_item->rename(avatar_name);
mask |= LLInventoryObserver::LABEL;
}
else
{
// Fetch the current name
gCacheName->get(id, FALSE,
boost::bind(&LLViewerInventoryItem::onCallingCardNameLookup, new_item.get(),
_1, _2, _3));
}
}
}
addChangedMask(mask, item->getUUID());
/*else if (new_item->getType() == LLAssetType::AT_GESTURE)
{
mask |= LLInventoryObserver::GESTURE;
}*/
addChangedMask(mask, new_item->getUUID());
return mask;
}
@@ -1001,39 +1060,40 @@ void LLInventoryModel::deleteObject(const LLUUID& id)
return;
}
lldebugs << "Deleting inventory object " << id << llendl;
mLastItem = NULL;
LLUUID parent_id = obj->getParentUUID();
mCategoryMap.erase(id);
mItemMap.erase(id);
//mInventory.erase(id);
item_array_t* item_list = getUnlockedItemArray(parent_id);
if(item_list)
{
LLViewerInventoryItem* item = (LLViewerInventoryItem*)((LLInventoryObject*)obj);
item_list->removeObj(item);
}
cat_array_t* cat_list = getUnlockedCatArray(parent_id);
if(cat_list)
{
LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)((LLInventoryObject*)obj);
cat_list->removeObj(cat);
}
item_list = getUnlockedItemArray(id);
if(item_list)
{
delete item_list;
mParentChildItemTree.erase(id);
}
cat_list = getUnlockedCatArray(id);
if(cat_list)
{
delete cat_list;
mParentChildCategoryTree.erase(id);
}
addChangedMask(LLInventoryObserver::REMOVE, id);
obj = NULL; // delete obj
lldebugs << "Deleting inventory object " << id << llendl;
mLastItem = NULL;
LLUUID parent_id = obj->getParentUUID();
mCategoryMap.erase(id);
mItemMap.erase(id);
//mInventory.erase(id);
item_array_t* item_list = getUnlockedItemArray(parent_id);
if(item_list)
{
LLViewerInventoryItem* item = (LLViewerInventoryItem*)((LLInventoryObject*)obj);
item_list->removeObj(item);
}
cat_array_t* cat_list = getUnlockedCatArray(parent_id);
if(cat_list)
{
LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)((LLInventoryObject*)obj);
cat_list->removeObj(cat);
}
item_list = getUnlockedItemArray(id);
if(item_list)
{
delete item_list;
mParentChildItemTree.erase(id);
}
cat_list = getUnlockedCatArray(id);
if(cat_list)
{
delete cat_list;
mParentChildCategoryTree.erase(id);
}
addChangedMask(LLInventoryObserver::REMOVE, id);
obj = NULL; // delete obj
gInventory.notifyObservers();
}
// Delete a particular inventory item by ID, and remove it from the server.
void LLInventoryModel::purgeObject(const LLUUID &id)
@@ -1219,39 +1279,56 @@ BOOL LLInventoryModel::containsObserver(LLInventoryObserver* observer) const
return mObservers.find(observer) != mObservers.end();
}
// Call this method when it's time to update everyone on a new state,
// by default, the inventory model will not update observers
// automatically.
// The optional argument 'service_name' is used by Agent Inventory Service [DEV-20328]
void LLInventoryModel::notifyObservers(const std::string service_name)
void LLInventoryModel::idleNotifyObservers()
{
if (mModifyMask == LLInventoryObserver::NONE && (mChangedItemIDs.size() == 0))
{
return;
}
notifyObservers();
}
// Call this method when it's time to update everyone on a new state.
void LLInventoryModel::notifyObservers()
{
if (mIsNotifyObservers)
{
// Within notifyObservers, something called notifyObservers
// again. This type of recursion is unsafe because it causes items to be
// processed twice, and this can easily lead to infinite loops.
llwarns << "Call was made to notifyObservers within notifyObservers!" << llendl;
return;
}
mIsNotifyObservers = TRUE;
for (observer_list_t::iterator iter = mObservers.begin();
iter != mObservers.end(); )
{
LLInventoryObserver* observer = *iter;
if (service_name.empty())
{
observer->changed(mModifyMask);
}
else
{
observer->mMessageName = service_name;
observer->changed(mModifyMask);
}
observer->changed(mModifyMask);
// safe way to incrament since changed may delete entries! (@!##%@!@&*!)
// safe way to increment since changed may delete entries! (@!##%@!@&*!)
iter = mObservers.upper_bound(observer);
}
mModifyMask = LLInventoryObserver::NONE;
mChangedItemIDs.clear();
mIsNotifyObservers = FALSE;
}
// store flag for change
// and id of object change applies to
void LLInventoryModel::addChangedMask(U32 mask, const LLUUID& referent)
{
if (mIsNotifyObservers)
{
// Something marked an item for change within a call to notifyObservers
// (which is in the process of processing the list of items marked for change).
// This means the change may fail to be processed.
llwarns << "Adding changed mask within notify observers! Change will likely be lost." << llendl;
}
mModifyMask |= mask;
if (referent.notNull())
{
@@ -1266,32 +1343,7 @@ void LLInventoryModel::addChangedMask(U32 mask, const LLUUID& referent)
}
}
// This method to prepares a set of mock inventory which provides
// minimal functionality before the actual arrival of inventory.
/*
void LLInventoryModel::mock(const LLUUID& root_id)
{
llinfos << "LLInventoryModel::mock() " << root_id << llendl;
if(root_id.isNull())
{
llwarns << "Not a valid root id" << llendl;
return;
}
LLPointer<LLViewerInventoryCategory> cat = new LLViewerInventoryCategory(
root_id,
LLUUID::null,
LLAssetType::AT_CATEGORY,
NEW_CATEGORY_NAMES[LLFolderType::FT_ROOT_CATEGORY],
gAgent.getID());
addCategory(cat);
gInventory.buildParentChildMap();
}
*/
//If we get back a normal response, handle it here
// Note: this is the responder used in "fetchInventory" cap,
// this is not responder for "WebFetchInventoryDescendents" or "agent/inventory" cap
// If we get back a normal response, handle it here
void LLInventoryModel::fetchInventoryResponder::result(const LLSD& content)
{
start_new_inventory_observer();
@@ -1352,16 +1404,16 @@ void LLInventoryModel::fetchInventoryResponder::result(const LLSD& content)
{
changes |= gInventory.updateItem(*it);
}
gInventory.notifyObservers("fetchinventory");
gInventory.notifyObservers();
gViewerWindow->getWindow()->decBusyCount();
}
//If we get back an error (not found, etc...), handle it here
void LLInventoryModel::fetchInventoryResponder::error(U32 status, const std::string& reason)
{
LL_INFOS("Inventory") << "fetchInventory::error "
<< status << ": " << reason << LL_ENDL;
gInventory.notifyObservers("fetchinventory");
llinfos << "fetchInventory::error "
<< status << ": " << reason << llendl;
gInventory.notifyObservers();
}
bool LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id) const
@@ -1462,7 +1514,7 @@ void fetchDescendentsResponder::result(const LLSD& content)
titem->setParent(lost_uuid);
titem->updateParentOnServer(FALSE);
gInventory.updateItem(titem);
gInventory.notifyObservers("fetchDescendents");
gInventory.notifyObservers();
}
}
@@ -1539,7 +1591,7 @@ void fetchDescendentsResponder::result(const LLSD& content)
LLInventoryModel::stopBackgroundFetch();
}
gInventory.notifyObservers("fetchDescendents");
gInventory.notifyObservers();
}
//If we get back an error (not found, etc...), handle it here
@@ -1572,7 +1624,7 @@ void fetchDescendentsResponder::error(U32 status, const std::string& reason)
LLInventoryModel::stopBackgroundFetch();
}
}
gInventory.notifyObservers("fetchDescendents");
gInventory.notifyObservers();
}
//static Bundle up a bunch of requests to send all at once.
@@ -1954,6 +2006,11 @@ void LLInventoryModel::addCategory(LLViewerInventoryCategory* category)
//llinfos << "LLInventoryModel::addCategory()" << llendl;
if(category)
{
// We aren't displaying the Meshes folder
if (category->getPreferredType() == LLFolderType::FT_MESH)
{
return;
}
// Insert category uniquely into the map
mCategoryMap[category->getUUID()] = category; // LLPointer will deref and delete the old one
//mInventory[category->getUUID()] = category;
@@ -1962,7 +2019,7 @@ void LLInventoryModel::addCategory(LLViewerInventoryCategory* category)
void LLInventoryModel::addItem(LLViewerInventoryItem* item)
{
//llinfos << "LLInventoryModel::addItem()" << llendl;
llassert(item);
if(item)
{
// This can happen if assettype enums from llassettype.h ever change.
@@ -2030,8 +2087,8 @@ void LLInventoryModel::accountForUpdate(const LLCategoryUpdate& update) const
descendents_actual += update.mDescendentDelta;
cat->setDescendentCount(descendents_actual);
cat->setVersion(++version);
llinfos << "accounted: '" << cat->getName() << "' "
<< version << " with " << descendents_actual
lldebugs << "accounted: '" << cat->getName() << "' "
<< version << " with " << descendents_actual
<< " descendents." << llendl;
}
}
@@ -3214,7 +3271,9 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**)
}
LLUUID tid;
msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_TransactionID, tid);
#ifndef LL_RELEASE_FOR_DOWNLOAD
llinfos << "Bulk inventory: " << tid << llendl;
#endif
update_map_t update;
cat_array_t folders;
@@ -3342,7 +3401,7 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**)
LLInventoryView::sWearNewClothing = FALSE;
}
if (tid == LLInventoryView::sWearNewClothingTransactionID)
if (tid.notNull() && tid == LLInventoryView::sWearNewClothingTransactionID)
{
count = wearable_ids.size();
for (i = 0; i < count; ++i)
@@ -3428,6 +3487,9 @@ void LLInventoryModel::processInventoryDescendents(LLMessageSystem* msg,void**)
{
cat->setVersion(version);
cat->setDescendentCount(descendents);
// Get this UUID on the changed list so that whatever's listening for it
// will get triggered.
gInventory.addChangedMask(LLInventoryObserver::INTERNAL, cat->getUUID());
}
gInventory.notifyObservers();
}

View File

@@ -131,6 +131,7 @@ public:
public:
LLInventoryModel();
~LLInventoryModel();
void cleanupInventory();
//<edit>
//protected:
//</edit>
@@ -375,7 +376,7 @@ public:
LLUUID createNewCategory(const LLUUID& parent_id,
LLFolderType::EType preferred_type,
const std::string& name);
// 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
@@ -446,10 +447,13 @@ public:
**/
public:
// Call to explicitly update everyone on a new state. The optional argument
// 'service_name' is used by Agent Inventory Service [DEV-20328]
void notifyObservers(const std::string service_name="");
// Called by the idle loop. Only updates if new state is detected. Call
// notifyObservers() manually to update regardless of whether state change
// has been indicated.
void idleNotifyObservers();
// Call to explicitly update everyone on a new state.
void notifyObservers();
// Allows outsiders to tell the inventory if something has
// been changed 'under the hood', but outside the control of the
// inventory. The next notify will include that notification.
@@ -459,6 +463,9 @@ protected:
// Updates all linked items pointing to this id.
void addChangedMaskForLinks(const LLUUID& object_id, U32 mask);
private:
// Flag set when notifyObservers is being called, to look for bugs
// where it's called recursively.
BOOL mIsNotifyObservers;
// Variables used to track what has changed since the last notify.
U32 mModifyMask;
changed_items_t mChangedItemIDs;

View File

@@ -1077,3 +1077,9 @@ LLViewerInventoryCategory *LLViewerInventoryItem::getLinkedCategory() const
}
return NULL;
}
void LLViewerInventoryItem::onCallingCardNameLookup(const LLUUID& id, const std::string& name, bool is_group)
{
rename(name);
gInventory.addChangedMask(LLInventoryObserver::LABEL, getUUID());
gInventory.notifyObservers();
}

View File

@@ -145,6 +145,8 @@ public:
LLViewerInventoryItem *getLinkedItem() const;
LLViewerInventoryCategory *getLinkedCategory() const;
// callback
void onCallingCardNameLookup(const LLUUID& id, const std::string& name, bool is_group);
protected:
BOOL mIsComplete;
LLTransactionID mTransactionID;