From 903fbd1f06b990141a90b539a2dbe77ab6be830e Mon Sep 17 00:00:00 2001
From: Melanie Thielker
Date: Thu, 18 Sep 2008 18:50:39 +0000
Subject: XEngine: fix collisions, add event coalescing for collision events.
 Fix a nasty concurrency issue that could cause a high event frequency to
 start more than one thread pool job for a single script.

---
 OpenSim/Region/Physics/OdePlugin/ODEPrim.cs        |  10 +-
 .../ScriptEngine/Shared/Instance/ScriptInstance.cs | 244 ++++++++++++---------
 .../Region/ScriptEngine/XEngine/EventManager.cs    |  25 ++-
 3 files changed, 154 insertions(+), 125 deletions(-)

diff --git a/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs b/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs
index 21e514b..79c4041 100644
--- a/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs
+++ b/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs
@@ -2294,14 +2294,12 @@ namespace OpenSim.Region.Physics.OdePlugin
             if (CollisionEventsThisFrame == null)
                 return;
 
-            //if (CollisionEventsThisFrame.m_objCollisionList == null)
-            //    return;
+            base.SendCollisionUpdate(CollisionEventsThisFrame);
 
-            if (CollisionEventsThisFrame.m_objCollisionList.Count > 0)
-            {
-                base.SendCollisionUpdate(CollisionEventsThisFrame);
+            if(CollisionEventsThisFrame.m_objCollisionList.Count == 0)
+                CollisionEventsThisFrame = null;
+            else
                 CollisionEventsThisFrame = new CollisionEventUpdate();
-            }
         }
 
         public override bool SubscribedEvents()
diff --git a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs
index 71bdd6e..506f0f5 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs
@@ -79,6 +79,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
         private bool m_ShuttingDown = false;
         private int m_ControlEventsInQueue = 0;
         private int m_LastControlLevel = 0;
+        private bool m_CollisionInQueue = false;
 
         private Dictionary<string,IScriptApi> m_Apis = new Dictionary<string,IScriptApi>();
 
@@ -495,6 +496,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
                     m_ControlEventsInQueue++;
                 }
 
+                if (data.EventName == "collision")
+                {
+                    if (m_CollisionInQueue)
+                        return;
+                    if (data.DetectParams == null)
+                        return;
+
+                    m_CollisionInQueue = true;
+                }
+
                 m_EventQueue.Enqueue(data);
 
                 if (m_CurrentResult == null)
@@ -510,146 +521,159 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
         /// <returns></returns>        
         public object EventProcessor()
         {
-            EventParams data = null;
-
-            lock (m_EventQueue)
+            lock(m_Script)
             {
-                data = (EventParams) m_EventQueue.Dequeue();
-                if (data == null) // Shouldn't happen
-                {
-                    m_CurrentResult = null;
-                    return 0;
-                }
-                if (data.EventName == "timer")
-                    m_TimerQueued = false;
-                if (data.EventName == "control")
+                EventParams data = null;
+
+                lock (m_EventQueue)
                 {
-                    if (m_ControlEventsInQueue > 0)
-                        m_ControlEventsInQueue--;
+                    data = (EventParams) m_EventQueue.Dequeue();
+                    if (data == null) // Shouldn't happen
+                    {
+                        if ((m_EventQueue.Count > 0) && m_RunEvents && (!m_ShuttingDown))
+                        {
+                            m_CurrentResult = m_Engine.QueueEventHandler(this);
+                        }
+                        else
+                        {
+                            m_CurrentResult = null;
+                        }
+                        return 0;
+                    }
+
+                    if (data.EventName == "timer")
+                        m_TimerQueued = false;
+                    if (data.EventName == "control")
+                    {
+                        if (m_ControlEventsInQueue > 0)
+                            m_ControlEventsInQueue--;
+                    }
+                    if (data.EventName == "collision")
+                        m_CollisionInQueue = false;
                 }
-            }
-            
-            //m_log.DebugFormat("[XENGINE]: Processing event {0} for {1}", data.EventName, this);
+                
+                //m_log.DebugFormat("[XENGINE]: Processing event {0} for {1}", data.EventName, this);
 
-            m_DetectParams = data.DetectParams;
+                m_DetectParams = data.DetectParams;
 
-            if (data.EventName == "state") // Hardcoded state change
-            {
-//                m_Engine.Log.DebugFormat("[Script] Script {0}.{1} state set to {2}",
-//                        m_PrimName, m_ScriptName, data.Params[0].ToString());
-                m_State=data.Params[0].ToString();
-                AsyncCommandManager async = (AsyncCommandManager)m_Engine.AsyncCommands;
-                async.RemoveScript(
-                    m_LocalID, m_ItemID);
-
-                SceneObjectPart part = m_Engine.World.GetSceneObjectPart(
-                    m_LocalID);
-                if (part != null)
+                if (data.EventName == "state") // Hardcoded state change
                 {
-                    part.SetScriptEvents(m_ItemID,
-                                         (int)m_Script.GetStateEventFlags(State));
+    //                m_Engine.Log.DebugFormat("[Script] Script {0}.{1} state set to {2}",
+    //                        m_PrimName, m_ScriptName, data.Params[0].ToString());
+                    m_State=data.Params[0].ToString();
+                    AsyncCommandManager async = (AsyncCommandManager)m_Engine.AsyncCommands;
+                    async.RemoveScript(
+                        m_LocalID, m_ItemID);
+
+                    SceneObjectPart part = m_Engine.World.GetSceneObjectPart(
+                        m_LocalID);
+                    if (part != null)
+                    {
+                        part.SetScriptEvents(m_ItemID,
+                                             (int)m_Script.GetStateEventFlags(State));
+                    }
                 }
-            }
-            else
-            {
-                SceneObjectPart part = m_Engine.World.GetSceneObjectPart(
-                    m_LocalID);
-//                m_Engine.Log.DebugFormat("[Script] Delivered event {2} in state {3} to {0}.{1}",
-//                        m_PrimName, m_ScriptName, data.EventName, m_State);
-
-                try
+                else
                 {
-                    m_CurrentEvent = data.EventName;
-                    m_EventStart = DateTime.Now;
-                    m_InEvent = true;
+                    SceneObjectPart part = m_Engine.World.GetSceneObjectPart(
+                        m_LocalID);
+    //                m_Engine.Log.DebugFormat("[Script] Delivered event {2} in state {3} to {0}.{1}",
+    //                        m_PrimName, m_ScriptName, data.EventName, m_State);
 
-                    m_Script.ExecuteEvent(State, data.EventName, data.Params);
+                    try
+                    {
+                        m_CurrentEvent = data.EventName;
+                        m_EventStart = DateTime.Now;
+                        m_InEvent = true;
 
-                    m_InEvent = false;
-                    m_CurrentEvent = String.Empty;
+                        m_Script.ExecuteEvent(State, data.EventName, data.Params);
 
-                    if (m_SaveState)
-                    {
-                        // This will be the very first event we deliver
-                        // (state_entry) in defualt state
-                        //
+                        m_InEvent = false;
+                        m_CurrentEvent = String.Empty;
 
-                        SaveState(m_Assembly);
+                        if (m_SaveState)
+                        {
+                            // This will be the very first event we deliver
+                            // (state_entry) in defualt state
+                            //
 
-                        m_SaveState = false;
-                    }
-                }
-                catch (Exception e)
-                {
-                    m_InEvent = false;
-                    m_CurrentEvent = String.Empty;
+                            SaveState(m_Assembly);
 
-                    if (!(e is TargetInvocationException) || (!(e.InnerException is EventAbortException) && (!(e.InnerException is SelfDeleteException))))
+                            m_SaveState = false;
+                        }
+                    }
+                    catch (Exception e)
                     {
-                        if (e is System.Threading.ThreadAbortException)
+                        m_InEvent = false;
+                        m_CurrentEvent = String.Empty;
+
+                        if (!(e is TargetInvocationException) || (!(e.InnerException is EventAbortException) && (!(e.InnerException is SelfDeleteException))))
                         {
-                            lock (m_EventQueue)
+                            if (e is System.Threading.ThreadAbortException)
                             {
-                                if ((m_EventQueue.Count > 0) && m_RunEvents && (!m_ShuttingDown))
+                                lock (m_EventQueue)
                                 {
-                                    m_CurrentResult=m_Engine.QueueEventHandler(this);
+                                    if ((m_EventQueue.Count > 0) && m_RunEvents && (!m_ShuttingDown))
+                                    {
+                                        m_CurrentResult=m_Engine.QueueEventHandler(this);
+                                    }
+                                    else
+                                    {
+                                        m_CurrentResult = null;
+                                    }
                                 }
-                                else
-                                {
-                                    m_CurrentResult = null;
-                                }
-                            }
 
-                            m_DetectParams = null;
+                                m_DetectParams = null;
 
-                            return 0;
-                        }
+                                return 0;
+                            }
 
-                        try
-                        {
-                            // DISPLAY ERROR INWORLD
-                            string text = "Runtime error:\n" + e.InnerException.ToString();
-                            if (text.Length > 1000)
-                                text = text.Substring(0, 1000);
-                            m_Engine.World.SimChat(Utils.StringToBytes(text),
-                                                   ChatTypeEnum.DebugChannel, 2147483647,
-                                                   part.AbsolutePosition,
-                                                   part.Name, part.UUID, false);
+                            try
+                            {
+                                // DISPLAY ERROR INWORLD
+                                string text = "Runtime error:\n" + e.InnerException.ToString();
+                                if (text.Length > 1000)
+                                    text = text.Substring(0, 1000);
+                                m_Engine.World.SimChat(Utils.StringToBytes(text),
+                                                       ChatTypeEnum.DebugChannel, 2147483647,
+                                                       part.AbsolutePosition,
+                                                       part.Name, part.UUID, false);
+                            }
+                            catch (Exception e2) // LEGIT: User Scripting
+                            {
+                                m_Engine.Log.Error("[Script]: "+
+                                                   "Error displaying error in-world: " +
+                                                   e2.ToString());
+                                m_Engine.Log.Error("[Script]: " +
+                                                   "Errormessage: Error compiling script:\r\n" +
+                                                   e.ToString());
+                            }
                         }
-                        catch (Exception e2) // LEGIT: User Scripting
+                        else if ((e is TargetInvocationException) && (e.InnerException is SelfDeleteException))
                         {
-                            m_Engine.Log.Error("[Script]: "+
-                                               "Error displaying error in-world: " +
-                                               e2.ToString());
-                            m_Engine.Log.Error("[Script]: " +
-                                               "Errormessage: Error compiling script:\r\n" +
-                                               e.ToString());
+                            m_InSelfDelete = true;
+                            if (part != null && part.ParentGroup != null)
+                                m_Engine.World.DeleteSceneObject(part.ParentGroup);
                         }
                     }
-                    else if ((e is TargetInvocationException) && (e.InnerException is SelfDeleteException))
-                    {
-                        m_InSelfDelete = true;
-                        if (part != null && part.ParentGroup != null)
-                            m_Engine.World.DeleteSceneObject(part.ParentGroup);
-                    }
                 }
-            }
 
-            lock (m_EventQueue)
-            {
-                if ((m_EventQueue.Count > 0) && m_RunEvents && (!m_ShuttingDown))
-                {
-                    m_CurrentResult = m_Engine.QueueEventHandler(this);
-                }
-                else
+                lock (m_EventQueue)
                 {
-                    m_CurrentResult = null;
+                    if ((m_EventQueue.Count > 0) && m_RunEvents && (!m_ShuttingDown))
+                    {
+                        m_CurrentResult = m_Engine.QueueEventHandler(this);
+                    }
+                    else
+                    {
+                        m_CurrentResult = null;
+                    }
                 }
-            }
 
-            m_DetectParams = null;
+                m_DetectParams = null;
 
-            return 0;
+                return 0;
+            }
         }
 
         public int EventTime()
@@ -730,6 +754,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
 
         public DetectParams GetDetectParams(int idx)
         {
+            if (m_DetectParams == null)
+                return null;
             if (idx < 0 || idx >= m_DetectParams.Length)
                 return null;
 
@@ -738,6 +764,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
 
         public UUID GetDetectID(int idx)
         {
+            if (m_DetectParams == null)
+                return UUID.Zero;
             if (idx < 0 || idx >= m_DetectParams.Length)
                 return UUID.Zero;
 
diff --git a/OpenSim/Region/ScriptEngine/XEngine/EventManager.cs b/OpenSim/Region/ScriptEngine/XEngine/EventManager.cs
index 22abd79..9ed2fbb 100644
--- a/OpenSim/Region/ScriptEngine/XEngine/EventManager.cs
+++ b/OpenSim/Region/ScriptEngine/XEngine/EventManager.cs
@@ -198,10 +198,11 @@ namespace OpenSim.Region.ScriptEngine.XEngine
                 det.Add(d);
             }
 
-            myScriptEngine.PostObjectEvent(localID, new EventParams(
-                    "collision_start",
-                    new Object[] { new LSL_Types.LSLInteger(1) },
-                    det.ToArray()));
+            if (det.Count > 0)
+                myScriptEngine.PostObjectEvent(localID, new EventParams(
+                        "collision_start",
+                        new Object[] { new LSL_Types.LSLInteger(det.Count) },
+                        det.ToArray()));
         }
 
         public void collision(uint localID, ColliderArgs col)
@@ -217,9 +218,10 @@ namespace OpenSim.Region.ScriptEngine.XEngine
                 det.Add(d);
             }
 
-            myScriptEngine.PostObjectEvent(localID, new EventParams(
-                    "collision", new Object[] { new LSL_Types.LSLInteger(1) },
-                    det.ToArray()));
+            if (det.Count > 0)
+                myScriptEngine.PostObjectEvent(localID, new EventParams(
+                        "collision", new Object[] { new LSL_Types.LSLInteger(det.Count) },
+                        det.ToArray()));
         }
 
         public void collision_end(uint localID, ColliderArgs col)
@@ -235,10 +237,11 @@ namespace OpenSim.Region.ScriptEngine.XEngine
                 det.Add(d);
             }
 
-            myScriptEngine.PostObjectEvent(localID, new EventParams(
-                    "collision_end",
-                    new Object[] { new LSL_Types.LSLInteger(1) },
-                    det.ToArray()));
+            if (det.Count > 0)
+                myScriptEngine.PostObjectEvent(localID, new EventParams(
+                        "collision_end",
+                        new Object[] { new LSL_Types.LSLInteger(det.Count) },
+                        det.ToArray()));
         }
 
         public void land_collision_start(uint localID, UUID itemID)
-- 
cgit v1.1