For clients that are entering a simulator from initial login, stop executing FriendsModule.FetchFriendslist() asychronously.

Executing this asynchronously allows a race condition where subsequent friends fetches hit a cache that FetchFriendsList() had not yet populated.
Changing this to synchronous may improve issues where a user does not see friends as online even though they are.
I don't believe synchronous is a problem here, but if it is, then a more complicated signalling mechanism is required.  Locking the cache isn't sufficient.
iar_mods
Justin Clark-Casey (justincc) 2011-11-15 15:57:53 +00:00
parent 8d0aaa359f
commit 50803dfe2c
3 changed files with 34 additions and 11 deletions

View File

@ -79,9 +79,19 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
protected IFriendsService m_FriendsService = null; protected IFriendsService m_FriendsService = null;
protected FriendsSimConnector m_FriendsSimConnector; protected FriendsSimConnector m_FriendsSimConnector;
protected Dictionary<UUID, UserFriendData> m_Friends = /// <summary>
new Dictionary<UUID, UserFriendData>(); /// Cache friends lists for users.
/// </summary>
/// <remarks>
/// This is a complex and error-prone thing to do. At the moment, we assume that the efficiency gained in
/// permissions checks outweighs the disadvantages of that complexity.
/// </remarks>
protected Dictionary<UUID, UserFriendData> m_Friends = new Dictionary<UUID, UserFriendData>();
/// <summary>
/// Maintain a record of viewers that need to be sent notifications for friends that are online. This only
/// needs to be done on login. Subsequent online/offline friend changes are sent by a different mechanism.
/// </summary>
protected HashSet<UUID> m_NeedsListOfFriends = new HashSet<UUID>(); protected HashSet<UUID> m_NeedsListOfFriends = new HashSet<UUID>();
protected IPresenceService PresenceService protected IPresenceService PresenceService
@ -189,6 +199,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
{ {
if (!m_Enabled) if (!m_Enabled)
return; return;
m_log.DebugFormat("[FRIENDS MODULE]: AddRegion on {0}", Name); m_log.DebugFormat("[FRIENDS MODULE]: AddRegion on {0}", Name);
m_Scenes.Add(scene); m_Scenes.Add(scene);
@ -244,12 +255,23 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
client.OnTerminateFriendship += (thisClient, agentID, exfriendID) => RemoveFriendship(thisClient, exfriendID); client.OnTerminateFriendship += (thisClient, agentID, exfriendID) => RemoveFriendship(thisClient, exfriendID);
client.OnGrantUserRights += OnGrantUserRights; client.OnGrantUserRights += OnGrantUserRights;
Util.FireAndForget(delegate { FetchFriendslist(client); }); // Do not do this asynchronously. If we do, then subsequent code can outrace FetchFriendsList() and
// return misleading results from the still empty friends cache.
// If we absolutely need to do this asynchronously, then a signalling mechanism is needed so that calls
// to GetFriends() will wait until FetchFriendslist() completes. Locks are insufficient.
FetchFriendslist(client);
} }
/// <summary>
/// Fetch the friends list or increment the refcount for the existing /// Fetch the friends list or increment the refcount for the existing
/// friends list /// friends list.
/// </summary>
/// <param name="client">
/// </param>
/// <returns>
/// Returns true if the list was fetched, false if it wasn't /// Returns true if the list was fetched, false if it wasn't
/// </returns>
protected virtual bool FetchFriendslist(IClientAPI client) protected virtual bool FetchFriendslist(IClientAPI client)
{ {
UUID agentID = client.AgentId; UUID agentID = client.AgentId;

View File

@ -480,7 +480,6 @@ namespace OpenSim.Region.CoreModules.World.Permissions
protected bool IsFriendWithPerms(UUID user,UUID objectOwner) protected bool IsFriendWithPerms(UUID user,UUID objectOwner)
{ {
if (user == UUID.Zero) if (user == UUID.Zero)
return false; return false;

View File

@ -73,8 +73,10 @@ namespace OpenSim.Region.Framework.Scenes
/// </summary> /// </summary>
public event OnNewClientDelegate OnNewClient; public event OnNewClientDelegate OnNewClient;
public delegate void OnClientLoginDelegate(IClientAPI client); /// <summary>
public event OnClientLoginDelegate OnClientLogin; /// Fired if the client entering this sim is doing so as a new login
/// </summary>
public event Action<IClientAPI> OnClientLogin;
public delegate void OnNewPresenceDelegate(ScenePresence presence); public delegate void OnNewPresenceDelegate(ScenePresence presence);
@ -651,10 +653,10 @@ namespace OpenSim.Region.Framework.Scenes
public void TriggerOnClientLogin(IClientAPI client) public void TriggerOnClientLogin(IClientAPI client)
{ {
OnClientLoginDelegate handlerClientLogin = OnClientLogin; Action<IClientAPI> handlerClientLogin = OnClientLogin;
if (handlerClientLogin != null) if (handlerClientLogin != null)
{ {
foreach (OnClientLoginDelegate d in handlerClientLogin.GetInvocationList()) foreach (Action<IClientAPI> d in handlerClientLogin.GetInvocationList())
{ {
try try
{ {