From a43bb10000ca0b58af2f33750ea17cb94bfbad7d Mon Sep 17 00:00:00 2001 From: Tedd Hansen Date: Fri, 22 Feb 2008 14:09:38 +0000 Subject: Execution threads are now shared between regions too. Default thread count regardless of number of regions is now 3. This will save you around 33 threads for a normal 3x3 region server. But, this is totally completely untested. So it probably won't work for another patch or five. --- .../Common/ScriptEngineBase/EventQueueManager.cs | 64 +--- .../ScriptEngineBase/EventQueueThreadClass.cs | 402 +++++++++++---------- .../Common/ScriptEngineBase/MaintenanceThread.cs | 5 +- 3 files changed, 229 insertions(+), 242 deletions(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs index e3d448c..581c7a0 100644 --- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs +++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs @@ -45,7 +45,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase public class EventQueueManager : iScriptEngineFunctionModule { // - // Class is instanced in "ScriptEngine" and used by "EventManager" also instanced in "ScriptEngine". + // Class is instanced in "ScriptEngine" and used by "EventManager" which is also instanced in "ScriptEngine". // // Class purpose is to queue and execute functions that are received by "EventManager": // - allowing "EventManager" to release its event thread immediately, thus not interrupting server execution. @@ -68,25 +68,25 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase /// List of threads (classes) processing event queue /// Note that this may or may not be a reference to a static object depending on PrivateRegionThreads config setting. /// - internal List eventQueueThreads; // Thread pool that we work on + internal static List eventQueueThreads; // Thread pool that we work on /// /// Locking access to eventQueueThreads AND staticGlobalEventQueueThreads. /// // private object eventQueueThreadsLock = new object(); // Static objects for referencing the objects above if we don't have private threads: - internal static List staticEventQueueThreads; // A static reference used if we don't use private threads + //internal static List staticEventQueueThreads; // A static reference used if we don't use private threads // internal static object staticEventQueueThreadsLock; // Statick lock object reference for same reason /// /// Global static list of all threads (classes) processing event queue -- used by max enforcment thread /// - private List staticGlobalEventQueueThreads = new List(); + //private List staticGlobalEventQueueThreads = new List(); /// /// Used internally to specify how many threads should exit gracefully /// - public int ThreadsToExit; - public object ThreadsToExitLock = new object(); + public static int ThreadsToExit; + public static object ThreadsToExitLock = new object(); //public object queueLock = new object(); // Mutex lock object @@ -94,14 +94,14 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase /// /// How many threads to process queue with /// - internal int numberOfThreads; + internal static int numberOfThreads; internal static int EventExecutionMaxQueueSize; /// /// Maximum time one function can use for execution before we perform a thread kill. /// - private int maxFunctionExecutionTimems + private static int maxFunctionExecutionTimems { get { return (int)(maxFunctionExecutionTimens / 10000); } set { maxFunctionExecutionTimens = value * 10000; } @@ -111,15 +111,15 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase /// Contains nanoseconds version of maxFunctionExecutionTimems so that it matches time calculations better (performance reasons). /// WARNING! ONLY UPDATE maxFunctionExecutionTimems, NEVER THIS DIRECTLY. /// - public long maxFunctionExecutionTimens; + public static long maxFunctionExecutionTimens; /// /// Enforce max execution time /// - public bool EnforceMaxExecutionTime; + public static bool EnforceMaxExecutionTime; /// /// Kill script (unload) when it exceeds execution time /// - private bool KillScriptOnMaxFunctionExecutionTime; + private static bool KillScriptOnMaxFunctionExecutionTime; /// /// List of localID locks for mutex processing of script events @@ -172,33 +172,8 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase { m_ScriptEngine = _ScriptEngine; - // TODO: We need to move from single EventQueueManager to list of it in to share threads - bool PrivateRegionThreads = true; // m_ScriptEngine.ScriptConfigSource.GetBoolean("PrivateRegionThreads", false); - - // Create thread pool list and lock object - // Determine from config if threads should be dedicated to regions or shared - if (PrivateRegionThreads) - { - // PRIVATE THREAD POOL PER REGION - eventQueueThreads = new List(); - // eventQueueThreadsLock = new object(); - } - else - { - // SHARED THREAD POOL - // Crate the static objects - if (staticEventQueueThreads == null) - staticEventQueueThreads = new List(); - // if (staticEventQueueThreadsLock == null) - // staticEventQueueThreadsLock = new object(); - - // Now reference our locals to them - eventQueueThreads = staticEventQueueThreads; - //eventQueueThreadsLock = staticEventQueueThreadsLock; - } - + eventQueueThreads = new List(); ReadConfig(); - } public void ReadConfig() @@ -230,18 +205,18 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase private void Stop() { - if (eventQueueThreads != null && eventQueueThreads != null) + if (eventQueueThreads != null) { // Kill worker threads - //lock (eventQueueThreads) - //{ + lock (eventQueueThreads) + { foreach (EventQueueThreadClass EventQueueThread in new ArrayList(eventQueueThreads)) { AbortThreadClass(EventQueueThread); } //eventQueueThreads.Clear(); //staticGlobalEventQueueThreads.Clear(); - //} + } } // Remove all entries from our event queue @@ -256,9 +231,8 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase #region " Start / stop script execution threads (ThreadClasses) " private void StartNewThreadClass() { - EventQueueThreadClass eqtc = new EventQueueThreadClass(this); + EventQueueThreadClass eqtc = new EventQueueThreadClass(); eventQueueThreads.Add(eqtc); - staticGlobalEventQueueThreads.Add(eqtc); m_ScriptEngine.Log.Debug("[" + m_ScriptEngine.ScriptEngineName + "]: Started new script execution thread. Current thread count: " + eventQueueThreads.Count); } @@ -266,8 +240,6 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase { if (eventQueueThreads.Contains(threadClass)) eventQueueThreads.Remove(threadClass); - if (staticGlobalEventQueueThreads.Contains(threadClass)) - staticGlobalEventQueueThreads.Remove(threadClass); try { @@ -426,7 +398,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase // Iterate through all ScriptThreadClasses and check how long their current function has been executing lock (eventQueueThreads) { - foreach (EventQueueThreadClass EventQueueThread in staticGlobalEventQueueThreads) + foreach (EventQueueThreadClass EventQueueThread in eventQueueThreads) { // Is thread currently executing anything? if (EventQueueThread.InExecution) diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueThreadClass.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueThreadClass.cs index 11fd896..f36baa7 100644 --- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueThreadClass.cs +++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueThreadClass.cs @@ -40,27 +40,27 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase /// /// Because every thread needs some data set for it (time started to execute current function), it will do its work within a class /// - public class EventQueueThreadClass: iScriptEngineFunctionModule + public class EventQueueThreadClass : iScriptEngineFunctionModule { /// /// How many ms to sleep if queue is empty /// - private int nothingToDoSleepms;// = 50; - private ThreadPriority MyThreadPriority; + private static int nothingToDoSleepms;// = 50; + private static ThreadPriority MyThreadPriority; public long LastExecutionStarted; public bool InExecution = false; public bool KillCurrentScript = false; - private EventQueueManager eventQueueManager; + //private EventQueueManager eventQueueManager; public Thread EventQueueThread; private static int ThreadCount = 0; private string ScriptEngineName = "ScriptEngine.Common"; - public EventQueueThreadClass(EventQueueManager eqm) + public EventQueueThreadClass()//EventQueueManager eqm { - eventQueueManager = eqm; + //eventQueueManager = eqm; ReadConfig(); Start(); } @@ -72,32 +72,36 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase public void ReadConfig() { - ScriptEngineName = eventQueueManager.m_ScriptEngine.ScriptEngineName; - nothingToDoSleepms = eventQueueManager.m_ScriptEngine.ScriptConfigSource.GetInt("SleepTimeIfNoScriptExecutionMs", 50); - - // Later with ScriptServer we might want to ask OS for stuff too, so doing this a bit manually - string pri = eventQueueManager.m_ScriptEngine.ScriptConfigSource.GetString("ScriptThreadPriority", "BelowNormal"); - switch (pri.ToLower()) + foreach (ScriptEngine m_ScriptEngine in ScriptEngine.ScriptEngines) { - case "lowest": - MyThreadPriority = ThreadPriority.Lowest; - break; - case "belownormal": - MyThreadPriority = ThreadPriority.BelowNormal; - break; - case "normal": - MyThreadPriority = ThreadPriority.Normal; - break; - case "abovenormal": - MyThreadPriority = ThreadPriority.AboveNormal; - break; - case "highest": - MyThreadPriority = ThreadPriority.Highest; - break; - default: - MyThreadPriority = ThreadPriority.BelowNormal; // Default - eventQueueManager.m_ScriptEngine.Log.Error("[ScriptEngineBase]: Unknown priority type \"" + pri + "\" in config file. Defaulting to \"BelowNormal\"."); - break; + ScriptEngineName = m_ScriptEngine.ScriptEngineName; + nothingToDoSleepms = m_ScriptEngine.ScriptConfigSource.GetInt("SleepTimeIfNoScriptExecutionMs", 50); + + // Later with ScriptServer we might want to ask OS for stuff too, so doing this a bit manually + string pri = m_ScriptEngine.ScriptConfigSource.GetString("ScriptThreadPriority", "BelowNormal"); + switch (pri.ToLower()) + { + case "lowest": + MyThreadPriority = ThreadPriority.Lowest; + break; + case "belownormal": + MyThreadPriority = ThreadPriority.BelowNormal; + break; + case "normal": + MyThreadPriority = ThreadPriority.Normal; + break; + case "abovenormal": + MyThreadPriority = ThreadPriority.AboveNormal; + break; + case "highest": + MyThreadPriority = ThreadPriority.Highest; + break; + default: + MyThreadPriority = ThreadPriority.BelowNormal; // Default + m_ScriptEngine.Log.Error("[ScriptEngineBase]: Unknown priority type \"" + pri + + "\" in config file. Defaulting to \"BelowNormal\"."); + break; + } } // Now set that priority @@ -113,7 +117,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase { EventQueueThread = new Thread(EventQueueThreadLoop); EventQueueThread.IsBackground = true; - + EventQueueThread.Priority = MyThreadPriority; EventQueueThread.Name = "EventQueueManagerThread_" + ThreadCount; EventQueueThread.Start(); @@ -127,8 +131,8 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase public void Stop() { - PleaseShutdown = true; // Set shutdown flag - Thread.Sleep(100); // Wait a bit + //PleaseShutdown = true; // Set shutdown flag + //Thread.Sleep(100); // Wait a bit if (EventQueueThread != null && EventQueueThread.IsAlive == true) { try @@ -143,6 +147,8 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase } } + private EventQueueManager.QueueItemStruct BlankQIS = new EventQueueManager.QueueItemStruct(); + private ScriptEngine lastScriptEngine; /// /// Queue processing thread loop /// @@ -151,188 +157,198 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase //myScriptEngine.Log.Info("[" + ScriptEngineName + "]: EventQueueManager Worker thread spawned"); try { - while (true) + while (true) { try { - EventQueueManager.QueueItemStruct BlankQIS = new EventQueueManager.QueueItemStruct(); while (true) { - // Every now and then check if we should shut down - if (PleaseShutdown || eventQueueManager.ThreadsToExit > 0) - { - // Someone should shut down, lets get exclusive lock - lock (eventQueueManager.ThreadsToExitLock) - { - // Lets re-check in case someone grabbed it - if (eventQueueManager.ThreadsToExit > 0) - { - // Its crowded here so we'll shut down - eventQueueManager.ThreadsToExit--; - Stop(); - return; - } - else - { - // We have been asked to shut down - Stop(); - return; - } - } - } + DoProcessQueue(); + } + } + catch (ThreadAbortException tae) + { + if (lastScriptEngine != null) + lastScriptEngine.Log.Info("[" + ScriptEngineName + "]: ThreadAbortException while executing function."); + } + catch (Exception e) + { + if (lastScriptEngine != null) + lastScriptEngine.Log.Error("[" + ScriptEngineName + "]: Exception in EventQueueThreadLoop: " + e.ToString()); + } + } + } + catch (ThreadAbortException) + { + //myScriptEngine.Log.Info("[" + ScriptEngineName + "]: EventQueueManager Worker thread killed: " + tae.Message); + } + } + + public void DoProcessQueue() + { + foreach (ScriptEngine m_ScriptEngine in ScriptEngine.ScriptEngines) + { + lastScriptEngine = m_ScriptEngine; + // Every now and then check if we should shut down + //if (PleaseShutdown || EventQueueManager.ThreadsToExit > 0) + //{ + // // Someone should shut down, lets get exclusive lock + // lock (EventQueueManager.ThreadsToExitLock) + // { + // // Lets re-check in case someone grabbed it + // if (EventQueueManager.ThreadsToExit > 0) + // { + // // Its crowded here so we'll shut down + // EventQueueManager.ThreadsToExit--; + // Stop(); + // return; + // } + // else + // { + // // We have been asked to shut down + // Stop(); + // return; + // } + // } + //} + + //try + // { + EventQueueManager.QueueItemStruct QIS = BlankQIS; + bool GotItem = false; - //try - // { - EventQueueManager.QueueItemStruct QIS = BlankQIS; - bool GotItem = false; + //if (PleaseShutdown) + // return; - if (PleaseShutdown) - return; + if (m_ScriptEngine.m_EventQueueManager.eventQueue.Count == 0) + { + // Nothing to do? Sleep a bit waiting for something to do + Thread.Sleep(nothingToDoSleepms); + } + else + { + // Something in queue, process + //myScriptEngine.Log.Info("[" + ScriptEngineName + "]: Processing event for localID: " + QIS.localID + ", itemID: " + QIS.itemID + ", FunctionName: " + QIS.FunctionName); - if (eventQueueManager.eventQueue.Count == 0) + // OBJECT BASED LOCK - TWO THREADS WORKING ON SAME OBJECT IS NOT GOOD + lock (m_ScriptEngine.m_EventQueueManager.eventQueue) + { + GotItem = false; + for (int qc = 0; qc < m_ScriptEngine.m_EventQueueManager.eventQueue.Count; qc++) + { + // Get queue item + QIS = m_ScriptEngine.m_EventQueueManager.eventQueue.Dequeue(); + + // Check if object is being processed by someone else + if (m_ScriptEngine.m_EventQueueManager.TryLock(QIS.localID) == false) { - // Nothing to do? Sleep a bit waiting for something to do - Thread.Sleep(nothingToDoSleepms); + // Object is already being processed, requeue it + m_ScriptEngine.m_EventQueueManager.eventQueue.Enqueue(QIS); } else { - // Something in queue, process - //myScriptEngine.Log.Info("[" + ScriptEngineName + "]: Processing event for localID: " + QIS.localID + ", itemID: " + QIS.itemID + ", FunctionName: " + QIS.FunctionName); - - // OBJECT BASED LOCK - TWO THREADS WORKING ON SAME OBJECT IS NOT GOOD - lock (eventQueueManager.eventQueue) - { - GotItem = false; - for (int qc = 0; qc < eventQueueManager.eventQueue.Count; qc++) - { - // Get queue item - QIS = eventQueueManager.eventQueue.Dequeue(); - - // Check if object is being processed by someone else - if (eventQueueManager.TryLock(QIS.localID) == false) - { - // Object is already being processed, requeue it - eventQueueManager.eventQueue.Enqueue(QIS); - } - else - { - // We have lock on an object and can process it - GotItem = true; - break; - } - } - } + // We have lock on an object and can process it + GotItem = true; + break; + } + } + } - if (GotItem == true) - { - // Execute function - try - { -///cfk 2-7-08 dont need this right now and the default Linux build has DEBUG defined + if (GotItem == true) + { + // Execute function + try + { + ///cfk 2-7-08 dont need this right now and the default Linux build has DEBUG defined #if DEBUG - //eventQueueManager.m_ScriptEngine.Log.Debug("[" + ScriptEngineName + "]: " + - // "Executing event:\r\n" - // + "QIS.localID: " + QIS.localID - // + ", QIS.itemID: " + QIS.itemID - // + ", QIS.functionName: " + - // QIS.functionName); + //eventQueueManager.m_ScriptEngine.Log.Debug("[" + ScriptEngineName + "]: " + + // "Executing event:\r\n" + // + "QIS.localID: " + QIS.localID + // + ", QIS.itemID: " + QIS.itemID + // + ", QIS.functionName: " + + // QIS.functionName); #endif - LastExecutionStarted = DateTime.Now.Ticks; - KillCurrentScript = false; - InExecution = true; - eventQueueManager.m_ScriptEngine.m_ScriptManager.ExecuteEvent(QIS.localID, - QIS.itemID, - QIS.functionName, - QIS.llDetectParams, - QIS.param); - InExecution = false; - } - catch (Exception e) - { - InExecution = false; - // DISPLAY ERROR INWORLD - string text = "Error executing script function \"" + QIS.functionName + - "\":\r\n"; - if (e.InnerException != null) - { - // Send inner exception - text += e.InnerException.Message.ToString(); - } - else - { - text += "\r\n"; - // Send normal - text += e.Message.ToString(); - } - if (KillCurrentScript) - text += "\r\nScript will be deactivated!"; + LastExecutionStarted = DateTime.Now.Ticks; + KillCurrentScript = false; + InExecution = true; + m_ScriptEngine.m_ScriptManager.ExecuteEvent(QIS.localID, + QIS.itemID, + QIS.functionName, + QIS.llDetectParams, + QIS.param); + InExecution = false; + } + catch (Exception e) + { + InExecution = false; + // DISPLAY ERROR INWORLD + string text = "Error executing script function \"" + QIS.functionName + + "\":\r\n"; + if (e.InnerException != null) + { + // Send inner exception + text += e.InnerException.Message.ToString(); + } + else + { + text += "\r\n"; + // Send normal + text += e.Message.ToString(); + } + if (KillCurrentScript) + text += "\r\nScript will be deactivated!"; - try - { - if (text.Length > 1500) - text = text.Substring(0, 1500); - IScriptHost m_host = - eventQueueManager.m_ScriptEngine.World.GetSceneObjectPart(QIS.localID); - //if (m_host != null) - //{ - eventQueueManager.m_ScriptEngine.World.SimChat(Helpers.StringToField(text), - ChatTypeEnum.Say, 0, - m_host.AbsolutePosition, - m_host.Name, m_host.UUID); - } - catch - { - //} - //else - //{ - // T oconsole - eventQueueManager.m_ScriptEngine.Log.Error("[" + ScriptEngineName + "]: " + - "Unable to send text in-world:\r\n" + - text); - } - finally - { - // So we are done sending message in-world - if (KillCurrentScript) - { - eventQueueManager.m_ScriptEngine.m_ScriptManager.StopScript( - QIS.localID, QIS.itemID); - } - } - } - finally - { - InExecution = false; - eventQueueManager.ReleaseLock(QIS.localID); - } + try + { + if (text.Length > 1500) + text = text.Substring(0, 1500); + IScriptHost m_host = + m_ScriptEngine.World.GetSceneObjectPart(QIS.localID); + //if (m_host != null) + //{ + m_ScriptEngine.World.SimChat(Helpers.StringToField(text), + ChatTypeEnum.Say, 0, + m_host.AbsolutePosition, + m_host.Name, m_host.UUID); + } + catch + { + //} + //else + //{ + // T oconsole + m_ScriptEngine.m_EventQueueManager.m_ScriptEngine.Log.Error("[" + ScriptEngineName + "]: " + + "Unable to send text in-world:\r\n" + + text); + } + finally + { + // So we are done sending message in-world + if (KillCurrentScript) + { + m_ScriptEngine.m_EventQueueManager.m_ScriptEngine.m_ScriptManager.StopScript( + QIS.localID, QIS.itemID); } } } - } - catch (ThreadAbortException tae) - { - eventQueueManager.m_ScriptEngine.Log.Info("[" + ScriptEngineName + "]: ThreadAbortException while executing function."); - } - catch (Exception e) - { - eventQueueManager.m_ScriptEngine.Log.Error("[" + ScriptEngineName + "]: Exception in EventQueueThreadLoop: " + e.ToString()); + finally + { + InExecution = false; + m_ScriptEngine.m_EventQueueManager.ReleaseLock(QIS.localID); + } } } } - catch (ThreadAbortException) - { - //myScriptEngine.Log.Info("[" + ScriptEngineName + "]: EventQueueManager Worker thread killed: " + tae.Message); - } } - /// - /// If set to true then threads and stuff should try to make a graceful exit - /// - public bool PleaseShutdown - { - get { return _PleaseShutdown; } - set { _PleaseShutdown = value; } - } - private bool _PleaseShutdown = false; + ///// + ///// If set to true then threads and stuff should try to make a graceful exit + ///// + //public bool PleaseShutdown + //{ + // get { return _PleaseShutdown; } + // set { _PleaseShutdown = value; } + //} + //private bool _PleaseShutdown = false; } } diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/MaintenanceThread.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/MaintenanceThread.cs index 5c9f76d..0911afb 100644 --- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/MaintenanceThread.cs +++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/MaintenanceThread.cs @@ -156,12 +156,11 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase m_ScriptEngine.m_EventQueueManager.AdjustNumberOfScriptThreads(); // Check if any script has exceeded its max execution time - if (m_ScriptEngine.m_EventQueueManager != null && - m_ScriptEngine.m_EventQueueManager.EnforceMaxExecutionTime) + if (EventQueueManager.EnforceMaxExecutionTime) { // We are enforcing execution time if (DateTime.Now.Ticks - Last_maxFunctionExecutionTimens > - m_ScriptEngine.m_EventQueueManager.maxFunctionExecutionTimens) + EventQueueManager.maxFunctionExecutionTimens) { // Its time to check again m_ScriptEngine.m_EventQueueManager.CheckScriptMaxExecTime(); // Do check -- cgit v1.1