From 8415b988063234ab7686b873520708891bca50a6 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Thu, 9 Sep 2010 14:45:10 -0700 Subject: Over a dozen thread safety fixes in FriendsModule --- .../CoreModules/Avatar/Friends/FriendsModule.cs | 330 ++++++++++----------- 1 file changed, 163 insertions(+), 167 deletions(-) (limited to 'OpenSim/Region/CoreModules/Avatar') diff --git a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs index 1c1ba2d..4e4eee9 100644 --- a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs @@ -54,7 +54,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends public UUID PrincipalID; public FriendInfo[] Friends; public int Refcount; - public UUID RegionID; public bool IsFriend(string friend) { @@ -67,7 +66,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends return false; } } - + + private static readonly FriendInfo[] EMPTY_FRIENDS = new FriendInfo[0]; private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); protected List m_Scenes = new List(); @@ -79,7 +79,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends protected Dictionary m_Friends = new Dictionary(); - protected List m_NeedsListOfFriends = new List(); + protected HashSet m_NeedsListOfFriends = new HashSet(); protected IPresenceService PresenceService { @@ -146,7 +146,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends // Instantiate the request handler IHttpServer server = MainServer.GetHttpServer((uint)mPort); server.AddStreamHandler(new FriendsRequestHandler(this)); - } if (m_FriendsService == null) @@ -173,7 +172,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends scene.EventManager.OnNewClient += OnNewClient; scene.EventManager.OnClientClosed += OnClientClosed; scene.EventManager.OnMakeRootAgent += OnMakeRootAgent; - scene.EventManager.OnMakeChildAgent += OnMakeChildAgent; scene.EventManager.OnClientLogin += OnClientLogin; } @@ -198,16 +196,13 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends public uint GetFriendPerms(UUID principalID, UUID friendID) { - if (!m_Friends.ContainsKey(principalID)) - return 0; - - UserFriendData data = m_Friends[principalID]; - - foreach (FriendInfo fi in data.Friends) + FriendInfo[] friends = GetFriends(principalID); + foreach (FriendInfo fi in friends) { if (fi.Friend == friendID.ToString()) return (uint)fi.TheirFlags; } + return 0; } @@ -217,73 +212,59 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends client.OnApproveFriendRequest += OnApproveFriendRequest; client.OnDenyFriendRequest += OnDenyFriendRequest; client.OnTerminateFriendship += OnTerminateFriendship; - client.OnGrantUserRights += OnGrantUserRights; - lock (m_Friends) - { - if (m_Friends.ContainsKey(client.AgentId)) + // Asynchronously fetch the friends list or increment the refcount for the existing + // friends list + Util.FireAndForget( + delegate(object o) { - m_Friends[client.AgentId].Refcount++; - return; - } - - UserFriendData newFriends = new UserFriendData(); - - newFriends.PrincipalID = client.AgentId; - newFriends.Friends = m_FriendsService.GetFriends(client.AgentId); - newFriends.Refcount = 1; - newFriends.RegionID = UUID.Zero; + lock (m_Friends) + { + UserFriendData friendsData; + if (m_Friends.TryGetValue(client.AgentId, out friendsData)) + { + friendsData.Refcount++; + } + else + { + friendsData = new UserFriendData(); + friendsData.PrincipalID = client.AgentId; + friendsData.Friends = FriendsService.GetFriends(client.AgentId); + friendsData.Refcount = 1; - m_Friends.Add(client.AgentId, newFriends); - } - + m_Friends[client.AgentId] = friendsData; + } + } + } + ); } private void OnClientClosed(UUID agentID, Scene scene) { ScenePresence sp = scene.GetScenePresence(agentID); if (sp != null && !sp.IsChildAgent) + { // do this for root agents closing out StatusChange(agentID, false); + } lock (m_Friends) - if (m_Friends.ContainsKey(agentID)) + { + UserFriendData friendsData; + if (m_Friends.TryGetValue(agentID, out friendsData)) { - if (m_Friends[agentID].Refcount == 1) + friendsData.Refcount--; + if (friendsData.Refcount <= 0) m_Friends.Remove(agentID); - else - m_Friends[agentID].Refcount--; } - } - - private void OnMakeRootAgent(ScenePresence sp) - { - UUID agentID = sp.ControllingClient.AgentId; - - if (m_Friends.ContainsKey(agentID)) - { - // This is probably an overkill, but just - // to make sure we have the latest and greatest - // friends list -- always pull OnMakeRoot - m_Friends[agentID].Friends = - m_FriendsService.GetFriends(agentID); - - m_Friends[agentID].RegionID = - sp.ControllingClient.Scene.RegionInfo.RegionID; } } - - private void OnMakeChildAgent(ScenePresence sp) + private void OnMakeRootAgent(ScenePresence sp) { UUID agentID = sp.ControllingClient.AgentId; - - if (m_Friends.ContainsKey(agentID)) - { - if (m_Friends[agentID].RegionID == sp.ControllingClient.Scene.RegionInfo.RegionID) - m_Friends[agentID].RegionID = UUID.Zero; - } + UpdateFriendsCache(agentID); } private void OnClientLogin(IClientAPI client) @@ -295,72 +276,56 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends // Register that we need to send the list of online friends to this user lock (m_NeedsListOfFriends) - if (!m_NeedsListOfFriends.Contains(agentID)) - { - m_NeedsListOfFriends.Add(agentID); - } + m_NeedsListOfFriends.Add(agentID); } public void SendFriendsOnlineIfNeeded(IClientAPI client) { UUID agentID = client.AgentId; - if (m_NeedsListOfFriends.Contains(agentID)) + + // Check if the online friends list is needed + lock (m_NeedsListOfFriends) { - if (!m_Friends.ContainsKey(agentID)) - { - m_log.DebugFormat("[FRIENDS MODULE]: agent {0} not found in local cache", agentID); + if (!m_NeedsListOfFriends.Remove(agentID)) return; - } - - // - // Send the friends online - // - List online = GetOnlineFriends(agentID); - if (online.Count > 0) - { - m_log.DebugFormat("[FRIENDS MODULE]: User {0} in region {1} has {2} friends online", client.AgentId, client.Scene.RegionInfo.RegionName, online.Count); - client.SendAgentOnline(online.ToArray()); - } - - // - // Send outstanding friendship offers - // - if (m_Friends.ContainsKey(agentID)) - { - List outstanding = new List(); + } - foreach (FriendInfo fi in m_Friends[agentID].Friends) - if (fi.TheirFlags == -1) - outstanding.Add(fi.Friend); + // Send the friends online + List online = GetOnlineFriends(agentID); + if (online.Count > 0) + { + m_log.DebugFormat("[FRIENDS MODULE]: User {0} in region {1} has {2} friends online", client.AgentId, client.Scene.RegionInfo.RegionName, online.Count); + client.SendAgentOnline(online.ToArray()); + } - GridInstantMessage im = new GridInstantMessage(client.Scene, UUID.Zero, "", agentID, (byte)InstantMessageDialog.FriendshipOffered, "Will you be my friend?", true, Vector3.Zero); - foreach (string fid in outstanding) - { - try - { - im.fromAgentID = new Guid(fid); - } - catch - { - continue; - } + // Send outstanding friendship offers + List outstanding = new List(); + FriendInfo[] friends = GetFriends(agentID); + foreach (FriendInfo fi in friends) + { + if (fi.TheirFlags == -1) + outstanding.Add(fi.Friend); + } - UserAccount account = m_Scenes[0].UserAccountService.GetUserAccount(client.Scene.RegionInfo.ScopeID, new UUID(im.fromAgentID)); - im.fromAgentName = account.FirstName + " " + account.LastName; + GridInstantMessage im = new GridInstantMessage(client.Scene, UUID.Zero, String.Empty, agentID, (byte)InstantMessageDialog.FriendshipOffered, + "Will you be my friend?", true, Vector3.Zero); - PresenceInfo presence = PresenceService.GetAgent(new UUID(im.fromAgentID)); - if (presence != null) - im.offline = 0; + foreach (string fid in outstanding) + { + UUID fromAgentID; + if (!UUID.TryParse(fid, out fromAgentID)) + continue; - im.imSessionID = im.fromAgentID; + UserAccount account = m_Scenes[0].UserAccountService.GetUserAccount(client.Scene.RegionInfo.ScopeID, fromAgentID); + PresenceInfo presence = PresenceService.GetAgent(fromAgentID); - // Finally - LocalFriendshipOffered(agentID, im); - } - } + im.fromAgentID = fromAgentID.Guid; + im.fromAgentName = account.FirstName + " " + account.LastName; + im.offline = (byte)((presence == null) ? 1 : 0); + im.imSessionID = im.fromAgentID; - lock (m_NeedsListOfFriends) - m_NeedsListOfFriends.Remove(agentID); + // Finally + LocalFriendshipOffered(agentID, im); } } @@ -369,44 +334,46 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends List friendList = new List(); List online = new List(); - foreach (FriendInfo fi in m_Friends[userID].Friends) + FriendInfo[] friends = GetFriends(userID); + foreach (FriendInfo fi in friends) { if (((fi.TheirFlags & 1) != 0) && (fi.TheirFlags != -1)) friendList.Add(fi.Friend); } - if (friendList.Count == 0) - // no friends whatsoever - return online; - - PresenceInfo[] presence = PresenceService.GetAgents(friendList.ToArray()); - - foreach (PresenceInfo pi in presence) - online.Add(new UUID(pi.UserID)); - //m_log.DebugFormat("[XXX] {0} friend online {1}", userID, pi.UserID); + if (friendList.Count > 0) + { + PresenceInfo[] presence = PresenceService.GetAgents(friendList.ToArray()); + foreach (PresenceInfo pi in presence) + { + UUID presenceID; + if (UUID.TryParse(pi.UserID, out presenceID)) + online.Add(presenceID); + } + } return online; } - // - // Find the client for a ID - // + /// + /// Find the client for a ID + /// public IClientAPI LocateClientObject(UUID agentID) { Scene scene = GetClientScene(agentID); - if (scene == null) - return null; - - ScenePresence presence = scene.GetScenePresence(agentID); - if (presence == null) - return null; + if (scene != null) + { + ScenePresence presence = scene.GetScenePresence(agentID); + if (presence != null) + return presence.ControllingClient; + } - return presence.ControllingClient; + return null; } - // - // Find the scene for an agent - // + /// + /// Find the scene for an agent + /// private Scene GetClientScene(UUID agentId) { lock (m_Scenes) @@ -414,13 +381,11 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends foreach (Scene scene in m_Scenes) { ScenePresence presence = scene.GetScenePresence(agentId); - if (presence != null) - { - if (!presence.IsChildAgent) - return scene; - } + if (presence != null && !presence.IsChildAgent) + return scene; } } + return null; } @@ -431,35 +396,33 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends /// private void StatusChange(UUID agentID, bool online) { - //m_log.DebugFormat("[FRIENDS]: StatusChange {0}", online); - if (m_Friends.ContainsKey(agentID)) + FriendInfo[] friends = GetFriends(agentID); + if (friends.Length > 0) { - //m_log.DebugFormat("[FRIENDS]: # of friends: {0}", m_Friends[agentID].Friends.Length); List friendList = new List(); - foreach (FriendInfo fi in m_Friends[agentID].Friends) + foreach (FriendInfo fi in friends) { if (((fi.MyFlags & 1) != 0) && (fi.TheirFlags != -1)) friendList.Add(fi); } - Util.FireAndForget(delegate - { - foreach (FriendInfo fi in friendList) + Util.FireAndForget( + delegate { - //m_log.DebugFormat("[FRIENDS]: Notifying {0}", fi.PrincipalID); - // Notify about this user status - StatusNotify(fi, agentID, online); + foreach (FriendInfo fi in friendList) + { + //m_log.DebugFormat("[FRIENDS]: Notifying {0}", fi.PrincipalID); + // Notify about this user status + StatusNotify(fi, agentID, online); + } } - }); + ); } - else - m_log.WarnFormat("[FRIENDS]: {0} not found in cache", agentID); } private void StatusNotify(FriendInfo friend, UUID userID, bool online) { - UUID friendID = UUID.Zero; - + UUID friendID; if (UUID.TryParse(friend.Friend, out friendID)) { // Try local @@ -476,12 +439,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends } } else + { m_log.WarnFormat("[FRIENDS]: Error parsing friend ID {0}", friend.Friend); + } } private void OnInstantMessage(IClientAPI client, GridInstantMessage im) { - if (im.dialog == (byte)OpenMetaverse.InstantMessageDialog.FriendshipOffered) + if ((InstantMessageDialog)im.dialog == InstantMessageDialog.FriendshipOffered) { // we got a friendship offer UUID principalID = new UUID(im.fromAgentID); @@ -527,9 +492,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends FriendsService.StoreFriend(agentID, friendID.ToString(), 1); FriendsService.StoreFriend(friendID, agentID.ToString(), 1); - // update the local cache - m_Friends[agentID].Friends = FriendsService.GetFriends(agentID); + // Update the local cache + UpdateFriendsCache(agentID); // // Notify the friend @@ -584,7 +549,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends FriendsService.Delete(exfriendID, agentID.ToString()); // Update local cache - m_Friends[agentID].Friends = FriendsService.GetFriends(agentID); + UpdateFriendsCache(agentID); client.SendTerminateFriend(exfriendID); @@ -606,16 +571,18 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends private void OnGrantUserRights(IClientAPI remoteClient, UUID requester, UUID target, int rights) { - if (!m_Friends.ContainsKey(remoteClient.AgentId)) + FriendInfo[] friends = GetFriends(remoteClient.AgentId); + if (friends.Length == 0) return; m_log.DebugFormat("[FRIENDS MODULE]: User {0} changing rights to {1} for friend {2}", requester, rights, target); // Let's find the friend in this user's friend list - UserFriendData fd = m_Friends[remoteClient.AgentId]; FriendInfo friend = null; - foreach (FriendInfo fi in fd.Friends) + foreach (FriendInfo fi in friends) + { if (fi.Friend == target.ToString()) friend = fi; + } if (friend != null) // Found it { @@ -672,8 +639,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends GridInstantMessage im = new GridInstantMessage(Scene, userID, userName, friendID, (byte)OpenMetaverse.InstantMessageDialog.FriendshipAccepted, userID.ToString(), false, Vector3.Zero); friendClient.SendInstantMessage(im); - // update the local cache - m_Friends[friendID].Friends = FriendsService.GetFriends(friendID); + + // Update the local cache + UpdateFriendsCache(friendID); + // we're done return true; } @@ -687,7 +656,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends if (friendClient != null) { // the prospective friend in this sim as root agent - GridInstantMessage im = new GridInstantMessage(Scene, userID, userName, friendID, (byte)OpenMetaverse.InstantMessageDialog.FriendshipDeclined, userID.ToString(), false, Vector3.Zero); friendClient.SendInstantMessage(im); @@ -706,7 +674,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends // the friend in this sim as root agent friendClient.SendTerminateFriend(exfriendID); // update local cache - m_Friends[exfriendID].Friends = FriendsService.GetFriends(exfriendID); + UpdateFriendsCache(exfriendID); // we're done return true; } @@ -735,11 +703,16 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends } - // update local cache - //m_Friends[friendID].Friends = m_FriendsService.GetFriends(friendID); - foreach (FriendInfo finfo in m_Friends[friendID].Friends) - if (finfo.Friend == userID.ToString()) - finfo.TheirFlags = rights; + // Update local cache + lock (m_Friends) + { + FriendInfo[] friends = GetFriends(friendID); + foreach (FriendInfo finfo in friends) + { + if (finfo.Friend == userID.ToString()) + finfo.TheirFlags = rights; + } + } return true; } @@ -765,7 +738,30 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends return false; } + #endregion + private FriendInfo[] GetFriends(UUID agentID) + { + UserFriendData friendsData; + + lock (m_Friends) + { + if (m_Friends.TryGetValue(agentID, out friendsData)) + return friendsData.Friends; + } + + return EMPTY_FRIENDS; + } + + private void UpdateFriendsCache(UUID agentID) + { + lock (m_Friends) + { + UserFriendData friendsData; + if (m_Friends.TryGetValue(agentID, out friendsData)) + friendsData.Friends = FriendsService.GetFriends(agentID); + } + } } } -- cgit v1.1