From 7770c65a7e888739dfb0c8a9ccf2dc521e99a33a Mon Sep 17 00:00:00 2001 From: Tedd Hansen Date: Sun, 19 Aug 2007 11:09:54 +0000 Subject: [PATCH] Sped up EventQueueManager response time (scripts now respond quickly). Added support for multiple threads executing events on objects, but only one thread on one script at the time (to utilize MultiCore/hyperthreading CPU's). --- .../DotNetEngine/EventQueueManager.cs | 152 +++++++++++++----- 1 file changed, 115 insertions(+), 37 deletions(-) diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs index 311d32b249..ec34bb077c 100644 --- a/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs +++ b/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs @@ -42,8 +42,10 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine [Serializable] class EventQueueManager { - private Thread EventQueueThread; - private int NothingToDoSleepms = 200; + private List EventQueueThreads = new List(); + private object QueueLock = new object(); + private int NothingToDoSleepms = 50; + private int NumberOfThreads = 2; private Queue EventQueue = new Queue(); private struct QueueItemStruct { @@ -59,26 +61,36 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine myScriptEngine = _ScriptEngine; //myScriptEngine.m_logger.Verbose("ScriptEngine", "EventQueueManager Start"); // Start worker thread - EventQueueThread = new Thread(EventQueueThreadLoop); - EventQueueThread.IsBackground = true; - EventQueueThread.Name = "EventQueueManagerThread"; - EventQueueThread.Start(); + + for (int ThreadCount = 0; ThreadCount <= NumberOfThreads; ThreadCount++) + { + Thread EventQueueThread = new Thread(EventQueueThreadLoop); + EventQueueThreads.Add(EventQueueThread); + EventQueueThread.IsBackground = true; + EventQueueThread.Name = "EventQueueManagerThread_" + ThreadCount; + EventQueueThread.Start(); + } } ~EventQueueManager() { - // Kill worker thread - if (EventQueueThread != null && EventQueueThread.IsAlive == true) + + // Kill worker threads + foreach (Thread EventQueueThread in new System.Collections.ArrayList(EventQueueThreads)) { - try + if (EventQueueThread != null && EventQueueThread.IsAlive == true) { - EventQueueThread.Abort(); - EventQueueThread.Join(); - } - catch (Exception e) - { - myScriptEngine.Log.Verbose("ScriptEngine", "EventQueueManager Exception killing worker thread: " + e.ToString()); + try + { + EventQueueThread.Abort(); + EventQueueThread.Join(); + } + catch (Exception e) + { + myScriptEngine.Log.Verbose("ScriptEngine", "EventQueueManager Exception killing worker thread: " + e.ToString()); + } } } + EventQueueThreads.Clear(); // Todo: Clean up our queues } @@ -88,8 +100,12 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine //myScriptEngine.m_logger.Verbose("ScriptEngine", "EventQueueManager Worker thread spawned"); try { + QueueItemStruct BlankQIS = new QueueItemStruct(); while (true) { + QueueItemStruct QIS = BlankQIS; + bool GotItem = false; + if (EventQueue.Count == 0) { // Nothing to do? Sleep a bit waiting for something to do @@ -98,45 +114,107 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine else { // Something in queue, process - QueueItemStruct QIS = EventQueue.Dequeue(); //myScriptEngine.m_logger.Verbose("ScriptEngine", "Processing event for ObjectID: " + QIS.ObjectID + ", ScriptID: " + QIS.ScriptID + ", FunctionName: " + QIS.FunctionName); - // TODO: Execute function - myScriptEngine.myScriptManager.ExecuteEvent(QIS.ObjectID, QIS.ScriptID, QIS.FunctionName, QIS.param); - } - } - } + + // OBJECT BASED LOCK - TWO THREADS WORKING ON SAME OBJECT IS NOT GOOD + lock (QueueLock) + { + GotItem = false; + for (int qc = 0; qc < EventQueue.Count; qc++) + { + // Get queue item + QIS = EventQueue.Dequeue(); + + // Check if object is being processed by someone else + if (TryLock(QIS.ObjectID) == false) + { + // Object is already being processed, requeue it + EventQueue.Enqueue(QIS); + } + else + { + // We have lock on an object and can process it + GotItem = true; + break; + } + } // go through queue + } // lock + + if (GotItem == true) + { + // Execute function + myScriptEngine.myScriptManager.ExecuteEvent(QIS.ObjectID, QIS.ScriptID, QIS.FunctionName, QIS.param); + ReleaseLock(QIS.ObjectID); + } + + } // Something in queue + } // while + } // try catch (ThreadAbortException tae) { myScriptEngine.Log.Verbose("ScriptEngine", "EventQueueManager Worker thread killed: " + tae.Message); } } + private List ObjectLocks = new List(); + private object TryLockLock = new object(); + private bool TryLock(IScriptHost ObjectID) + { + lock (TryLockLock) + { + if (ObjectLocks.Contains(ObjectID) == true) + { + return false; + } + else + { + ObjectLocks.Add(ObjectID); + return true; + } + } + } + + private void ReleaseLock(IScriptHost ObjectID) + { + lock (TryLockLock) + { + if (ObjectLocks.Contains(ObjectID) == true) + { + ObjectLocks.Remove(ObjectID); + } + } + } + public void AddToObjectQueue(IScriptHost ObjectID, string FunctionName, object[] param) { // Determine all scripts in Object and add to their queue //myScriptEngine.m_logger.Verbose("ScriptEngine", "EventQueueManager Adding ObjectID: " + ObjectID + ", FunctionName: " + FunctionName); - foreach (string ScriptID in myScriptEngine.myScriptManager.GetScriptKeys(ObjectID)) + lock (QueueLock) { - // Add to each script in that object - // TODO: Some scripts may not subscribe to this event. Should we NOT add it? Does it matter? - // Create a structure and add data - QueueItemStruct QIS = new QueueItemStruct(); - QIS.ObjectID = ObjectID; - QIS.ScriptID = ScriptID; - QIS.FunctionName = FunctionName; - QIS.param = param; + foreach (string ScriptID in myScriptEngine.myScriptManager.GetScriptKeys(ObjectID)) + { + // Add to each script in that object + // TODO: Some scripts may not subscribe to this event. Should we NOT add it? Does it matter? - // Add it to queue - EventQueue.Enqueue(QIS); - + // Create a structure and add data + QueueItemStruct QIS = new QueueItemStruct(); + QIS.ObjectID = ObjectID; + QIS.ScriptID = ScriptID; + QIS.FunctionName = FunctionName; + QIS.param = param; + + // Add it to queue + EventQueue.Enqueue(QIS); + + } } + //public void AddToScriptQueue(string ObjectID, string FunctionName, object[] param) + //{ + // // Add to script queue + //} } - //public void AddToScriptQueue(string ObjectID, string FunctionName, object[] param) - //{ - // // Add to script queue - //} } }