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
ThreadPoolClientBranch
Tedd Hansen 2008-02-02 03:11:06 +00:00
parent 8ccc12d642
commit e06ee9fd4b
6 changed files with 98 additions and 21 deletions

View File

@ -99,6 +99,8 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
/// </summary> /// </summary>
internal int numberOfThreads; internal int numberOfThreads;
internal static int EventExecutionMaxQueueSize;
/// <summary> /// <summary>
/// Maximum time one function can use for execution before we perform a thread kill. /// Maximum time one function can use for execution before we perform a thread kill.
/// </summary> /// </summary>
@ -208,6 +210,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
maxFunctionExecutionTimems = m_ScriptEngine.ScriptConfigSource.GetInt("MaxEventExecutionTimeMs", 5000); maxFunctionExecutionTimems = m_ScriptEngine.ScriptConfigSource.GetInt("MaxEventExecutionTimeMs", 5000);
EnforceMaxExecutionTime = m_ScriptEngine.ScriptConfigSource.GetBoolean("EnforceMaxEventExecutionTime", false); EnforceMaxExecutionTime = m_ScriptEngine.ScriptConfigSource.GetBoolean("EnforceMaxEventExecutionTime", false);
KillScriptOnMaxFunctionExecutionTime = m_ScriptEngine.ScriptConfigSource.GetBoolean("DeactivateScriptOnTimeout", false); KillScriptOnMaxFunctionExecutionTime = m_ScriptEngine.ScriptConfigSource.GetBoolean("DeactivateScriptOnTimeout", false);
EventExecutionMaxQueueSize = m_ScriptEngine.ScriptConfigSource.GetInt("EventExecutionMaxQueueSize", 300);
// Now refresh config in all threads // Now refresh config in all threads
lock (eventQueueThreadsLock) lock (eventQueueThreadsLock)
@ -362,6 +365,13 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
{ {
lock (queueLock) 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 // Create a structure and add data
QueueItemStruct QIS = new QueueItemStruct(); QueueItemStruct QIS = new QueueItemStruct();
QIS.localID = localID; QIS.localID = localID;

View File

@ -38,19 +38,20 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
/// <summary> /// <summary>
/// Maintenance thread. Enforcing max execution time for example. /// Maintenance thread. Enforcing max execution time for example.
/// </summary> /// </summary>
public static Thread MaintenanceThreadThread; public Thread MaintenanceThreadThread;
/// <summary> /// <summary>
/// Starts maintenance thread /// Starts maintenance thread
/// </summary> /// </summary>
private void StartMaintenanceThread() private void StartMaintenanceThread()
{ {
StopMaintenanceThread(); if (MaintenanceThreadThread == null)
{
MaintenanceThreadThread = new Thread(MaintenanceLoop); MaintenanceThreadThread = new Thread(MaintenanceLoop);
MaintenanceThreadThread.Name = "ScriptMaintenanceThread"; MaintenanceThreadThread.Name = "ScriptMaintenanceThread";
MaintenanceThreadThread.IsBackground = true; MaintenanceThreadThread.IsBackground = true;
MaintenanceThreadThread.Start(); MaintenanceThreadThread.Start();
}
} }
/// <summary> /// <summary>

View File

@ -92,23 +92,26 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
m_log = logger; m_log = logger;
ConfigSource = config; ConfigSource = config;
Log.Verbose(ScriptEngineName, "ScriptEngine initializing"); Log.Verbose(ScriptEngineName, "ScriptEngine initializing");
Log.Verbose(ScriptEngineName, "Reading configuration from config section \"" + ScriptEngineName + "\"");
// Make sure we have config // Make sure we have config
if (ConfigSource.Configs[ScriptEngineName] == null) if (ConfigSource.Configs[ScriptEngineName] == null)
ConfigSource.AddConfig(ScriptEngineName); ConfigSource.AddConfig(ScriptEngineName);
ScriptConfigSource = ConfigSource.Configs[ScriptEngineName]; ScriptConfigSource = ConfigSource.Configs[ScriptEngineName];
//m_logger.Status(ScriptEngineName, "InitializeEngine"); //m_logger.Status(ScriptEngineName, "InitializeEngine");
// Create all objects we'll be using // Create all objects we'll be using
m_EventQueueManager = new EventQueueManager(this); m_EventQueueManager = new EventQueueManager(this);
m_EventManager = new EventManager(this, HookUpToServer); m_EventManager = new EventManager(this, HookUpToServer);
// We need to start it
newScriptManager.Start();
m_ScriptManager = newScriptManager; m_ScriptManager = newScriptManager;
m_AppDomainManager = new AppDomainManager(this); m_AppDomainManager = new AppDomainManager(this);
m_ASYNCLSLCommandManager = new AsyncLSLCommandManager(this); m_ASYNCLSLCommandManager = new AsyncLSLCommandManager(this);
m_MaintenanceThread = new MaintenanceThread(this); m_MaintenanceThread = new MaintenanceThread(this);
Log.Verbose(ScriptEngineName, "Reading configuration from config section \"" + ScriptEngineName + "\"");
ReadConfig(); ReadConfig();

View File

@ -62,9 +62,11 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
#region Declares #region Declares
private Thread scriptLoadUnloadThread; private Thread scriptLoadUnloadThread;
private static Thread staticScriptLoadUnloadThread;
private int scriptLoadUnloadThread_IdleSleepms; private int scriptLoadUnloadThread_IdleSleepms;
private Queue<LUStruct> LUQueue = new Queue<LUStruct>(); private Queue<LUStruct> LUQueue = new Queue<LUStruct>();
private static bool PrivateThread;
private int LoadUnloadMaxQueueSize;
// Load/Unload structure // Load/Unload structure
private struct LUStruct private struct LUStruct
@ -98,6 +100,8 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
public void ReadConfig() public void ReadConfig()
{ {
scriptLoadUnloadThread_IdleSleepms = m_scriptEngine.ScriptConfigSource.GetInt("ScriptLoadUnloadLoopms", 30); 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 #region Object init/shutdown
@ -107,14 +111,52 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
public ScriptManager(ScriptEngineBase.ScriptEngine scriptEngine) public ScriptManager(ScriptEngineBase.ScriptEngine scriptEngine)
{ {
m_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); AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
scriptLoadUnloadThread = new Thread(ScriptLoadUnloadThreadLoop);
scriptLoadUnloadThread.Name = "ScriptLoadUnloadThread"; //
scriptLoadUnloadThread.IsBackground = true; // CREATE THREAD
scriptLoadUnloadThread.Priority = ThreadPriority.BelowNormal; // Private or shared
scriptLoadUnloadThread.Start(); //
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() ~ScriptManager()
@ -122,6 +164,8 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
// Abort load/unload thread // Abort load/unload thread
try try
{ {
PleaseShutdown = true;
Thread.Sleep(100);
if (scriptLoadUnloadThread != null) if (scriptLoadUnloadThread != null)
{ {
if (scriptLoadUnloadThread.IsAlive == true) if (scriptLoadUnloadThread.IsAlive == true)
@ -148,6 +192,8 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
{ {
if (LUQueue.Count == 0) if (LUQueue.Count == 0)
Thread.Sleep(scriptLoadUnloadThread_IdleSleepms); Thread.Sleep(scriptLoadUnloadThread_IdleSleepms);
if (PleaseShutdown)
return;
if (LUQueue.Count > 0) if (LUQueue.Count > 0)
{ {
LUStruct item = LUQueue.Dequeue(); LUStruct item = LUQueue.Dequeue();
@ -185,7 +231,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
#endregion #endregion
#region Start/Stop/Reset script #region Start/Stop/Reset script
@ -198,6 +244,12 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
/// <param name="localID"></param> /// <param name="localID"></param>
public void StartScript(uint localID, LLUUID itemID, string Script) 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(); LUStruct ls = new LUStruct();
ls.localID = localID; ls.localID = localID;
ls.itemID = itemID; ls.itemID = itemID;
@ -224,9 +276,8 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
//private Compiler.LSL.Compiler LSLCompiler = new Compiler.LSL.Compiler(); //private Compiler.LSL.Compiler LSLCompiler = new Compiler.LSL.Compiler();
public abstract void _StartScript(uint localID, LLUUID itemID, string Script); public abstract void _StartScript(uint localID, LLUUID itemID, string Script);
public abstract void _StopScript(uint localID, LLUUID itemID); public abstract void _StopScript(uint localID, LLUUID itemID);
#endregion #endregion

View File

@ -86,7 +86,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
CompiledScript.Source = Script; CompiledScript.Source = Script;
// Add it to our script memstruct // 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 // 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. // 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 // Get AppDomain
AppDomain ad = LSLBC.Exec.GetAppDomain(); AppDomain ad = LSLBC.Exec.GetAppDomain();
// Tell script not to accept new requests // 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 // Remove from internal structure
RemoveScript(localID, itemID); m_scriptEngine.m_ScriptManager.RemoveScript(localID, itemID);
// Tell AppDomain that we have stopped script // Tell AppDomain that we have stopped script
m_scriptEngine.m_AppDomainManager.StopScript(ad); m_scriptEngine.m_AppDomainManager.StopScript(ad);
} }

View File

@ -195,6 +195,18 @@ ScriptsPerAppDomain=1
; But once active it will process all in queue before sleeping again ; But once active it will process all in queue before sleeping again
ScriptLoadUnloadLoopms=30 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 ; Async LL command sleep
; If no async LL commands are waiting, how long should thread sleep before checking again ; 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 ; Async LL commands are LSL-commands that causes an event to be fired back with result