From e06ee9fd4bb60c95009af516d92786de2a88f859 Mon Sep 17 00:00:00 2001
From: Tedd Hansen
Date: Sat, 2 Feb 2008 03:11:06 +0000
Subject: 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
---
.../Common/ScriptEngineBase/EventQueueManager.cs | 10 +++
.../Common/ScriptEngineBase/MaintenanceThread.cs | 15 ++---
.../Common/ScriptEngineBase/ScriptEngine.cs | 5 +-
.../Common/ScriptEngineBase/ScriptManager.cs | 71 +++++++++++++++++++---
.../ScriptEngine/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 fb20f40..7da2769 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 dc98637..fc2fda9 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 ea8ae1f..0b58da0 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 22cffff..53ef217 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 f6c9e30..821b2d4 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 9b9a139..495f4ce 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
--
cgit v1.1