From a9e8bd59a377e7c7ce81e3693875467926dd7d4b Mon Sep 17 00:00:00 2001
From: Mic Bowman
Date: Mon, 13 Feb 2012 19:38:22 -0800
Subject: Fix a race condition in the simian groups connector. When requests
 were too slow they would circumvent the cache (piling up on the network
 service and making the problem even worse). This condition happens frequently
 during permission checks.

---
 .../SimianGroupsServicesConnectorModule.cs         | 71 +++++++++++++++++++---
 1 file changed, 63 insertions(+), 8 deletions(-)

diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs
index 42008da..130513d 100644
--- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs
+++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/SimianGroupsServicesConnectorModule.cs
@@ -30,6 +30,7 @@ using System.Collections;
 using System.Collections.Generic;
 using System.Collections.Specialized;
 using System.Reflection;
+using System.Threading;
 
 using Nwc.XmlRpc;
 
@@ -167,6 +168,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
 
         private bool m_debugEnabled = false;
 
+        private Dictionary<string, bool> m_pendingRequests = new Dictionary<string,bool>();
+        
         private ExpiringCache<string, OSDMap> m_memoryCache;
         private int m_cacheTimeout = 30;
 
@@ -1348,6 +1351,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
             // Immediately forward the request if the cache is disabled.
             if (m_cacheTimeout == 0)
             {
+                m_log.WarnFormat("[SIMIAN GROUPS CONNECTOR]: cache is disabled");
                 return WebUtil.PostToService(m_groupsServerURI, requestArgs);
             }
 
@@ -1355,6 +1359,8 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
             if (requestArgs["RequestMethod"] == "RemoveGeneric"
                 || requestArgs["RequestMethod"] == "AddGeneric")
             {
+                m_log.WarnFormat("[SIMIAN GROUPS CONNECTOR]: clearing generics cache");
+                
                 // Any and all updates cause the cache to clear
                 m_memoryCache.Clear();
 
@@ -1366,18 +1372,67 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
 
             // Create the cache key for the request and see if we have it cached
             string CacheKey = WebUtil.BuildQueryString(requestArgs);
-            OSDMap response = null;
-            if (!m_memoryCache.TryGetValue(CacheKey, out response))
+
+            // This code uses a leader/follower pattern. On a cache miss, the request is added
+            // to a queue; the first thread to add it to the queue completes the request while
+            // follow on threads busy wait for the results, this situation seems to happen
+            // often when checking permissions
+            while (true)
             {
-                // if it wasn't in the cache, pass the request to the Simian Grid Services
-                response = WebUtil.PostToService(m_groupsServerURI, requestArgs);
+                OSDMap response = null;
+                bool firstRequest = false;
 
-                // and cache the response
-                m_memoryCache.AddOrUpdate(CacheKey, response, TimeSpan.FromSeconds(m_cacheTimeout));
+                lock (m_memoryCache)
+                {
+                    if (m_memoryCache.TryGetValue(CacheKey, out response))
+                        return response;
+                    
+                    if (! m_pendingRequests.ContainsKey(CacheKey))
+                    {
+                        m_pendingRequests.Add(CacheKey,true);
+                        firstRequest = true;
+                    }
+                }
+                
+                if (firstRequest)
+                {
+                    // if it wasn't in the cache, pass the request to the Simian Grid Services
+                    try
+                    {
+                        response = WebUtil.PostToService(m_groupsServerURI, requestArgs);
+                    }
+                    catch (Exception e)
+                    {
+                        m_log.InfoFormat("[SIMIAN GROUPS CONNECTOR] request failed {0}",CacheKey);
+                    }
+                    
+                    // and cache the response
+                    lock (m_memoryCache)
+                    {
+                        m_memoryCache.AddOrUpdate(CacheKey, response, TimeSpan.FromSeconds(m_cacheTimeout));
+                        m_pendingRequests.Remove(CacheKey);
+                    }
+
+                    return response;
+                }
+
+                Thread.Sleep(50); // waiting for a web request to complete, 50msecs is reasonable
             }
 
-            // return cached response
-            return response;
+            // if (!m_memoryCache.TryGetValue(CacheKey, out response))
+            // {
+            //     m_log.WarnFormat("[SIMIAN GROUPS CONNECTOR]: query not in the cache");
+            //     Util.PrintCallStack();
+                
+            //     // if it wasn't in the cache, pass the request to the Simian Grid Services
+            //     response = WebUtil.PostToService(m_groupsServerURI, requestArgs);
+
+            //     // and cache the response
+            //     m_memoryCache.AddOrUpdate(CacheKey, response, TimeSpan.FromSeconds(m_cacheTimeout));
+            // }
+
+            // // return cached response
+            // return response;
         }
         #endregion
 
-- 
cgit v1.1


From 2ebb421331c4e6c4f57e0cc1bab79cea70937172 Mon Sep 17 00:00:00 2001
From: Dan Lake
Date: Tue, 14 Feb 2012 17:20:34 -0800
Subject: Refactor appearance saving for NPC to use AvatarFactoryModule
 interface.

---
 .../Avatar/AvatarFactory/AvatarFactoryModule.cs    | 40 +++++++++++++++++-----
 .../Framework/Interfaces/IAvatarFactoryModule.cs   |  1 +
 .../Region/OptionalModules/World/NPC/NPCModule.cs  | 12 +++----
 3 files changed, 38 insertions(+), 15 deletions(-)

diff --git a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs
index 8d503bd..c7f4c20 100644
--- a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs
@@ -111,6 +111,15 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
 
         #region IAvatarFactoryModule
 
+        /// </summary>
+        /// <param name="sp"></param>
+        /// <param name="texture"></param>
+        /// <param name="visualParam"></param>
+        public void SetAppearance(IScenePresence sp, AvatarAppearance appearance)
+        {
+            SetAppearance(sp, appearance.Texture, appearance.VisualParams);
+        }
+
         /// <summary>
         /// Set appearance data (texture asset IDs and slider settings) 
         /// </summary>
@@ -156,14 +165,23 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
                     changed = sp.Appearance.SetTextureEntries(textureEntry) || changed;
 
 //                    WriteBakedTexturesReport(sp, m_log.DebugFormat);
-                    if (!ValidateBakedTextureCache(sp))
+
+                    // If bake textures are missing and this is not an NPC, request a rebake from client
+                    if (!ValidateBakedTextureCache(sp) && (((ScenePresence)sp).PresenceType != PresenceType.Npc))
                         RequestRebake(sp, true);
 
                     // This appears to be set only in the final stage of the appearance
                     // update transaction. In theory, we should be able to do an immediate
                     // appearance send and save here.
                 }
-                
+
+                // NPC should send to clients immediately and skip saving appearance
+                if (((ScenePresence)sp).PresenceType == PresenceType.Npc)
+                {
+                    SendAppearance((ScenePresence)sp);
+                    return;
+                }
+
                 // save only if there were changes, send no matter what (doesn't hurt to send twice)
                 if (changed)
                     QueueAppearanceSave(sp.ControllingClient.AgentId);
@@ -174,6 +192,15 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
             // m_log.WarnFormat("[AVFACTORY]: complete SetAppearance for {0}:\n{1}",client.AgentId,sp.Appearance.ToString());
         }
 
+        private void SendAppearance(ScenePresence sp)
+        {
+            // Send the appearance to everyone in the scene
+            sp.SendAppearanceToAllOtherAgents();
+
+            // Send animations back to the avatar as well
+            sp.Animator.SendAnimPack();
+        }
+
         public bool SendAppearance(UUID agentId)
         {
 //            m_log.DebugFormat("[AVFACTORY]: Sending appearance for {0}", agentId);
@@ -185,12 +212,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
                 return false;
             }
 
-            // Send the appearance to everyone in the scene
-            sp.SendAppearanceToAllOtherAgents();
-
-            // Send animations back to the avatar as well
-            sp.Animator.SendAnimPack();
-
+            SendAppearance(sp);
             return true;
         }
 
@@ -626,4 +648,4 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
             outputAction("{0} baked appearance texture is {1}", sp.Name, bakedTextureValid ? "OK" : "corrupt");
         }
     }
-}
\ No newline at end of file
+}
diff --git a/OpenSim/Region/Framework/Interfaces/IAvatarFactoryModule.cs b/OpenSim/Region/Framework/Interfaces/IAvatarFactoryModule.cs
index 39a760c..34aca33 100644
--- a/OpenSim/Region/Framework/Interfaces/IAvatarFactoryModule.cs
+++ b/OpenSim/Region/Framework/Interfaces/IAvatarFactoryModule.cs
@@ -35,6 +35,7 @@ namespace OpenSim.Region.Framework.Interfaces
 
     public interface IAvatarFactoryModule
     {
+        void SetAppearance(IScenePresence sp, AvatarAppearance appearance);
         void SetAppearance(IScenePresence sp, Primitive.TextureEntry textureEntry, byte[] visualParams);
 
         /// <summary>
diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs
index dc6eefc..5359354 100644
--- a/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs
+++ b/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs
@@ -96,15 +96,15 @@ namespace OpenSim.Region.OptionalModules.World.NPC
                 if (!m_avatars.ContainsKey(agentId))
                     return false;
 
+            // Delete existing sp attachments
             scene.AttachmentsModule.DeleteAttachmentsFromScene(sp, false);
 
-            AvatarAppearance npcAppearance = new AvatarAppearance(appearance, true);
-            sp.Appearance = npcAppearance;
+            // Set new sp appearance. Also sends to clients.
+            scene.RequestModuleInterface<IAvatarFactoryModule>().SetAppearance(sp, new AvatarAppearance(appearance, true));
+            
+            // Rez needed sp attachments
             scene.AttachmentsModule.RezAttachments(sp);
-
-            IAvatarFactoryModule module = scene.RequestModuleInterface<IAvatarFactoryModule>();
-            module.SendAppearance(sp.UUID);
-
+            
             return true;
         }
 
-- 
cgit v1.1