diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs index 70db7245aa..730b63e1a3 100644 --- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs +++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs @@ -69,21 +69,25 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase /// /// List of threads processing event queue /// - private List eventQueueThreads = new List(); - private object eventQueueThreadsLock = new object(); + private List eventQueueThreads;// = new List(); + private object eventQueueThreadsLock;// = new object(); + + private static List staticEventQueueThreads;// = new List(); + private static object staticEventQueueThreadsLock;// = new object(); public object queueLock = new object(); // Mutex lock object /// /// How many threads to process queue with /// - private int numberOfThreads = 2; + private int numberOfThreads; /// /// Maximum time one function can use for execution before we perform a thread kill /// - private int maxFunctionExecutionTimems = 50; - private bool EnforceMaxExecutionTime = true; + private int maxFunctionExecutionTimems; + private bool EnforceMaxExecutionTime; + /// /// Queue containing events waiting to be executed @@ -138,6 +142,36 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase { m_ScriptEngine = _ScriptEngine; + + // Create thread pool list and lock object + // Determine from config if threads should be dedicated to regions or shared + if (m_ScriptEngine.ScriptConfigSource.GetBoolean("PrivateRegionThreads", false)) + { + // PRIVATE THREAD POOL PER REGION + eventQueueThreads = new List(); + eventQueueThreadsLock = new object(); + } + else + { + // SHARED THREAD POOL + // Crate the objects in statics + if (staticEventQueueThreads == null) + staticEventQueueThreads = new List(); + if (staticEventQueueThreadsLock == null) + staticEventQueueThreadsLock = new object(); + + // Create local reference to them + eventQueueThreads = staticEventQueueThreads; + eventQueueThreadsLock = staticEventQueueThreadsLock; + } + + numberOfThreads = m_ScriptEngine.ScriptConfigSource.GetInt("NumberOfScriptThreads", 2); + + maxFunctionExecutionTimems = m_ScriptEngine.ScriptConfigSource.GetInt("MaxEventExecutionTimeMs", 5000); + EnforceMaxExecutionTime = m_ScriptEngine.ScriptConfigSource.GetBoolean("EnforceMaxEventExecutionTime", false); + + + // Start function max exec time enforcement thread if (EnforceMaxExecutionTime) { @@ -150,9 +184,10 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase // // Start event queue processing threads (worker threads) // + lock (eventQueueThreadsLock) { - for (int ThreadCount = 0; ThreadCount <= numberOfThreads; ThreadCount++) + for (int ThreadCount = eventQueueThreads.Count; ThreadCount <= numberOfThreads; ThreadCount++) { StartNewThreadClass(); } @@ -315,7 +350,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase } } - private static void AbortThreadClass(EventQueueThreadClass threadClass) + private void AbortThreadClass(EventQueueThreadClass threadClass) { try { @@ -326,12 +361,15 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase Console.WriteLine("Could you please report this to Tedd:"); Console.WriteLine("Script thread execution timeout kill ended in exception: " + ex.ToString()); } + m_ScriptEngine.Log.Debug("DotNetEngine", "Killed script execution thread, count: " + eventQueueThreads.Count); } private void StartNewThreadClass() { EventQueueThreadClass eqtc = new EventQueueThreadClass(this); eventQueueThreads.Add(eqtc); + m_ScriptEngine.Log.Debug("DotNetEngine", "Started new script execution thread, count: " + eventQueueThreads.Count); + } } } \ No newline at end of file diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueThreadClass.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueThreadClass.cs index ad79fbc0de..67cf0e23c5 100644 --- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueThreadClass.cs +++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueThreadClass.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Text; using System.Threading; using libsecondlife; +using Nini.Config; using OpenSim.Framework; using OpenSim.Region.Environment.Scenes.Scripting; @@ -16,7 +17,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase /// /// How many ms to sleep if queue is empty /// - private int nothingToDoSleepms = 50; + private int nothingToDoSleepms;// = 50; public DateTime LastExecutionStarted; public bool InExecution = false; @@ -28,6 +29,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase public EventQueueThreadClass(EventQueueManager eqm) { eventQueueManager = eqm; + nothingToDoSleepms = eqm.m_ScriptEngine.ScriptConfigSource.GetInt("SleepTimeIfNoScriptExecutionMs", 50); Start(); } @@ -183,7 +185,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase } catch (ThreadAbortException tae) { - throw tae; + eventQueueManager.m_ScriptEngine.Log.Notice("ScriptEngine", "ThreadAbortException while executing function."); } catch (Exception e) { diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptEngine.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptEngine.cs index da0baba193..8b64d3d2a9 100644 --- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptEngine.cs +++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptEngine.cs @@ -51,10 +51,15 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase public AppDomainManager m_AppDomainManager; public LSLLongCmdHandler m_LSLLongCmdHandler; + public IConfigSource ConfigSource; + public IConfig ScriptConfigSource; + public abstract string ScriptConfigSourceName { get; } + public ScriptManager GetScriptManager() { return _GetScriptManager(); } + public abstract ScriptManager _GetScriptManager(); private LogBase m_log; @@ -74,6 +79,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase { World = Sceneworld; m_log = logger; + ScriptConfigSource = ConfigSource.Configs[ScriptConfigSourceName]; Log.Verbose("ScriptEngine", "DotNet & LSL ScriptEngine initializing"); diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptEngine.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptEngine.cs index 48ac3bb437..de168b718c 100644 --- a/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptEngine.cs +++ b/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptEngine.cs @@ -39,6 +39,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine // We need to override a few things for our DotNetEngine public override void Initialise(Scene scene, IConfigSource config) { + ConfigSource = config; InitializeEngine(scene, MainLog.Instance, true, GetScriptManager()); } @@ -46,5 +47,10 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine { return new ScriptManager(this); } + + public override string ScriptConfigSourceName + { + get { return "ScriptEngine.DotNetEngine"; } + } } } \ No newline at end of file diff --git a/OpenSim/Region/ScriptEngine/LSOEngine/ScriptEngine.cs b/OpenSim/Region/ScriptEngine/LSOEngine/ScriptEngine.cs index 342a8ca443..aac210be19 100644 --- a/OpenSim/Region/ScriptEngine/LSOEngine/ScriptEngine.cs +++ b/OpenSim/Region/ScriptEngine/LSOEngine/ScriptEngine.cs @@ -51,5 +51,11 @@ namespace OpenSim.Region.ScriptEngine.LSOEngine { return new ScriptManager(this); } + + public override string ScriptConfigSourceName + { + get { return "ScriptEngine.LSOEngine"; } + } + } } \ No newline at end of file diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index 784083da70..a0ada5cbe8 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -114,3 +114,35 @@ shout_distance = 100 ; send a Sun update ever frame_rate # of frames. A lower number will ; make for smoother sun transition at the cost of network ;frame_rate = 100 + +[ScriptEngine.DotNetEngine] + +; When a script receives an event the event is queued. +; Any free thread will start executing this event. One script can only have one event executed simultaneously. +; If you have only one thread, and one script has a loop or does a lot of work, then no other scripts can run at the same time. +; Same if you have 10 threads, then only 10 scripts can be run simultaneously. +; But because most scripts exit after their task, the threads are free to go on to the next script. + +; Number of threads to use for script event execution +; Threads are shared across all regions +NumberOfScriptThreads=2 + +; Should the script threads be private for each region? +; true: Each region will get dedicated to scripts within that region +; Number of threads will be * +; false: All regions share for all their scripts +PrivateRegionThreads=false + +; How long MAX should a script be allowed to run? +; Do not set this too low (like 50ms) as there are some time wasted in simply executing a function +; There is also a small speed penalty for every kill that is made +MaxEventExecutionTimeMs=5000 + +; Should we enable the max script event execution thread to look for scripts that exceed their timeslice? +EnforceMaxEventExecutionTime=true + +; If no scripts have executed in this pass how long should we sleep before checking again +; Impact: +; Too low and you will waste lots of CPU +; Too high and people touching object or similar will have to wait up to this amount of time before script responding +SleepTimeIfNoScriptExecutionMs=50