Commits

Kelly Washington committed 937ec90

MAINT-1979 Viewer crashes while attempting to join group in the moment of loading group members
* Fix one race condition that could dereference a dangling pointer.
reviewed with Simon and Baker.

Comments (0)

Files changed (6)

indra/newview/llgroupmgr.cpp

 	mPendingRoleMemberRequest(FALSE),
 	mAccessTime(0.0f)
 {
+	mMemberVersion.generate();
 }
 
 void LLGroupMgrGroupData::setAccessed()
 			role_data.mChangeType = RC_UPDATE_DATA;
 		}
 		else
-	{
+		{
 			role_data.mChangeType = RC_UPDATE_POWERS;
 		}
 
 		mRoleChanges[role_id] = role_data;
 	}
 	else
-		{
+	{
 		llwarns << "Change being made to non-existant role " << role_id << llendl;
 	}
 }
 	}
 	mMembers.clear();
 	mMemberDataComplete = FALSE;
+	mMemberVersion.generate();
 }
 
 void LLGroupMgrGroupData::removeRoleData()
 		}
 	}
 
+	group_datap->mMemberVersion.generate();
+
 	if (group_datap->mMembers.size() ==  (U32)group_datap->mMemberCount)
 	{
 		group_datap->mMemberDataComplete = TRUE;
 	bool start_message = true;
 	LLMessageSystem* msg = gMessageSystem;
 
-	
-
 	LLGroupMgrGroupData* group_datap = LLGroupMgr::getInstance()->getGroupData(group_id);
 	if (!group_datap) return;
 
 	{
 		gAgent.sendReliableMessage();
 	}
+
+	group_datap->mMemberVersion.generate();
 }
 
 
 		group_datap->mMembers[member_id] = data;
 	}
 
+	group_datap->mMemberVersion.generate();
+
 	// Technically, we have this data, but to prevent completely overhauling
 	// this entire system (it would be nice, but I don't have the time), 
 	// I'm going to be dumb and just call services I most likely don't need 

indra/newview/llgroupmgr.h

 	F32 getAccessTime() const { return mAccessTime; }
 	void setAccessed();
 
+	const LLUUID& getMemberVersion() const { return mMemberVersion; }
+
 public:
 	typedef	std::map<LLUUID,LLGroupMemberData*> member_list_t;
 	typedef	std::map<LLUUID,LLGroupRoleData*> role_list_t;
 
 	BOOL				mPendingRoleMemberRequest;
 	F32					mAccessTime;
+
+	// Generate a new ID every time mMembers
+	LLUUID				mMemberVersion;
 };
 
 struct LLRoleAction

indra/newview/llpanelgroupgeneral.cpp

 		{
 			mMemberProgress = gdatap->mMembers.begin();
 			mPendingMemberUpdate = TRUE;
-			mUdpateSessionID.generate();
 
 			sSDTime = 0.0f;
 			sElementTime = 0.0f;
 			// If name is not cached, onNameCache() should be called when it is cached and add this member to list.
 			LLAvatarNameCache::get(mMemberProgress->first, 
 									boost::bind(&LLPanelGroupGeneral::onNameCache,
-												this, mUdpateSessionID, member, _1, _2));
+												this, gdatap->getMemberVersion(), member, _2));
 		}
 	}
 
 	}
 }
 
-void LLPanelGroupGeneral::onNameCache(const LLUUID& update_id, LLGroupMemberData* member, const LLUUID& id, const LLAvatarName& av_name)
+void LLPanelGroupGeneral::onNameCache(const LLUUID& update_id, LLGroupMemberData* member, const LLAvatarName& av_name)
 {
-	if (!member 
-		|| update_id != mUdpateSessionID)
+	LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
+
+	if (!gdatap
+		|| !gdatap->isMemberDataComplete()
+		|| gdatap->getMemberVersion() != update_id)
 	{
+		// Stale data
 		return;
 	}
 

indra/newview/llpanelgroupgeneral.h

 
 	virtual void setupCtrls	(LLPanel* parent);
 
-	void onNameCache(const LLUUID& update_id, LLGroupMemberData* member, const LLUUID& id, const LLAvatarName& av_name);
+	void onNameCache(const LLUUID& update_id, LLGroupMemberData* member, const LLAvatarName& av_name);
 private:
 	void	reset();
 
 	BOOL			mChanged;
 	BOOL			mFirstUse;
 	std::string		mIncompleteMemberDataStr;
-	LLUUID			mUdpateSessionID;
 
 	// Group information (include any updates in updateChanged)
 	LLLineEditor		*mGroupNameEditor;

indra/newview/llpanelgrouproles.cpp

 	mHasMatch(FALSE),
 	mNumOwnerAdditions(0)
 {
-	mUdpateSessionID = LLUUID::null;
 }
 
 LLPanelGroupMembersSubTab::~LLPanelGroupMembersSubTab()
 		return GP_NO_POWERS;
 	}
 
-	LLGroupMemberData* member_data = gdatap->mMembers[agent_id];
-	if ( !member_data )
+	LLGroupMgrGroupData::member_list_t::iterator iter = gdatap->mMembers.find(agent_id);
+	if ( iter == gdatap->mMembers.end() )
 	{
 		llwarns << "LLPanelGroupMembersSubTab::getAgentPowersBasedOnRoleChanges() -- No member data for member with UUID " << agent_id << llendl;
 		return GP_NO_POWERS;
 	}
 
+	LLGroupMemberData* member_data = (*iter).second;
+	if (!member_data)
+	{
+		llwarns << "LLPanelGroupMembersSubTab::getAgentPowersBasedOnRoleChanges() -- Null member data for member with UUID " << agent_id << llendl;
+		return GP_NO_POWERS;
+	}
+
 	//see if there are unsaved role changes for this agent
 	role_change_data_map_t* role_change_datap = NULL;
 	member_role_changes_map_t::iterator member = mMemberRoleChangeData.find(agent_id);
 		mMemberProgress = gdatap->mMembers.begin();
 		mPendingMemberUpdate = TRUE;
 		mHasMatch = FALSE;
-		// Generate unique ID for current updateMembers()- see onNameCache for details.
-		// Using unique UUID is perhaps an overkill but this way we are perfectly safe
-		// from coincidences.
-		mUdpateSessionID.generate();
 	}
 	else
 	{
 	}
 }
 
-void LLPanelGroupMembersSubTab::addMemberToList(LLUUID id, LLGroupMemberData* data)
+void LLPanelGroupMembersSubTab::addMemberToList(LLGroupMemberData* data)
 {
 	if (!data) return;
 	LLUIString donated = getString("donation_area");
 	donated.setArg("[AREA]", llformat("%d", data->getContribution()));
 
 	LLNameListCtrl::NameItem item_params;
-	item_params.value = id;
+	item_params.value = data->getID();
 
 	item_params.columns.add().column("name").font.name("SANSSERIF_SMALL").style("NORMAL");
 
 	mHasMatch = TRUE;
 }
 
-void LLPanelGroupMembersSubTab::onNameCache(const LLUUID& update_id, LLGroupMemberData* member, const LLUUID& id, const LLAvatarName& av_name)
+void LLPanelGroupMembersSubTab::onNameCache(const LLUUID& update_id, LLGroupMemberData* member, const LLAvatarName& av_name)
 {
-	// Update ID is used to determine whether member whose id is passed
-	// into onNameCache() was passed after current or previous user-initiated update.
-	// This is needed to avoid probable duplication of members in list after changing filter
-	// or adding of members of another group if gets for their names were called on
-	// previous update. If this id is from get() called from older update,
-	// we do nothing.
-	if (mUdpateSessionID != update_id) return;
-	
-	if (!member)
+	LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
+	if (!gdatap
+		|| gdatap->getMemberVersion() != update_id
+		|| !member)
 	{
 		return;
 	}
 	// trying to avoid unnecessary hash lookups
 	if (matchesSearchFilter(av_name.getLegacyName()))
 	{
-		addMemberToList(id, member);
+		addMemberToList(member);
 		if(!mMembersList->getEnabled())
 		{
 			mMembersList->setEnabled(TRUE);
 		{
 			if (matchesSearchFilter(av_name.getLegacyName()))
 			{
-				addMemberToList(mMemberProgress->first, mMemberProgress->second);
+				addMemberToList(mMemberProgress->second);
 			}
 		}
 		else
 		{
 			// If name is not cached, onNameCache() should be called when it is cached and add this member to list.
 			LLAvatarNameCache::get(mMemberProgress->first, boost::bind(&LLPanelGroupMembersSubTab::onNameCache,
-																	   this, mUdpateSessionID, mMemberProgress->second, _1, _2));
+									this, gdatap->getMemberVersion(), mMemberProgress->second, _2));
 		}
 	}
 

indra/newview/llpanelgrouproles.h

 
 	virtual void setGroupID(const LLUUID& id);
 
-	void addMemberToList(LLUUID id, LLGroupMemberData* data);
-	void onNameCache(const LLUUID& update_id, LLGroupMemberData* member, const LLUUID& id, const LLAvatarName& av_name);
+	void addMemberToList(LLGroupMemberData* data);
+	void onNameCache(const LLUUID& update_id, LLGroupMemberData* member, const LLAvatarName& av_name);
 
 protected:
 	typedef std::map<LLUUID, LLRoleMemberChangeType> role_change_data_map_t;
 	BOOL mPendingMemberUpdate;
 	BOOL mHasMatch;
 
-	// This id is generated after each user initiated member list update(opening Roles or changing filter)
-	LLUUID mUdpateSessionID;
-
 	member_role_changes_map_t mMemberRoleChangeData;
 	U32 mNumOwnerAdditions;