From 53be4774b32f6736373c1364c3bd659c977fbb4e Mon Sep 17 00:00:00 2001
From: Tedd Hansen
Date: Sat, 25 Aug 2007 15:31:47 +0000
Subject: Scripts no longer crash sim after 5 minutes (override
InitializeLifetimeService). Loading/Unloading of scripts are now handled in
separate thread so server is no delayed because of this. Each script is
loaded into a single AppDomain (temporary test for script unload, eats ~15KB
more memory for each script). Unload of scripts has been verified to free up
memory.
---
.../ScriptEngine/DotNetEngine/AppDomainManager.cs | 35 +++---
.../DotNetEngine/Compiler/LSL/LSL_BaseClass.cs | 23 +++-
.../Compiler/Server_API/LSL_BuiltIn_Commands.cs | 18 +++
.../ScriptEngine/DotNetEngine/EventManager.cs | 6 +-
.../ScriptEngine/DotNetEngine/EventQueueManager.cs | 2 +-
.../ScriptEngine/DotNetEngine/ScriptManager.cs | 123 +++++++++++++++++----
6 files changed, 166 insertions(+), 41 deletions(-)
(limited to 'OpenSim/Region/ScriptEngine/DotNetEngine')
diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/AppDomainManager.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/AppDomainManager.cs
index 3319783..63e1844 100644
--- a/OpenSim/Region/ScriptEngine/DotNetEngine/AppDomainManager.cs
+++ b/OpenSim/Region/ScriptEngine/DotNetEngine/AppDomainManager.cs
@@ -15,7 +15,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
{
public class AppDomainManager
{
- private int MaxScriptsPerAppDomain = 3;
+ private int MaxScriptsPerAppDomain = 1;
///
/// Internal list of all AppDomains
///
@@ -59,7 +59,6 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
private AppDomainStructure GetFreeAppDomain()
{
Console.WriteLine("Finding free AppDomain");
- FreeAppDomains(); // Outsite lock, has its own GetLock
lock (GetLock)
{
// Current full?
@@ -111,7 +110,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
///
/// Unload appdomains that are full and have only dead scripts
///
- private void FreeAppDomains()
+ private void UnloadAppDomains()
{
lock (FreeLock)
{
@@ -125,10 +124,18 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
// 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
+
+ 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");
+#endif
}
}
} // foreach
@@ -142,20 +149,12 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
// Find next available AppDomain to put it in
AppDomainStructure FreeAppDomain = GetFreeAppDomain();
- //if (FreeAppDomain == null) Console.WriteLine("FreeAppDomain == null");
- //if (FreeAppDomain.CurrentAppDomain == null) Console.WriteLine("FreeAppDomain.CurrentAppDomain == null");
Console.WriteLine("Loading into AppDomain: " + FileName);
LSL_BaseClass mbrt = (LSL_BaseClass)FreeAppDomain.CurrentAppDomain.CreateInstanceFromAndUnwrap(FileName, "SecondLife.Script");
- //Type mytype = mbrt.GetType();
- Console.WriteLine("ScriptEngine AppDomainManager: is proxy={0}", RemotingServices.IsTransparentProxy(mbrt));
-
- // Increase script count in tihs AppDomain
+ //Console.WriteLine("ScriptEngine AppDomainManager: is proxy={0}", RemotingServices.IsTransparentProxy(mbrt));
FreeAppDomain.ScriptsLoaded++;
- //mbrt.Start();
return mbrt;
- //return (LSL_BaseClass)mbrt;
-
}
@@ -168,6 +167,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
{
lock (FreeLock)
{
+ Console.WriteLine("Stopping script in AppDomain");
// Check if it is current AppDomain
if (CurrentAD.CurrentAppDomain == ad)
{
@@ -181,15 +181,16 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
{
if (ads.CurrentAppDomain == ad)
{
- // Found it - messy code to increase structure
- //AppDomainStructure ads2 = ads;
+ // Found it
ads.ScriptsWaitingUnload++;
- //AppDomains.Remove(ads);
- //AppDomains.Add(ads2);
- return;
+ break;
}
} // foreach
} // lock
+
+ UnloadAppDomains(); // Outsite lock, has its own GetLock
+
+
}
diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/LSL/LSL_BaseClass.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/LSL/LSL_BaseClass.cs
index bfb8913..cb0f9ba 100644
--- a/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/LSL/LSL_BaseClass.cs
+++ b/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/LSL/LSL_BaseClass.cs
@@ -5,14 +5,33 @@ using OpenSim.Region.ScriptEngine.DotNetEngine.Compiler;
using OpenSim.Region.ScriptEngine.Common;
using System.Threading;
using System.Reflection;
-
+using System.Runtime.Remoting.Lifetime;
namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL
{
public class LSL_BaseClass : MarshalByRefObject, LSL_BuiltIn_Commands_Interface, IScript
{
+
+ // Object never expires
+ public override Object InitializeLifetimeService()
+ {
+ Console.WriteLine("LSL_BaseClass: InitializeLifetimeService()");
+ // return null;
+ ILease lease = (ILease)base.InitializeLifetimeService();
+
+ if (lease.CurrentState == LeaseState.Initial)
+ {
+ lease.InitialLeaseTime = TimeSpan.Zero; // TimeSpan.FromMinutes(1);
+ //lease.SponsorshipTimeout = TimeSpan.FromMinutes(2);
+ //lease.RenewOnCallTime = TimeSpan.FromSeconds(2);
+ }
+ return lease;
+ }
+
+
private Executor m_Exec;
- public Executor Exec {
+ public Executor Exec
+ {
get
{
if (m_Exec == null)
diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/Server_API/LSL_BuiltIn_Commands.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/Server_API/LSL_BuiltIn_Commands.cs
index ca51542..187ac59 100644
--- a/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/Server_API/LSL_BuiltIn_Commands.cs
+++ b/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/Server_API/LSL_BuiltIn_Commands.cs
@@ -7,6 +7,7 @@ using OpenSim.Region.Environment.Scenes.Scripting;
using OpenSim.Region.ScriptEngine.DotNetEngine.Compiler;
using OpenSim.Region.ScriptEngine.Common;
using OpenSim.Framework.Console;
+using System.Runtime.Remoting.Lifetime;
namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler
{
@@ -35,6 +36,23 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler
return m_state;
}
+ // Object never expires
+ public override Object InitializeLifetimeService()
+ {
+ Console.WriteLine("LSL_BuiltIn_Commands: InitializeLifetimeService()");
+ // return null;
+ ILease lease = (ILease)base.InitializeLifetimeService();
+
+ if (lease.CurrentState == LeaseState.Initial)
+ {
+ lease.InitialLeaseTime = TimeSpan.Zero; // TimeSpan.FromMinutes(1);
+ // lease.SponsorshipTimeout = TimeSpan.FromMinutes(2);
+ // lease.RenewOnCallTime = TimeSpan.FromSeconds(2);
+ }
+ return lease;
+ }
+
+
public Scene World
{
get { return m_manager.World; }
diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/EventManager.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/EventManager.cs
index ea5500a..986d333 100644
--- a/OpenSim/Region/ScriptEngine/DotNetEngine/EventManager.cs
+++ b/OpenSim/Region/ScriptEngine/DotNetEngine/EventManager.cs
@@ -62,7 +62,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
{
// Add to queue for all scripts in ObjectID object
//myScriptEngine.m_logger.Verbose("ScriptEngine", "EventManager Event: touch_start");
- Console.WriteLine("touch_start localID: " + localID);
+ //Console.WriteLine("touch_start localID: " + localID);
myScriptEngine.myEventQueueManager.AddToObjectQueue(localID, "touch_start", new object[] { (int)1 });
}
public void OnRezScript(uint localID, LLUUID itemID, string script)
@@ -85,6 +85,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
// Path.Combine("ScriptEngines", "Default.lsl"),
// new OpenSim.Region.Environment.Scenes.Scripting.NullScriptHost()
//);
+ Console.WriteLine("OnRemoveScript localID: " + localID + " LLUID: " + itemID.ToString());
myScriptEngine.myScriptManager.StopScript(
localID,
itemID
@@ -96,7 +97,8 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
// These needs to be hooked up to OpenSim during init of this class
// then queued in EventQueueManager.
// When queued in EventQueueManager they need to be LSL compatible (name and params)
- public void state_entry() { }
+
+ //public void state_entry() { } //
public void state_exit() { }
//public void touch_start() { }
public void touch() { }
diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs
index 012e60d..1cab01e 100644
--- a/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs
+++ b/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs
@@ -243,7 +243,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
// Do we have any scripts in this object at all? If not, return
if (myScriptEngine.myScriptManager.Scripts.ContainsKey(localID) == false)
{
- Console.WriteLine("Event \"" + FunctionName + "\" for localID: " + localID + ". No scripts found on this localID.");
+ //Console.WriteLine("Event \"" + FunctionName + "\" for localID: " + localID + ". No scripts found on this localID.");
return;
}
diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs
index cadae05..9621e56 100644
--- a/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs
+++ b/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs
@@ -50,11 +50,82 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
public class ScriptManager
{
+ private Thread ScriptLoadUnloadThread;
+ private int ScriptLoadUnloadThread_IdleSleepms = 100;
+ private Queue LoadQueue = new Queue();
+ private Queue UnloadQueue = new Queue();
+ private struct LoadStruct
+ {
+ public uint localID;
+ public LLUUID itemID;
+ public string Script;
+ }
+ private struct UnloadStruct
+ {
+ public uint localID;
+ public LLUUID itemID;
+ }
+
private ScriptEngine m_scriptEngine;
public ScriptManager(ScriptEngine scriptEngine)
{
m_scriptEngine = scriptEngine;
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
+ ScriptLoadUnloadThread = new Thread(ScriptLoadUnloadThreadLoop);
+ ScriptLoadUnloadThread.Name = "ScriptLoadUnloadThread";
+ ScriptLoadUnloadThread.IsBackground = true;
+ ScriptLoadUnloadThread.Priority = ThreadPriority.BelowNormal;
+ ScriptLoadUnloadThread.Start();
+
+ }
+ ~ScriptManager ()
+ {
+ // Abort load/unload thread
+ try
+ {
+ if (ScriptLoadUnloadThread != null)
+ {
+ if (ScriptLoadUnloadThread.IsAlive == true)
+ {
+ ScriptLoadUnloadThread.Abort();
+ ScriptLoadUnloadThread.Join();
+ }
+ }
+ }
+ catch
+ {
+ }
+ }
+ private void ScriptLoadUnloadThreadLoop()
+ {
+ try
+ {
+ while (true)
+ {
+ if (LoadQueue.Count == 0 && UnloadQueue.Count == 0)
+ Thread.Sleep(ScriptLoadUnloadThread_IdleSleepms);
+
+ if (LoadQueue.Count > 0)
+ {
+ LoadStruct item = LoadQueue.Dequeue();
+ _StartScript(item.localID, item.itemID, item.Script);
+ }
+
+ if (UnloadQueue.Count > 0)
+ {
+ UnloadStruct item = UnloadQueue.Dequeue();
+ _StopScript(item.localID, item.itemID);
+ }
+
+
+
+ }
+ }
+ catch (ThreadAbortException tae)
+ {
+ // Expected
+ }
+
}
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
@@ -146,8 +217,29 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
///
public void StartScript(uint localID, LLUUID itemID, string Script)
{
+ LoadStruct ls = new LoadStruct();
+ ls.localID = localID;
+ ls.itemID = itemID;
+ ls.Script = Script;
+ LoadQueue.Enqueue(ls);
+ }
+ ///
+ /// Disables and unloads a script
+ ///
+ ///
+ ///
+ public void StopScript(uint localID, LLUUID itemID)
+ {
+ UnloadStruct ls = new UnloadStruct();
+ ls.localID = localID;
+ ls.itemID = itemID;
+ UnloadQueue.Enqueue(ls);
+ }
+
+ private void _StartScript(uint localID, LLUUID itemID, string Script)
+ {
//IScriptHost root = host.GetRoot();
- m_scriptEngine.Log.Verbose("ScriptEngine", "ScriptManager StartScript: localID: " + localID + ", itemID: " + itemID);
+ Console.WriteLine("ScriptManager StartScript: localID: " + localID + ", itemID: " + itemID);
// We will initialize and start the script.
// It will be up to the script itself to hook up the correct events.
@@ -161,32 +253,23 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL.Compiler LSLCompiler = new OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL.Compiler();
// Compile (We assume LSL)
FileName = LSLCompiler.CompileFromLSLText(Script);
- m_scriptEngine.Log.Verbose("ScriptEngine", "Compilation of " + FileName + " done");
+ Console.WriteLine("Compilation of " + FileName + " done");
// * Insert yield into code
FileName = ProcessYield(FileName);
- //OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSO.LSL_BaseClass Script = LoadAndInitAssembly(FreeAppDomain, FileName);
-
- //OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL.LSL_BaseClass Script = LoadAndInitAssembly(FreeAppDomain, FileName, localID);
-
+#if DEBUG
long before;
before = GC.GetTotalMemory(false);
+#endif
LSL_BaseClass CompiledScript = m_scriptEngine.myAppDomainManager.LoadScript(FileName);
+#if DEBUG
Console.WriteLine("Script " + itemID + " occupies {0} bytes", GC.GetTotalMemory(false) - before);
- //before = GC.GetTotalMemory(false);
-
+#endif
- //Script = m_scriptEngine.myAppDomainManager.LoadScript(FileName);
- //Console.WriteLine("Script occupies {0} bytes", GC.GetTotalMemory(true) - before);
- //before = GC.GetTotalMemory(true);
- //Script = m_scriptEngine.myAppDomainManager.LoadScript(FileName);
- //Console.WriteLine("Script occupies {0} bytes", GC.GetTotalMemory(true) - before);
-
-
- // Add it to our temporary active script keeper
- //Scripts.Add(FullitemID, Script);
+ // Add it to our script memstruct
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.
LSL_BuiltIn_Commands LSLB = new LSL_BuiltIn_Commands(this, World.GetSceneObjectPart(localID));
@@ -202,9 +285,11 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
}
- public void StopScript(uint localID, LLUUID itemID)
+
+ private void _StopScript(uint localID, LLUUID itemID)
{
// Stop script
+ Console.WriteLine("Stop script localID: " + localID + " LLUID: " + itemID.ToString());
// Get AppDomain
AppDomain ad = GetScript(localID, itemID).Exec.GetAppDomain();
@@ -235,7 +320,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
{
// Execute a function in the script
- m_scriptEngine.Log.Verbose("ScriptEngine", "Executing Function localID: " + localID + ", itemID: " + itemID + ", FunctionName: " + FunctionName);
+ //m_scriptEngine.Log.Verbose("ScriptEngine", "Executing Function localID: " + localID + ", itemID: " + itemID + ", FunctionName: " + FunctionName);
LSL_BaseClass Script = m_scriptEngine.myScriptManager.GetScript(localID, itemID);
// Must be done in correct AppDomain, so leaving it up to the script itself
--
cgit v1.1