Over a dozen thread safety fixes in FriendsModule

viewer-2-initial-appearance
John Hurliman 2010-09-09 14:45:10 -07:00
parent 972ef92590
commit 8415b98806
1 changed files with 163 additions and 167 deletions

View File

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