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(); gEventNotifier.update();
gIdleCallbacks.callFunctions(); gIdleCallbacks.callFunctions();
gInventory.idleNotifyObservers();
} }
if (gDisconnected) if (gDisconnected)

View File

@@ -221,9 +221,18 @@ bool LLCanCache::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
LLInventoryModel gInventory; LLInventoryModel gInventory;
// Default constructor // Default constructor
LLInventoryModel::LLInventoryModel() : LLInventoryModel::LLInventoryModel()
mModifyMask(LLInventoryObserver::ALL), : mModifyMask(LLInventoryObserver::ALL),
mChangedItemIDs(),
mCategoryMap(),
mItemMap(),
mCategoryLock(),
mItemLock(),
mLastItem(NULL), mLastItem(NULL),
mParentChildCategoryTree(),
mParentChildItemTree(),
mObservers(),
mIsNotifyObservers(FALSE),
mIsAgentInvUsable(false) mIsAgentInvUsable(false)
{ {
} }
@@ -231,12 +240,21 @@ LLInventoryModel::LLInventoryModel() :
// Destroys the object // Destroys the object
LLInventoryModel::~LLInventoryModel() LLInventoryModel::~LLInventoryModel()
{ {
empty(); cleanupInventory();
for (observer_list_t::iterator iter = mObservers.begin();
iter != mObservers.end(); ++iter)
{
delete *iter;
} }
void LLInventoryModel::cleanupInventory()
{
empty();
// Deleting one observer might erase others from the list, so always pop off the front
while (!mObservers.empty())
{
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 // 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 LLUUID& cat_id,
const BOOL break_on_recursion) const const BOOL break_on_recursion) const
{ {
if (obj_id == cat_id) return TRUE;
LLInventoryObject* obj = getObject(obj_id); LLInventoryObject* obj = getObject(obj_id);
int depthCounter = 0; int depthCounter = 0;
while(obj) while(obj)
@@ -758,12 +777,23 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item)
return mask; return mask;
} }
// We're hiding mesh types
#if 0
if (item->getType() == LLAssetType::AT_MESH)
{
return mask;
}
#endif
LLViewerInventoryItem* old_item = getItem(item->getUUID()); LLViewerInventoryItem* old_item = getItem(item->getUUID());
LLPointer<LLViewerInventoryItem> new_item;
if(old_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 old_parent_id = old_item->getParentUUID();
LLUUID new_parent_id = item->getParentUUID(); LLUUID new_parent_id = item->getParentUUID();
if(old_parent_id != new_parent_id) if(old_parent_id != new_parent_id)
{ {
// need to update the parent-child tree // need to update the parent-child tree
@@ -790,7 +820,7 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item)
else else
{ {
// Simply add this item // Simply add this item
LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item); new_item = new LLViewerInventoryItem(item);
addItem(new_item); addItem(new_item);
if(item->getParentUUID().isNull()) if(item->getParentUUID().isNull())
@@ -850,11 +880,40 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item)
} }
mask |= LLInventoryObserver::ADD; mask |= LLInventoryObserver::ADD;
} }
if(item->getType() == LLAssetType::AT_CALLINGCARD) if(new_item->getType() == LLAssetType::AT_CALLINGCARD)
{ {
mask |= LLInventoryObserver::CALLING_CARD; 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;
} }
addChangedMask(mask, item->getUUID()); else
{
// Fetch the current name
gCacheName->get(id, FALSE,
boost::bind(&LLViewerInventoryItem::onCallingCardNameLookup, new_item.get(),
_1, _2, _3));
}
}
}
/*else if (new_item->getType() == LLAssetType::AT_GESTURE)
{
mask |= LLInventoryObserver::GESTURE;
}*/
addChangedMask(mask, new_item->getUUID());
return mask; return mask;
} }
@@ -1033,6 +1092,7 @@ void LLInventoryModel::deleteObject(const LLUUID& id)
} }
addChangedMask(LLInventoryObserver::REMOVE, id); addChangedMask(LLInventoryObserver::REMOVE, id);
obj = NULL; // delete obj obj = NULL; // delete obj
gInventory.notifyObservers();
} }
// Delete a particular inventory item by ID, and remove it from the server. // Delete a particular inventory item by ID, and remove it from the server.
@@ -1219,39 +1279,56 @@ BOOL LLInventoryModel::containsObserver(LLInventoryObserver* observer) const
return mObservers.find(observer) != mObservers.end(); return mObservers.find(observer) != mObservers.end();
} }
// Call this method when it's time to update everyone on a new state, void LLInventoryModel::idleNotifyObservers()
// 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)
{ {
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(); for (observer_list_t::iterator iter = mObservers.begin();
iter != mObservers.end(); ) iter != mObservers.end(); )
{ {
LLInventoryObserver* observer = *iter; LLInventoryObserver* observer = *iter;
if (service_name.empty())
{
observer->changed(mModifyMask); observer->changed(mModifyMask);
}
else
{
observer->mMessageName = service_name;
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); iter = mObservers.upper_bound(observer);
} }
mModifyMask = LLInventoryObserver::NONE; mModifyMask = LLInventoryObserver::NONE;
mChangedItemIDs.clear(); mChangedItemIDs.clear();
mIsNotifyObservers = FALSE;
} }
// store flag for change // store flag for change
// and id of object change applies to // and id of object change applies to
void LLInventoryModel::addChangedMask(U32 mask, const LLUUID& referent) 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; mModifyMask |= mask;
if (referent.notNull()) 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 // 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
void LLInventoryModel::fetchInventoryResponder::result(const LLSD& content) void LLInventoryModel::fetchInventoryResponder::result(const LLSD& content)
{ {
start_new_inventory_observer(); start_new_inventory_observer();
@@ -1352,16 +1404,16 @@ void LLInventoryModel::fetchInventoryResponder::result(const LLSD& content)
{ {
changes |= gInventory.updateItem(*it); changes |= gInventory.updateItem(*it);
} }
gInventory.notifyObservers("fetchinventory"); gInventory.notifyObservers();
gViewerWindow->getWindow()->decBusyCount(); gViewerWindow->getWindow()->decBusyCount();
} }
//If we get back an error (not found, etc...), handle it here //If we get back an error (not found, etc...), handle it here
void LLInventoryModel::fetchInventoryResponder::error(U32 status, const std::string& reason) void LLInventoryModel::fetchInventoryResponder::error(U32 status, const std::string& reason)
{ {
LL_INFOS("Inventory") << "fetchInventory::error " llinfos << "fetchInventory::error "
<< status << ": " << reason << LL_ENDL; << status << ": " << reason << llendl;
gInventory.notifyObservers("fetchinventory"); gInventory.notifyObservers();
} }
bool LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id) const bool LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id) const
@@ -1462,7 +1514,7 @@ void fetchDescendentsResponder::result(const LLSD& content)
titem->setParent(lost_uuid); titem->setParent(lost_uuid);
titem->updateParentOnServer(FALSE); titem->updateParentOnServer(FALSE);
gInventory.updateItem(titem); gInventory.updateItem(titem);
gInventory.notifyObservers("fetchDescendents"); gInventory.notifyObservers();
} }
} }
@@ -1539,7 +1591,7 @@ void fetchDescendentsResponder::result(const LLSD& content)
LLInventoryModel::stopBackgroundFetch(); LLInventoryModel::stopBackgroundFetch();
} }
gInventory.notifyObservers("fetchDescendents"); gInventory.notifyObservers();
} }
//If we get back an error (not found, etc...), handle it here //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(); LLInventoryModel::stopBackgroundFetch();
} }
} }
gInventory.notifyObservers("fetchDescendents"); gInventory.notifyObservers();
} }
//static Bundle up a bunch of requests to send all at once. //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; //llinfos << "LLInventoryModel::addCategory()" << llendl;
if(category) if(category)
{ {
// We aren't displaying the Meshes folder
if (category->getPreferredType() == LLFolderType::FT_MESH)
{
return;
}
// Insert category uniquely into the map // Insert category uniquely into the map
mCategoryMap[category->getUUID()] = category; // LLPointer will deref and delete the old one mCategoryMap[category->getUUID()] = category; // LLPointer will deref and delete the old one
//mInventory[category->getUUID()] = category; //mInventory[category->getUUID()] = category;
@@ -1962,7 +2019,7 @@ void LLInventoryModel::addCategory(LLViewerInventoryCategory* category)
void LLInventoryModel::addItem(LLViewerInventoryItem* item) void LLInventoryModel::addItem(LLViewerInventoryItem* item)
{ {
//llinfos << "LLInventoryModel::addItem()" << llendl; llassert(item);
if(item) if(item)
{ {
// This can happen if assettype enums from llassettype.h ever change. // This can happen if assettype enums from llassettype.h ever change.
@@ -2030,7 +2087,7 @@ void LLInventoryModel::accountForUpdate(const LLCategoryUpdate& update) const
descendents_actual += update.mDescendentDelta; descendents_actual += update.mDescendentDelta;
cat->setDescendentCount(descendents_actual); cat->setDescendentCount(descendents_actual);
cat->setVersion(++version); cat->setVersion(++version);
llinfos << "accounted: '" << cat->getName() << "' " lldebugs << "accounted: '" << cat->getName() << "' "
<< version << " with " << descendents_actual << version << " with " << descendents_actual
<< " descendents." << llendl; << " descendents." << llendl;
} }
@@ -3214,7 +3271,9 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**)
} }
LLUUID tid; LLUUID tid;
msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_TransactionID, tid); msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_TransactionID, tid);
#ifndef LL_RELEASE_FOR_DOWNLOAD
llinfos << "Bulk inventory: " << tid << llendl; llinfos << "Bulk inventory: " << tid << llendl;
#endif
update_map_t update; update_map_t update;
cat_array_t folders; cat_array_t folders;
@@ -3342,7 +3401,7 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**)
LLInventoryView::sWearNewClothing = FALSE; LLInventoryView::sWearNewClothing = FALSE;
} }
if (tid == LLInventoryView::sWearNewClothingTransactionID) if (tid.notNull() && tid == LLInventoryView::sWearNewClothingTransactionID)
{ {
count = wearable_ids.size(); count = wearable_ids.size();
for (i = 0; i < count; ++i) for (i = 0; i < count; ++i)
@@ -3428,6 +3487,9 @@ void LLInventoryModel::processInventoryDescendents(LLMessageSystem* msg,void**)
{ {
cat->setVersion(version); cat->setVersion(version);
cat->setDescendentCount(descendents); 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(); gInventory.notifyObservers();
} }

View File

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

View File

@@ -1077,3 +1077,9 @@ LLViewerInventoryCategory *LLViewerInventoryItem::getLinkedCategory() const
} }
return NULL; 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; LLViewerInventoryItem *getLinkedItem() const;
LLViewerInventoryCategory *getLinkedCategory() const; LLViewerInventoryCategory *getLinkedCategory() const;
// callback
void onCallingCardNameLookup(const LLUUID& id, const std::string& name, bool is_group);
protected: protected:
BOOL mIsComplete; BOOL mIsComplete;
LLTransactionID mTransactionID; LLTransactionID mTransactionID;