SCRIPTING STILL BROKEN

Added comments and regions, restructured code
Changed a lot of AppDomain junk from console from using Console.Write to Log.Verbose and set it to #if DEBUG
All modules should now refresh their configuration runtime
Made all logging in ScriptEngine.Common get script name from actual engine
Renamed LSLLongCmdHandler to AsyncLSLCommandManager
Added auto-recover with 5 sec throttle for new MaintenanceThread
ThreadPoolClientBranch
Tedd Hansen 2008-02-01 23:36:36 +00:00
parent a6726b0c9d
commit d02a90823f
17 changed files with 621 additions and 404 deletions

View File

@ -112,7 +112,7 @@ namespace OpenSim.Region.ScriptEngine.Common
{
m_LSL_Functions = LSL_Functions;
//MainLog.Instance.Notice("ScriptEngine", "LSL_BaseClass.Start() called.");
//MainLog.Instance.Notice(ScriptEngineName, "LSL_BaseClass.Start() called.");
// Get this AppDomain's settings and display some of them.
AppDomainSetup ads = AppDomain.CurrentDomain.SetupInformation;

View File

@ -61,7 +61,7 @@ namespace OpenSim.Region.ScriptEngine.Common
m_localID = localID;
m_itemID = itemID;
//MainLog.Instance.Notice("ScriptEngine", "LSL_BaseClass.Start() called. Hosted by [" + m_host.Name + ":" + m_host.UUID + "@" + m_host.AbsolutePosition + "]");
//MainLog.Instance.Notice(ScriptEngineName, "LSL_BaseClass.Start() called. Hosted by [" + m_host.Name + ":" + m_host.UUID + "@" + m_host.AbsolutePosition + "]");
}
private DateTime m_timer = DateTime.Now;
@ -1038,7 +1038,7 @@ namespace OpenSim.Region.ScriptEngine.Common
public void llSetTimerEvent(double sec)
{
// Setting timer repeat
m_ScriptEngine.m_LSLLongCmdHandler.SetTimerEvent(m_localID, m_itemID, sec);
m_ScriptEngine.m_ASYNCLSLCommandManager.SetTimerEvent(m_localID, m_itemID, sec);
}
public void llSleep(double sec)

View File

@ -35,7 +35,7 @@ using OpenSim.Region.ScriptEngine.Common;
namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
{
public class AppDomainManager
public class AppDomainManager : iScriptEngineFunctionModule
{
//
@ -85,12 +85,17 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
private object getLock = new object(); // Mutex
private object freeLock = new object(); // Mutex
//private ScriptEngine m_scriptEngine;
private ScriptEngine m_scriptEngine;
//public AppDomainManager(ScriptEngine scriptEngine)
public AppDomainManager(int MaxScriptsPerDomain)
public AppDomainManager(ScriptEngine scriptEngine)
{
maxScriptsPerAppDomain = MaxScriptsPerDomain;
//m_scriptEngine = scriptEngine;
m_scriptEngine = scriptEngine;
ReadConfig();
}
public void ReadConfig()
{
maxScriptsPerAppDomain = m_scriptEngine.ScriptConfigSource.GetInt("ScriptsPerAppDomain", 1);
}
/// <summary>
@ -99,7 +104,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
/// <returns>Free AppDomain</returns>
private AppDomainStructure GetFreeAppDomain()
{
Console.WriteLine("Finding free AppDomain");
// Console.WriteLine("Finding free AppDomain");
lock (getLock)
{
// Current full?
@ -117,7 +122,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
currentAD.CurrentAppDomain = PrepareNewAppDomain();
}
Console.WriteLine("Scripts loaded in this Appdomain: " + currentAD.ScriptsLoaded);
// Console.WriteLine("Scripts loaded in this Appdomain: " + currentAD.ScriptsLoaded);
return currentAD;
} // lock
}
@ -144,7 +149,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
ads.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
AppDomain AD = AppDomain.CreateDomain("ScriptAppDomain_" + AppDomainNameCount, null, ads);
Console.WriteLine("Loading: " +
m_scriptEngine.Log.Verbose(m_scriptEngine.ScriptEngineName, "AppDomain Loading: " +
AssemblyName.GetAssemblyName("OpenSim.Region.ScriptEngine.Common.dll").ToString());
AD.Load(AssemblyName.GetAssemblyName("OpenSim.Region.ScriptEngine.Common.dll"));
@ -169,17 +174,16 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
// Is number of unloaded bigger or equal to number of loaded?
if (ads.ScriptsLoaded <= ads.ScriptsWaitingUnload)
{
Console.WriteLine("Found empty AppDomain, unloading");
// Remove from internal list
appDomains.Remove(ads);
#if DEBUG
Console.WriteLine("Found empty AppDomain, unloading");
long m = GC.GetTotalMemory(true);
#endif
// Unload
AppDomain.Unload(ads.CurrentAppDomain);
#if DEBUG
Console.WriteLine("AppDomain unload freed " + (m - GC.GetTotalMemory(true)) +
" bytes of memory");
m_scriptEngine.Log.Verbose(m_scriptEngine.ScriptEngineName, "AppDomain unload freed " + (m - GC.GetTotalMemory(true)) + " bytes of memory");
#endif
}
}
@ -193,7 +197,9 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
// Find next available AppDomain to put it in
AppDomainStructure FreeAppDomain = GetFreeAppDomain();
Console.WriteLine("Loading into AppDomain: " + FileName);
#if DEBUG
m_scriptEngine.Log.Verbose(m_scriptEngine.ScriptEngineName, "Loading into AppDomain: " + FileName);
#endif
IScript mbrt =
(IScript)
FreeAppDomain.CurrentAppDomain.CreateInstanceFromAndUnwrap(FileName, "SecondLife.Script");
@ -213,7 +219,9 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
{
lock (freeLock)
{
Console.WriteLine("Stopping script in AppDomain");
#if DEBUG
m_scriptEngine.Log.Verbose(m_scriptEngine.ScriptEngineName, "Stopping script in AppDomain");
#endif
// Check if it is current AppDomain
if (currentAD.CurrentAppDomain == ad)
{
@ -236,5 +244,15 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
UnloadAppDomains(); // Outsite lock, has its own GetLock
}
/// <summary>
/// If set to true then threads and stuff should try to make a graceful exit
/// </summary>
public bool PleaseShutdown
{
get { return _PleaseShutdown; }
set { _PleaseShutdown = value; }
}
private bool _PleaseShutdown = false;
}
}

View File

@ -38,16 +38,17 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
/// <summary>
/// Handles LSL commands that takes long time and returns an event, for example timers, HTTP requests, etc.
/// </summary>
public class LSLLongCmdHandler
public class AsyncLSLCommandManager : iScriptEngineFunctionModule
{
private Thread cmdHandlerThread;
private int cmdHandlerThreadCycleSleepms = 100;
private int cmdHandlerThreadCycleSleepms;
private ScriptEngine m_ScriptEngine;
public LSLLongCmdHandler(ScriptEngine _ScriptEngine)
public AsyncLSLCommandManager(ScriptEngine _ScriptEngine)
{
m_ScriptEngine = _ScriptEngine;
ReadConfig();
// Start the thread that will be doing the work
cmdHandlerThread = new Thread(CmdHandlerThreadLoop);
@ -57,7 +58,13 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
cmdHandlerThread.Start();
}
~LSLLongCmdHandler()
public void ReadConfig()
{
cmdHandlerThreadCycleSleepms = m_ScriptEngine.ScriptConfigSource.GetInt("AsyncLLCommandLoopms", 50);
}
~AsyncLSLCommandManager()
{
// Shut down thread
try
@ -291,5 +298,16 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
);
}
}
/// <summary>
/// If set to true then threads and stuff should try to make a graceful exit
/// </summary>
public bool PleaseShutdown
{
get { return _PleaseShutdown; }
set { _PleaseShutdown = value; }
}
private bool _PleaseShutdown = false;
}
}

View File

@ -44,14 +44,14 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
public static void SendToDebug(string Message)
{
//if (Debug == true)
mySE.Log.Verbose("ScriptEngine", "Debug: " + Message);
mySE.Log.Verbose(mySE.ScriptEngineName, "Debug: " + Message);
//SendToDebugEvent("\r\n" + DateTime.Now.ToString("[HH:mm:ss] ") + Message);
}
public static void SendToLog(string Message)
{
//if (Debug == true)
mySE.Log.Verbose("ScriptEngine", "LOG: " + Message);
mySE.Log.Verbose(mySE.ScriptEngineName, "LOG: " + Message);
//SendToLogEvent("\r\n" + DateTime.Now.ToString("[HH:mm:ss] ") + Message);
}
}

View File

@ -37,7 +37,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
/// Prepares events so they can be directly executed upon a script by EventQueueManager, then queues it.
/// </summary>
[Serializable]
public class EventManager : OpenSim.Region.ScriptEngine.Common.ScriptServerInterfaces.RemoteEvents
public class EventManager : OpenSim.Region.ScriptEngine.Common.ScriptServerInterfaces.RemoteEvents, iScriptEngineFunctionModule
{
//
@ -59,12 +59,13 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
public EventManager(ScriptEngine _ScriptEngine, bool performHookUp)
{
myScriptEngine = _ScriptEngine;
ReadConfig();
// Hook up to events from OpenSim
// We may not want to do it because someone is controlling us and will deliver events to us
if (performHookUp)
{
myScriptEngine.Log.Verbose("ScriptEngine", "Hooking up to server events");
myScriptEngine.Log.Verbose(myScriptEngine.ScriptEngineName, "Hooking up to server events");
myScriptEngine.World.EventManager.OnObjectGrab += touch_start;
myScriptEngine.World.EventManager.OnRezScript += OnRezScript;
myScriptEngine.World.EventManager.OnRemoveScript += OnRemoveScript;
@ -73,6 +74,11 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
}
}
public void ReadConfig()
{
}
public void changed(uint localID, uint change)
{
// Add to queue for all scripts in localID, Object pass change.
@ -263,5 +269,16 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
{
// myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "http_response", EventQueueManager.llDetectNull);
}
/// <summary>
/// If set to true then threads and stuff should try to make a graceful exit
/// </summary>
public bool PleaseShutdown
{
get { return _PleaseShutdown; }
set { _PleaseShutdown = value; }
}
private bool _PleaseShutdown = false;
}
}

View File

@ -42,7 +42,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
/// Events are queued and executed in separate thread
/// </summary>
[Serializable]
public class EventQueueManager
public class EventQueueManager : iScriptEngineFunctionModule
{
//
@ -197,13 +197,22 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
}
private void ReadConfig()
public void ReadConfig()
{
// Refresh config
numberOfThreads = m_ScriptEngine.ScriptConfigSource.GetInt("NumberOfScriptThreads", 2);
maxFunctionExecutionTimems = m_ScriptEngine.ScriptConfigSource.GetInt("MaxEventExecutionTimeMs", 5000);
EnforceMaxExecutionTime = m_ScriptEngine.ScriptConfigSource.GetBoolean("EnforceMaxEventExecutionTime", false);
KillScriptOnMaxFunctionExecutionTime = m_ScriptEngine.ScriptConfigSource.GetBoolean("DeactivateScriptOnTimeout", false);
// Now refresh config in all threads
lock (eventQueueThreadsLock)
{
foreach (EventQueueThreadClass EventQueueThread in eventQueueThreads)
{
EventQueueThread.ReadConfig();
}
}
}
#endregion
@ -222,7 +231,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
{
foreach (EventQueueThreadClass EventQueueThread in eventQueueThreads)
{
EventQueueThread.Shutdown();
AbortThreadClass(EventQueueThread);
}
eventQueueThreads.Clear();
staticGlobalEventQueueThreads.Clear();
@ -243,7 +252,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
EventQueueThreadClass eqtc = new EventQueueThreadClass(this);
eventQueueThreads.Add(eqtc);
staticGlobalEventQueueThreads.Add(eqtc);
m_ScriptEngine.Log.Debug("DotNetEngine", "Started new script execution thread. Current thread count: " + eventQueueThreads.Count);
m_ScriptEngine.Log.Debug(m_ScriptEngine.ScriptEngineName, "Started new script execution thread. Current thread count: " + eventQueueThreads.Count);
}
private void AbortThreadClass(EventQueueThreadClass threadClass)
@ -252,16 +261,17 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
eventQueueThreads.Remove(threadClass);
if (staticGlobalEventQueueThreads.Contains(threadClass))
staticGlobalEventQueueThreads.Remove(threadClass);
try
{
threadClass.Shutdown();
threadClass.Stop();
}
catch (Exception ex)
{
m_ScriptEngine.Log.Error("EventQueueManager", "If you see this, could you please report it to Tedd:");
m_ScriptEngine.Log.Error("EventQueueManager", "Script thread execution timeout kill ended in exception: " + ex.ToString());
m_ScriptEngine.Log.Error(m_ScriptEngine.ScriptEngineName + ":EventQueueManager", "If you see this, could you please report it to Tedd:");
m_ScriptEngine.Log.Error(m_ScriptEngine.ScriptEngineName + ":EventQueueManager", "Script thread execution timeout kill ended in exception: " + ex.ToString());
}
m_ScriptEngine.Log.Debug("DotNetEngine", "Killed script execution thread. Remaining thread count: " + eventQueueThreads.Count);
m_ScriptEngine.Log.Debug(m_ScriptEngine.ScriptEngineName, "Killed script execution thread. Remaining thread count: " + eventQueueThreads.Count);
}
#endregion
@ -313,7 +323,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
public void AddToObjectQueue(uint localID, string FunctionName, Queue_llDetectParams_Struct qParams, params object[] param)
{
// Determine all scripts in Object and add to their queue
//myScriptEngine.m_logger.Verbose("ScriptEngine", "EventQueueManager Adding localID: " + localID + ", FunctionName: " + FunctionName);
//myScriptEngine.m_logger.Verbose(ScriptEngineName, "EventQueueManager Adding localID: " + localID + ", FunctionName: " + FunctionName);
// Do we have any scripts in this object at all? If not, return
@ -367,6 +377,10 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
/// </summary>
public void AdjustNumberOfScriptThreads()
{
// Is there anything here for us to do?
if (eventQueueThreads.Count == numberOfThreads)
return;
lock (eventQueueThreadsLock)
{
int diff = numberOfThreads - eventQueueThreads.Count;
@ -424,5 +438,15 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
}
}
#endregion
/// <summary>
/// If set to true then threads and stuff should try to make a graceful exit
/// </summary>
public bool PleaseShutdown
{
get { return _PleaseShutdown; }
set { _PleaseShutdown = value; }
}
private bool _PleaseShutdown = false;
}
}

View File

@ -12,12 +12,13 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
/// <summary>
/// Because every thread needs some data set for it (time started to execute current function), it will do its work within a class
/// </summary>
public class EventQueueThreadClass
public class EventQueueThreadClass: iScriptEngineFunctionModule
{
/// <summary>
/// How many ms to sleep if queue is empty
/// </summary>
private int nothingToDoSleepms;// = 50;
private ThreadPriority MyThreadPriority;
public long LastExecutionStarted;
public bool InExecution = false;
@ -26,25 +27,27 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
private EventQueueManager eventQueueManager;
public Thread EventQueueThread;
private static int ThreadCount = 0;
private ThreadPriority MyThreadPriority;
private string ScriptEngineName = "ScriptEngine.Common";
public EventQueueThreadClass(EventQueueManager eqm)
{
eventQueueManager = eqm;
nothingToDoSleepms = eqm.m_ScriptEngine.ScriptConfigSource.GetInt("SleepTimeIfNoScriptExecutionMs", 50);
ReadConfig();
Start();
}
~EventQueueThreadClass()
{
Shutdown();
Stop();
}
/// <summary>
/// Start thread
/// </summary>
private void Start()
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())
@ -70,6 +73,19 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
break;
}
// Now set that priority
if (EventQueueThread != null)
if (EventQueueThread.IsAlive)
EventQueueThread.Priority = MyThreadPriority;
}
/// <summary>
/// Start thread
/// </summary>
private void Start()
{
EventQueueThread = new Thread(EventQueueThreadLoop);
EventQueueThread.IsBackground = true;
@ -84,18 +100,20 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
ThreadCount++;
}
public void Shutdown()
public void Stop()
{
PleaseShutdown = true; // Set shutdown flag
Thread.Sleep(100); // Wait a bit
if (EventQueueThread != null && EventQueueThread.IsAlive == true)
{
try
{
EventQueueThread.Abort();
EventQueueThread.Join();
EventQueueThread.Abort(); // Send abort
EventQueueThread.Join(); // Wait for it
}
catch (Exception)
{
//myScriptEngine.Log.Verbose("ScriptEngine", "EventQueueManager Exception killing worker thread: " + e.ToString());
//myScriptEngine.Log.Verbose(ScriptEngineName, "EventQueueManager Exception killing worker thread: " + e.ToString());
}
}
}
@ -106,10 +124,10 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
/// </summary>
private void EventQueueThreadLoop()
{
//myScriptEngine.m_logger.Verbose("ScriptEngine", "EventQueueManager Worker thread spawned");
//myScriptEngine.m_logger.Verbose(ScriptEngineName, "EventQueueManager Worker thread spawned");
try
{
while (true)
while (true)
{
try
{
@ -117,7 +135,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
while (true)
{
// Every now and then check if we should shut down
if (eventQueueManager.ThreadsToExit > 0)
if (PleaseShutdown || eventQueueManager.ThreadsToExit > 0)
{
// Someone should shut down, lets get exclusive lock
lock (eventQueueManager.ThreadsToExitLock)
@ -125,9 +143,15 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
// Lets re-check in case someone grabbed it
if (eventQueueManager.ThreadsToExit > 0)
{
// We are go for shutdown
// Its crowded here so we'll shut down
eventQueueManager.ThreadsToExit--;
Shutdown();
Stop();
return;
}
else
{
// We have been asked to shut down
Stop();
return;
}
}
@ -139,6 +163,9 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
EventQueueManager.QueueItemStruct QIS = BlankQIS;
bool GotItem = false;
if (PleaseShutdown)
return;
if (eventQueueManager.eventQueue.Count == 0)
{
// Nothing to do? Sleep a bit waiting for something to do
@ -147,7 +174,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
else
{
// Something in queue, process
//myScriptEngine.m_logger.Verbose("ScriptEngine", "Processing event for localID: " + QIS.localID + ", itemID: " + QIS.itemID + ", FunctionName: " + QIS.FunctionName);
//myScriptEngine.m_logger.Verbose(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.queueLock)
@ -179,7 +206,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
try
{
#if DEBUG
eventQueueManager.m_ScriptEngine.Log.Debug("ScriptEngine",
eventQueueManager.m_ScriptEngine.Log.Debug(ScriptEngineName,
"Executing event:\r\n"
+ "QIS.localID: " + QIS.localID
+ ", QIS.itemID: " + QIS.itemID
@ -235,7 +262,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
//else
//{
// T oconsole
eventQueueManager.m_ScriptEngine.Log.Error("ScriptEngine",
eventQueueManager.m_ScriptEngine.Log.Error(ScriptEngineName,
"Unable to send text in-world:\r\n" +
text);
}
@ -260,19 +287,28 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
}
catch (ThreadAbortException tae)
{
eventQueueManager.m_ScriptEngine.Log.Notice("ScriptEngine", "ThreadAbortException while executing function.");
eventQueueManager.m_ScriptEngine.Log.Notice(ScriptEngineName, "ThreadAbortException while executing function.");
}
catch (Exception e)
{
eventQueueManager.m_ScriptEngine.Log.Error("ScriptEngine", "Exception in EventQueueThreadLoop: " + e.ToString());
eventQueueManager.m_ScriptEngine.Log.Error(ScriptEngineName, "Exception in EventQueueThreadLoop: " + e.ToString());
}
} // while
} // try
catch (ThreadAbortException)
{
//myScriptEngine.Log.Verbose("ScriptEngine", "EventQueueManager Worker thread killed: " + tae.Message);
//myScriptEngine.Log.Verbose(ScriptEngineName, "EventQueueManager Worker thread killed: " + tae.Message);
}
}
/// <summary>
/// If set to true then threads and stuff should try to make a graceful exit
/// </summary>
public bool PleaseShutdown
{
get { return _PleaseShutdown; }
set { _PleaseShutdown = value; }
}
private bool _PleaseShutdown = false;
}
}

View File

@ -8,7 +8,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
/// <summary>
/// This class does maintenance on script engine.
/// </summary>
public class MaintenanceThread
public class MaintenanceThread : iScriptEngineFunctionModule
{
public ScriptEngine m_ScriptEngine;
private int MaintenanceLoopms;
@ -28,7 +28,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
StopMaintenanceThread();
}
private void ReadConfig()
public void ReadConfig()
{
MaintenanceLoopms = m_ScriptEngine.ScriptConfigSource.GetInt("MaintenanceLoopms", 50);
}
@ -80,48 +80,74 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
/// </summary>
public void MaintenanceLoop()
{
try
if (m_ScriptEngine.m_EventQueueManager.maxFunctionExecutionTimens < MaintenanceLoopms)
m_ScriptEngine.Log.Warn(m_ScriptEngine.ScriptEngineName,
"Configuration error: MaxEventExecutionTimeMs is less than MaintenanceLoopms. The Maintenance Loop will only check scripts once per run.");
while (true)
{
long Last_maxFunctionExecutionTimens = 0;// DateTime.Now.Ticks;
long Last_ReReadConfigFilens = DateTime.Now.Ticks;
while (true)
try
{
System.Threading.Thread.Sleep(MaintenanceLoopms); // Sleep
// Re-reading config every x seconds?
if (m_ScriptEngine.ReReadConfigFileSeconds > 0)
long Last_maxFunctionExecutionTimens = 0; // DateTime.Now.Ticks;
long Last_ReReadConfigFilens = DateTime.Now.Ticks;
while (true)
{
// Check if its time to re-read config
if (DateTime.Now.Ticks - Last_ReReadConfigFilens > m_ScriptEngine.ReReadConfigFilens)
System.Threading.Thread.Sleep(MaintenanceLoopms); // Sleep before next pass
if (PleaseShutdown)
return;
//
// Re-reading config every x seconds
//
if (m_ScriptEngine.RefreshConfigFileSeconds > 0)
{
// Its time to re-read config file
m_ScriptEngine.ConfigSource.Reload(); // Re-read config
Last_ReReadConfigFilens = DateTime.Now.Ticks; // Reset time
// Check if its time to re-read config
if (DateTime.Now.Ticks - Last_ReReadConfigFilens > m_ScriptEngine.RefreshConfigFilens)
{
// Its time to re-read config file
m_ScriptEngine.ConfigSource.Reload(); // Refresh config
m_ScriptEngine.ReadConfig();
Last_ReReadConfigFilens = DateTime.Now.Ticks; // Reset time
}
}
}
// Adjust number of running script threads if not correct
if (m_ScriptEngine.m_EventQueueManager.eventQueueThreads.Count != m_ScriptEngine.m_EventQueueManager.numberOfThreads)
{
//
// Adjust number of running script threads if not correct
//
m_ScriptEngine.m_EventQueueManager.AdjustNumberOfScriptThreads();
}
// Check if any script has exceeded its max execution time
if (m_ScriptEngine.m_EventQueueManager.EnforceMaxExecutionTime)
{
if (DateTime.Now.Ticks - Last_maxFunctionExecutionTimens > m_ScriptEngine.m_EventQueueManager.maxFunctionExecutionTimens)
//
// Check if any script has exceeded its max execution time
//
if (m_ScriptEngine.m_EventQueueManager.EnforceMaxExecutionTime)
{
m_ScriptEngine.m_EventQueueManager.CheckScriptMaxExecTime(); // Do check
Last_maxFunctionExecutionTimens = DateTime.Now.Ticks; // Reset time
// We are enforcing execution time
if (DateTime.Now.Ticks - Last_maxFunctionExecutionTimens >
m_ScriptEngine.m_EventQueueManager.maxFunctionExecutionTimens)
{
// Its time to check again
m_ScriptEngine.m_EventQueueManager.CheckScriptMaxExecTime(); // Do check
Last_maxFunctionExecutionTimens = DateTime.Now.Ticks; // Reset time
}
}
}
}
}
catch (ThreadAbortException tae)
{
catch (Exception ex)
{
m_ScriptEngine.Log.Error(m_ScriptEngine.ScriptEngineName, "Exception in MaintenanceLoopThread. Thread will recover after 5 sec throttle. Exception: " + ex.ToString());
Thread.Sleep(5000);
}
}
}
#endregion
/// <summary>
/// If set to true then threads and stuff should try to make a graceful exit
/// </summary>
public bool PleaseShutdown
{
get { return _PleaseShutdown; }
set { _PleaseShutdown = value; }
}
private bool _PleaseShutdown = false;
}
}

View File

@ -42,27 +42,28 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
/// </summary>
///
[Serializable]
public abstract class ScriptEngine : IRegionModule, OpenSim.Region.ScriptEngine.Common.ScriptServerInterfaces.ScriptEngine
public abstract class ScriptEngine : IRegionModule, OpenSim.Region.ScriptEngine.Common.ScriptServerInterfaces.ScriptEngine, iScriptEngineFunctionModule
{
public Scene World;
public EventManager m_EventManager; // Handles and queues incoming events from OpenSim
public EventQueueManager m_EventQueueManager; // Executes events
public ScriptManager m_ScriptManager; // Load, unload and execute scripts
public AppDomainManager m_AppDomainManager;
public LSLLongCmdHandler m_LSLLongCmdHandler;
public EventManager m_EventManager; // Handles and queues incoming events from OpenSim
public EventQueueManager m_EventQueueManager; // Executes events, handles script threads
public ScriptManager m_ScriptManager; // Load, unload and execute scripts
public AppDomainManager m_AppDomainManager; // Handles loading/unloading of scripts into AppDomains
public AsyncLSLCommandManager m_ASYNCLSLCommandManager; // Asyncronous LSL commands (commands that returns with an event)
public MaintenanceThread m_MaintenanceThread; // Thread that does different kinds of maintenance, for example refreshing config and killing scripts that has been running too long
public IConfigSource ConfigSource;
public IConfig ScriptConfigSource;
public abstract string ScriptConfigSourceName { get; }
public abstract string ScriptEngineName { get; }
/// <summary>
/// How many seconds between re-reading config-file. 0 = never. ScriptEngine will try to adjust to new config changes.
/// </summary>
public int ReReadConfigFileSeconds {
get { return (int)(ReReadConfigFilens / 10000); }
set { ReReadConfigFilens = value * 10000; }
public int RefreshConfigFileSeconds {
get { return (int)(RefreshConfigFilens / 10000); }
set { RefreshConfigFilens = value * 10000; }
}
public long ReReadConfigFilens = 0;
public long RefreshConfigFilens = 0;
public ScriptManager GetScriptManager()
{
@ -88,21 +89,22 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
{
World = Sceneworld;
m_log = logger;
ScriptConfigSource = ConfigSource.Configs[ScriptConfigSourceName];
ScriptConfigSource = ConfigSource.Configs[ScriptEngineName];
Log.Verbose("ScriptEngine", "DotNet & LSL ScriptEngine initializing");
Log.Verbose(ScriptEngineName, "DotNet & LSL ScriptEngine initializing");
//m_logger.Status("ScriptEngine", "InitializeEngine");
//m_logger.Status(ScriptEngineName, "InitializeEngine");
// Create all objects we'll be using
m_EventQueueManager = new EventQueueManager(this);
m_EventManager = new EventManager(this, HookUpToServer);
m_ScriptManager = newScriptManager;
//m_ScriptManager = new ScriptManager(this);
m_AppDomainManager = new AppDomainManager(ScriptConfigSource.GetInt("ScriptsPerAppDomain", 1));
m_LSLLongCmdHandler = new LSLLongCmdHandler(this);
m_AppDomainManager = new AppDomainManager(this);
m_ASYNCLSLCommandManager = new AsyncLSLCommandManager(this);
m_MaintenanceThread = new MaintenanceThread(this);
ReadConfig();
ReReadConfigFileSeconds = ScriptConfigSource.GetInt("ReReadConfig", 0);
// Should we iterate the region for scripts that needs starting?
@ -118,6 +120,26 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
{
return this.m_EventManager;
}
public void ReadConfig()
{
#if DEBUG
Log.Debug(ScriptEngineName, "Refreshing configuration for all modules");
#endif
RefreshConfigFileSeconds = ScriptConfigSource.GetInt("RefreshConfig", 0);
// Reload from disk
ConfigSource.Reload();
// Create a new object (probably not necessary?)
// ScriptConfigSource = ConfigSource.Configs[ScriptEngineName];
if (m_EventQueueManager != null) m_EventQueueManager.ReadConfig();
if (m_EventManager != null) m_EventManager.ReadConfig();
if (m_ScriptManager != null) m_ScriptManager.ReadConfig();
if (m_AppDomainManager != null) m_AppDomainManager.ReadConfig();
if (m_ASYNCLSLCommandManager != null) m_ASYNCLSLCommandManager.ReadConfig();
if (m_MaintenanceThread != null) m_MaintenanceThread.ReadConfig();
}
#region IRegionModule
@ -134,7 +156,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
public string Name
{
get { return "DotNetEngine"; }
get { return "Common." + ScriptEngineName; }
}
public bool IsSharedModule
@ -146,5 +168,15 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
#endregion
/// <summary>
/// If set to true then threads and stuff should try to make a graceful exit
/// </summary>
public bool PleaseShutdown
{
get { return _PleaseShutdown; }
set { _PleaseShutdown = value; }
}
private bool _PleaseShutdown = false;
}
}

View File

@ -57,12 +57,12 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
// This so that scripts starting or stopping will not slow down other theads or whole system.
//
[Serializable]
public abstract class ScriptManager
public abstract class ScriptManager : iScriptEngineFunctionModule
{
#region Declares
private Thread scriptLoadUnloadThread;
private int scriptLoadUnloadThread_IdleSleepms = 100;
private int scriptLoadUnloadThread_IdleSleepms;
private Queue<LUStruct> LUQueue = new Queue<LUStruct>();
@ -95,6 +95,11 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
#endregion
public void ReadConfig()
{
scriptLoadUnloadThread_IdleSleepms = m_scriptEngine.ScriptConfigSource.GetInt("ScriptLoadUnloadLoopms", 30);
}
#region Object init/shutdown
public ScriptEngineBase.ScriptEngine m_scriptEngine;
@ -102,6 +107,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
public ScriptManager(ScriptEngineBase.ScriptEngine scriptEngine)
{
m_scriptEngine = scriptEngine;
ReadConfig();
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
scriptLoadUnloadThread = new Thread(ScriptLoadUnloadThreadLoop);
scriptLoadUnloadThread.Name = "ScriptLoadUnloadThread";
@ -238,7 +244,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
Console.WriteLine("ScriptEngine: Inside ExecuteEvent for event " + FunctionName);
#endif
// Execute a function in the script
//m_scriptEngine.Log.Verbose("ScriptEngine", "Executing Function localID: " + localID + ", itemID: " + itemID + ", FunctionName: " + FunctionName);
//m_scriptEngine.Log.Verbose(ScriptEngineName, "Executing Function localID: " + localID + ", itemID: " + itemID + ", FunctionName: " + FunctionName);
//ScriptBaseInterface Script = (ScriptBaseInterface)GetScript(localID, itemID);
IScript Script = GetScript(localID, itemID);
if (Script == null)
@ -345,5 +351,16 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
}
#endregion
/// <summary>
/// If set to true then threads and stuff should try to make a graceful exit
/// </summary>
public bool PleaseShutdown
{
get { return _PleaseShutdown; }
set { _PleaseShutdown = value; }
}
private bool _PleaseShutdown = false;
}
}

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
{
public interface iScriptEngineFunctionModule
{
void ReadConfig();
bool PleaseShutdown { get; set; }
}
}

View File

@ -48,7 +48,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
return new ScriptManager(this);
}
public override string ScriptConfigSourceName
public override string ScriptEngineName
{
get { return "ScriptEngine.DotNetEngine"; }
}

View File

@ -124,7 +124,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
// Stop long command on script
m_scriptEngine.m_LSLLongCmdHandler.RemoveScript(localID, itemID);
m_scriptEngine.m_ASYNCLSLCommandManager.RemoveScript(localID, itemID);
IScript LSLBC = GetScript(localID, itemID);
if (LSLBC == null)

View File

@ -52,7 +52,7 @@ namespace OpenSim.Region.ScriptEngine.LSOEngine
return new ScriptManager(this);
}
public override string ScriptConfigSourceName
public override string ScriptEngineName
{
get { return "ScriptEngine.LSOEngine"; }
}

View File

@ -127,7 +127,7 @@ namespace OpenSim.Region.ScriptEngine.LSOEngine
// Stop long command on script
m_scriptEngine.m_LSLLongCmdHandler.RemoveScript(localID, itemID);
m_scriptEngine.m_ASYNCLSLCommandManager.RemoveScript(localID, itemID);
IScript LSLBC = GetScript(localID, itemID);
if (LSLBC == null)

View File

@ -123,6 +123,13 @@ shout_distance = 100
; 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.
; Refresh ScriptEngine config options (these settings) every xx seconds
; 0 = Do not refresh
; Set it to number of seconds between refresh, for example 30.
; Will allow you to change ScriptEngine settings while server is running just by editing this file.
; For example to increase or decrease number of threads.
RefreshConfig=0
; Number of threads to use for script event execution
; Threads are shared across all regions
NumberOfScriptThreads=2
@ -136,6 +143,7 @@ ScriptThreadPriority=BelowNormal
; Number of threads will be <NumberOfScriptThreads>*<NumberOfRegions>
; false: All regions share <NumberOfScriptThreads> for all their scripts
; Note! If you run multiple script engines based on "OpenSim.Region.ScriptEngine.Common" then all of them will share the same threads.
; *** This setting will not work until you restart OpenSim
PrivateRegionThreads=false
; How long MAX should a script event be allowed to run (per event execution)?
@ -164,5 +172,14 @@ SleepTimeIfNoScriptExecutionMs=50
; Each AppDomain has some memory overhead. But leaving dead scripts in memory also has memory overhead.
ScriptsPerAppDomain=1
; ReRead ScriptEngine config options how often?
ReReadConfig=0
; Script loading / unloading sleep
; How long load/unload thread should sleep if there is nothing to do
; Higher value makes it respond slower when scripts are added/removed from prims
; But once active it will process all in queue before sleeping again
ScriptLoadUnloadLoopms=30
; 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
AsyncLLCommandLoopms=50