From e06ee9fd4bb60c95009af516d92786de2a88f859 Mon Sep 17 00:00:00 2001 From: Tedd Hansen Date: Sat, 2 Feb 2008 03:11:06 +0000 Subject: [PATCH] Added load/unload queue size limit Added option to share script load/unload thread between regions Added event execution queue size limit + some bugfixes from all the changes --- .../ScriptEngineBase/EventQueueManager.cs | 10 +++ .../ScriptEngineBase/MaintenanceThread.cs | 15 ++-- .../Common/ScriptEngineBase/ScriptEngine.cs | 5 +- .../Common/ScriptEngineBase/ScriptManager.cs | 71 ++++++++++++++++--- .../DotNetEngine/ScriptManager.cs | 6 +- bin/OpenSim.ini.example | 12 ++++ 6 files changed, 98 insertions(+), 21 deletions(-) diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs index fb20f4059f..7da2769558 100644 --- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs +++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs @@ -99,6 +99,8 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase /// internal int numberOfThreads; + internal static int EventExecutionMaxQueueSize; + /// /// Maximum time one function can use for execution before we perform a thread kill. /// @@ -208,6 +210,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase maxFunctionExecutionTimems = m_ScriptEngine.ScriptConfigSource.GetInt("MaxEventExecutionTimeMs", 5000); EnforceMaxExecutionTime = m_ScriptEngine.ScriptConfigSource.GetBoolean("EnforceMaxEventExecutionTime", false); KillScriptOnMaxFunctionExecutionTime = m_ScriptEngine.ScriptConfigSource.GetBoolean("DeactivateScriptOnTimeout", false); + EventExecutionMaxQueueSize = m_ScriptEngine.ScriptConfigSource.GetInt("EventExecutionMaxQueueSize", 300); // Now refresh config in all threads lock (eventQueueThreadsLock) @@ -362,6 +365,13 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase { lock (queueLock) { + if (eventQueue.Count >= EventExecutionMaxQueueSize) + { + m_ScriptEngine.Log.Error(m_ScriptEngine.ScriptEngineName, "ERROR: Event execution queue item count is at " + eventQueue.Count + ". Config variable \"EventExecutionMaxQueueSize\" is set to " + EventExecutionMaxQueueSize + ", so ignoring new event."); + m_ScriptEngine.Log.Error(m_ScriptEngine.ScriptEngineName, "Event ignored: localID: " + localID + ", itemID: " + itemID + ", FunctionName: " + FunctionName); + return; + } + // Create a structure and add data QueueItemStruct QIS = new QueueItemStruct(); QIS.localID = localID; diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/MaintenanceThread.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/MaintenanceThread.cs index dc98637bb1..fc2fda9248 100644 --- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/MaintenanceThread.cs +++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/MaintenanceThread.cs @@ -38,19 +38,20 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase /// /// Maintenance thread. Enforcing max execution time for example. /// - public static Thread MaintenanceThreadThread; + public Thread MaintenanceThreadThread; /// /// Starts maintenance thread /// private void StartMaintenanceThread() { - StopMaintenanceThread(); - - MaintenanceThreadThread = new Thread(MaintenanceLoop); - MaintenanceThreadThread.Name = "ScriptMaintenanceThread"; - MaintenanceThreadThread.IsBackground = true; - MaintenanceThreadThread.Start(); + if (MaintenanceThreadThread == null) + { + MaintenanceThreadThread = new Thread(MaintenanceLoop); + MaintenanceThreadThread.Name = "ScriptMaintenanceThread"; + MaintenanceThreadThread.IsBackground = true; + MaintenanceThreadThread.Start(); + } } /// diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptEngine.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptEngine.cs index ea8ae1f907..0b58da0015 100644 --- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptEngine.cs +++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptEngine.cs @@ -92,23 +92,26 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase m_log = logger; ConfigSource = config; Log.Verbose(ScriptEngineName, "ScriptEngine initializing"); - Log.Verbose(ScriptEngineName, "Reading configuration from config section \"" + ScriptEngineName + "\""); // Make sure we have config if (ConfigSource.Configs[ScriptEngineName] == null) ConfigSource.AddConfig(ScriptEngineName); ScriptConfigSource = ConfigSource.Configs[ScriptEngineName]; + //m_logger.Status(ScriptEngineName, "InitializeEngine"); // Create all objects we'll be using m_EventQueueManager = new EventQueueManager(this); m_EventManager = new EventManager(this, HookUpToServer); + // We need to start it + newScriptManager.Start(); m_ScriptManager = newScriptManager; m_AppDomainManager = new AppDomainManager(this); m_ASYNCLSLCommandManager = new AsyncLSLCommandManager(this); m_MaintenanceThread = new MaintenanceThread(this); + Log.Verbose(ScriptEngineName, "Reading configuration from config section \"" + ScriptEngineName + "\""); ReadConfig(); diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptManager.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptManager.cs index 22cffff78f..53ef217056 100644 --- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptManager.cs +++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptManager.cs @@ -62,9 +62,11 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase #region Declares private Thread scriptLoadUnloadThread; + private static Thread staticScriptLoadUnloadThread; private int scriptLoadUnloadThread_IdleSleepms; private Queue LUQueue = new Queue(); - + private static bool PrivateThread; + private int LoadUnloadMaxQueueSize; // Load/Unload structure private struct LUStruct @@ -98,6 +100,8 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase public void ReadConfig() { scriptLoadUnloadThread_IdleSleepms = m_scriptEngine.ScriptConfigSource.GetInt("ScriptLoadUnloadLoopms", 30); + PrivateThread = m_scriptEngine.ScriptConfigSource.GetBoolean("PrivateScriptLoadUnloadThread", false); + LoadUnloadMaxQueueSize = m_scriptEngine.ScriptConfigSource.GetInt("LoadUnloadMaxQueueSize", 100); } #region Object init/shutdown @@ -107,14 +111,52 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase public ScriptManager(ScriptEngineBase.ScriptEngine scriptEngine) { m_scriptEngine = scriptEngine; - // We should not read config during startup as ScriptEngine may not have config object yet + } + public void Start() + { + ReadConfig(); AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); - scriptLoadUnloadThread = new Thread(ScriptLoadUnloadThreadLoop); - scriptLoadUnloadThread.Name = "ScriptLoadUnloadThread"; - scriptLoadUnloadThread.IsBackground = true; - scriptLoadUnloadThread.Priority = ThreadPriority.BelowNormal; - scriptLoadUnloadThread.Start(); + + // + // CREATE THREAD + // Private or shared + // + if (PrivateThread) + { + // Assign one thread per region + scriptLoadUnloadThread = StartScriptLoadUnloadThread(); + } + else + { + // Shared thread - make sure one exist, then assign it to the private + if (staticScriptLoadUnloadThread == null) + { + staticScriptLoadUnloadThread = StartScriptLoadUnloadThread(); + } + scriptLoadUnloadThread = staticScriptLoadUnloadThread; + } + } + + private static int privateThreadCount = 0; + private Thread StartScriptLoadUnloadThread() + { + Thread t = new Thread(ScriptLoadUnloadThreadLoop); + string name = "ScriptLoadUnloadThread:"; + if (PrivateThread) + { + name += "Private:" + privateThreadCount; + privateThreadCount++; + } + else + { + name += "Shared"; + } + t.Name = name; + t.IsBackground = true; + t.Priority = ThreadPriority.Normal; + t.Start(); + return t; } ~ScriptManager() @@ -122,6 +164,8 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase // Abort load/unload thread try { + PleaseShutdown = true; + Thread.Sleep(100); if (scriptLoadUnloadThread != null) { if (scriptLoadUnloadThread.IsAlive == true) @@ -148,6 +192,8 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase { if (LUQueue.Count == 0) Thread.Sleep(scriptLoadUnloadThread_IdleSleepms); + if (PleaseShutdown) + return; if (LUQueue.Count > 0) { LUStruct item = LUQueue.Dequeue(); @@ -185,7 +231,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase #endregion - + #region Start/Stop/Reset script @@ -198,6 +244,12 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase /// public void StartScript(uint localID, LLUUID itemID, string Script) { + if (LUQueue.Count >= LoadUnloadMaxQueueSize) + { + m_scriptEngine.Log.Error(m_scriptEngine.ScriptEngineName, "ERROR: Load/unload queue item count is at " + LUQueue.Count + ". Config variable \"LoadUnloadMaxQueueSize\" is set to " + LoadUnloadMaxQueueSize + ", so ignoring new script."); + return; + } + LUStruct ls = new LUStruct(); ls.localID = localID; ls.itemID = itemID; @@ -224,9 +276,8 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase //private Compiler.LSL.Compiler LSLCompiler = new Compiler.LSL.Compiler(); public abstract void _StartScript(uint localID, LLUUID itemID, string Script); - public abstract void _StopScript(uint localID, LLUUID itemID); - + #endregion diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs index f6c9e300ed..821b2d4fc9 100644 --- a/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs +++ b/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs @@ -86,7 +86,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine CompiledScript.Source = Script; // Add it to our script memstruct - SetScript(localID, itemID, CompiledScript); + m_scriptEngine.m_ScriptManager.SetScript(localID, itemID, CompiledScript); // We need to give (untrusted) assembly a private instance of BuiltIns // this private copy will contain Read-Only FullitemID so that it can bring that on to the server whenever needed. @@ -144,9 +144,9 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine // Get AppDomain AppDomain ad = LSLBC.Exec.GetAppDomain(); // Tell script not to accept new requests - GetScript(localID, itemID).Exec.StopScript(); + m_scriptEngine.m_ScriptManager.GetScript(localID, itemID).Exec.StopScript(); // Remove from internal structure - RemoveScript(localID, itemID); + m_scriptEngine.m_ScriptManager.RemoveScript(localID, itemID); // Tell AppDomain that we have stopped script m_scriptEngine.m_AppDomainManager.StopScript(ad); } diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index 9b9a13977a..495f4ce694 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -195,6 +195,18 @@ ScriptsPerAppDomain=1 ; But once active it will process all in queue before sleeping again ScriptLoadUnloadLoopms=30 +; Loading and unloading of scripts is queued and processed by a separate thread. +; This thread can either be shared among all regions, or private (one thread per region) +PrivateScriptLoadUnloadThread=false + +; Maximum number of items in load/unload queue before we start rejecting loads +; Note that we will only be rejecting load. Unloads will still be able to queue. +LoadUnloadMaxQueueSize=100 + +; Maximum number of (LSL) events that can be queued before new events are ignored. +EventExecutionMaxQueueSize=300 + + ; Async LL command sleep ; If no async LL commands are waiting, how long should thread sleep before checking again ; Async LL commands are LSL-commands that causes an event to be fired back with result