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; 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. // Get this AppDomain's settings and display some of them.
AppDomainSetup ads = AppDomain.CurrentDomain.SetupInformation; AppDomainSetup ads = AppDomain.CurrentDomain.SetupInformation;

View File

@ -61,7 +61,7 @@ namespace OpenSim.Region.ScriptEngine.Common
m_localID = localID; m_localID = localID;
m_itemID = itemID; 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; private DateTime m_timer = DateTime.Now;
@ -1038,7 +1038,7 @@ namespace OpenSim.Region.ScriptEngine.Common
public void llSetTimerEvent(double sec) public void llSetTimerEvent(double sec)
{ {
// Setting timer repeat // 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) public void llSleep(double sec)

View File

@ -35,7 +35,7 @@ using OpenSim.Region.ScriptEngine.Common;
namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase 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 getLock = new object(); // Mutex
private object freeLock = new object(); // Mutex private object freeLock = new object(); // Mutex
//private ScriptEngine m_scriptEngine; private ScriptEngine m_scriptEngine;
//public AppDomainManager(ScriptEngine 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> /// <summary>
@ -99,7 +104,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
/// <returns>Free AppDomain</returns> /// <returns>Free AppDomain</returns>
private AppDomainStructure GetFreeAppDomain() private AppDomainStructure GetFreeAppDomain()
{ {
Console.WriteLine("Finding free AppDomain"); // Console.WriteLine("Finding free AppDomain");
lock (getLock) lock (getLock)
{ {
// Current full? // Current full?
@ -117,7 +122,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
currentAD.CurrentAppDomain = PrepareNewAppDomain(); currentAD.CurrentAppDomain = PrepareNewAppDomain();
} }
Console.WriteLine("Scripts loaded in this Appdomain: " + currentAD.ScriptsLoaded); // Console.WriteLine("Scripts loaded in this Appdomain: " + currentAD.ScriptsLoaded);
return currentAD; return currentAD;
} // lock } // lock
} }
@ -144,7 +149,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
ads.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile; ads.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
AppDomain AD = AppDomain.CreateDomain("ScriptAppDomain_" + AppDomainNameCount, null, ads); 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()); AssemblyName.GetAssemblyName("OpenSim.Region.ScriptEngine.Common.dll").ToString());
AD.Load(AssemblyName.GetAssemblyName("OpenSim.Region.ScriptEngine.Common.dll")); 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? // Is number of unloaded bigger or equal to number of loaded?
if (ads.ScriptsLoaded <= ads.ScriptsWaitingUnload) if (ads.ScriptsLoaded <= ads.ScriptsWaitingUnload)
{ {
Console.WriteLine("Found empty AppDomain, unloading");
// Remove from internal list // Remove from internal list
appDomains.Remove(ads); appDomains.Remove(ads);
#if DEBUG #if DEBUG
Console.WriteLine("Found empty AppDomain, unloading");
long m = GC.GetTotalMemory(true); long m = GC.GetTotalMemory(true);
#endif #endif
// Unload // Unload
AppDomain.Unload(ads.CurrentAppDomain); AppDomain.Unload(ads.CurrentAppDomain);
#if DEBUG #if DEBUG
Console.WriteLine("AppDomain unload freed " + (m - GC.GetTotalMemory(true)) + m_scriptEngine.Log.Verbose(m_scriptEngine.ScriptEngineName, "AppDomain unload freed " + (m - GC.GetTotalMemory(true)) + " bytes of memory");
" bytes of memory");
#endif #endif
} }
} }
@ -193,7 +197,9 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
// Find next available AppDomain to put it in // Find next available AppDomain to put it in
AppDomainStructure FreeAppDomain = GetFreeAppDomain(); 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 mbrt =
(IScript) (IScript)
FreeAppDomain.CurrentAppDomain.CreateInstanceFromAndUnwrap(FileName, "SecondLife.Script"); FreeAppDomain.CurrentAppDomain.CreateInstanceFromAndUnwrap(FileName, "SecondLife.Script");
@ -213,7 +219,9 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
{ {
lock (freeLock) 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 // Check if it is current AppDomain
if (currentAD.CurrentAppDomain == ad) if (currentAD.CurrentAppDomain == ad)
{ {
@ -236,5 +244,15 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
UnloadAppDomains(); // Outsite lock, has its own GetLock 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

@ -1,295 +1,313 @@
/* /*
* Copyright (c) Contributors, http://opensimulator.org/ * Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders. * See CONTRIBUTORS.TXT for a full list of copyright holders.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSim Project nor the * * Neither the name of the OpenSim Project nor the
* names of its contributors may be used to endorse or promote products * names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission. * derived from this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS AS IS AND ANY * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS AS IS AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
*/ */
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using libsecondlife; using libsecondlife;
using OpenSim.Region.Environment.Interfaces; using OpenSim.Region.Environment.Interfaces;
using OpenSim.Region.Environment.Modules; using OpenSim.Region.Environment.Modules;
namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
{ {
/// <summary> /// <summary>
/// Handles LSL commands that takes long time and returns an event, for example timers, HTTP requests, etc. /// Handles LSL commands that takes long time and returns an event, for example timers, HTTP requests, etc.
/// </summary> /// </summary>
public class LSLLongCmdHandler public class AsyncLSLCommandManager : iScriptEngineFunctionModule
{ {
private Thread cmdHandlerThread; private Thread cmdHandlerThread;
private int cmdHandlerThreadCycleSleepms = 100; private int cmdHandlerThreadCycleSleepms;
private ScriptEngine m_ScriptEngine; private ScriptEngine m_ScriptEngine;
public LSLLongCmdHandler(ScriptEngine _ScriptEngine) public AsyncLSLCommandManager(ScriptEngine _ScriptEngine)
{ {
m_ScriptEngine = _ScriptEngine; m_ScriptEngine = _ScriptEngine;
ReadConfig();
// Start the thread that will be doing the work
cmdHandlerThread = new Thread(CmdHandlerThreadLoop); // Start the thread that will be doing the work
cmdHandlerThread.Name = "CmdHandlerThread"; cmdHandlerThread = new Thread(CmdHandlerThreadLoop);
cmdHandlerThread.Priority = ThreadPriority.BelowNormal; cmdHandlerThread.Name = "CmdHandlerThread";
cmdHandlerThread.IsBackground = true; cmdHandlerThread.Priority = ThreadPriority.BelowNormal;
cmdHandlerThread.Start(); cmdHandlerThread.IsBackground = true;
} cmdHandlerThread.Start();
}
~LSLLongCmdHandler()
{ public void ReadConfig()
// Shut down thread {
try cmdHandlerThreadCycleSleepms = m_ScriptEngine.ScriptConfigSource.GetInt("AsyncLLCommandLoopms", 50);
{ }
if (cmdHandlerThread != null)
{
if (cmdHandlerThread.IsAlive == true) ~AsyncLSLCommandManager()
{ {
cmdHandlerThread.Abort(); // Shut down thread
cmdHandlerThread.Join(); try
} {
} if (cmdHandlerThread != null)
} {
catch if (cmdHandlerThread.IsAlive == true)
{ {
} cmdHandlerThread.Abort();
} cmdHandlerThread.Join();
}
private void CmdHandlerThreadLoop() }
{ }
while (true) catch
{ {
// Check timers }
CheckTimerEvents(); }
Thread.Sleep(25);
// Check HttpRequests private void CmdHandlerThreadLoop()
CheckHttpRequests(); {
Thread.Sleep(25); while (true)
// Check XMLRPCRequests {
CheckXMLRPCRequests(); // Check timers
Thread.Sleep(25); CheckTimerEvents();
// Check Listeners Thread.Sleep(25);
CheckListeners(); // Check HttpRequests
Thread.Sleep(25); CheckHttpRequests();
Thread.Sleep(25);
// Sleep before next cycle // Check XMLRPCRequests
//Thread.Sleep(cmdHandlerThreadCycleSleepms); CheckXMLRPCRequests();
} Thread.Sleep(25);
} // Check Listeners
CheckListeners();
/// <summary> Thread.Sleep(25);
/// Remove a specific script (and all its pending commands)
/// </summary> // Sleep before next cycle
/// <param name="m_localID"></param> //Thread.Sleep(cmdHandlerThreadCycleSleepms);
/// <param name="m_itemID"></param> }
public void RemoveScript(uint localID, LLUUID itemID) }
{
// Remove a specific script /// <summary>
/// Remove a specific script (and all its pending commands)
// Remove from: Timers /// </summary>
UnSetTimerEvents(localID, itemID); /// <param name="m_localID"></param>
// Remove from: HttpRequest /// <param name="m_itemID"></param>
IHttpRequests iHttpReq = public void RemoveScript(uint localID, LLUUID itemID)
m_ScriptEngine.World.RequestModuleInterface<IHttpRequests>(); {
iHttpReq.StopHttpRequest(localID, itemID); // Remove a specific script
}
// Remove from: Timers
#region TIMER UnSetTimerEvents(localID, itemID);
// Remove from: HttpRequest
// IHttpRequests iHttpReq =
// TIMER m_ScriptEngine.World.RequestModuleInterface<IHttpRequests>();
// iHttpReq.StopHttpRequest(localID, itemID);
private class TimerClass }
{
public uint localID; #region TIMER
public LLUUID itemID;
public double interval; //
public DateTime next; // TIMER
} //
private class TimerClass
private List<TimerClass> Timers = new List<TimerClass>(); {
private object TimerListLock = new object(); public uint localID;
public LLUUID itemID;
public void SetTimerEvent(uint m_localID, LLUUID m_itemID, double sec) public double interval;
{ public DateTime next;
Console.WriteLine("SetTimerEvent"); }
// Always remove first, in case this is a re-set private List<TimerClass> Timers = new List<TimerClass>();
UnSetTimerEvents(m_localID, m_itemID); private object TimerListLock = new object();
if (sec == 0) // Disabling timer
return; public void SetTimerEvent(uint m_localID, LLUUID m_itemID, double sec)
{
// Add to timer Console.WriteLine("SetTimerEvent");
TimerClass ts = new TimerClass();
ts.localID = m_localID; // Always remove first, in case this is a re-set
ts.itemID = m_itemID; UnSetTimerEvents(m_localID, m_itemID);
ts.interval = sec; if (sec == 0) // Disabling timer
ts.next = DateTime.Now.ToUniversalTime().AddSeconds(ts.interval); return;
lock (TimerListLock)
{ // Add to timer
Timers.Add(ts); TimerClass ts = new TimerClass();
} ts.localID = m_localID;
} ts.itemID = m_itemID;
ts.interval = sec;
public void UnSetTimerEvents(uint m_localID, LLUUID m_itemID) ts.next = DateTime.Now.ToUniversalTime().AddSeconds(ts.interval);
{ lock (TimerListLock)
// Remove from timer {
lock (TimerListLock) Timers.Add(ts);
{ }
List<TimerClass> NewTimers = new List<TimerClass>(); }
foreach (TimerClass ts in Timers)
{ public void UnSetTimerEvents(uint m_localID, LLUUID m_itemID)
if (ts.localID != m_localID && ts.itemID != m_itemID) {
{ // Remove from timer
NewTimers.Add(ts); lock (TimerListLock)
} {
} List<TimerClass> NewTimers = new List<TimerClass>();
Timers.Clear(); foreach (TimerClass ts in Timers)
Timers = NewTimers; {
} if (ts.localID != m_localID && ts.itemID != m_itemID)
} {
NewTimers.Add(ts);
public void CheckTimerEvents() }
{ }
// Nothing to do here? Timers.Clear();
if (Timers.Count == 0) Timers = NewTimers;
return; }
}
lock (TimerListLock)
{ public void CheckTimerEvents()
// Go through all timers {
foreach (TimerClass ts in Timers) // Nothing to do here?
{ if (Timers.Count == 0)
// Time has passed? return;
if (ts.next.ToUniversalTime() < DateTime.Now.ToUniversalTime())
{ lock (TimerListLock)
// Add it to queue {
m_ScriptEngine.m_EventQueueManager.AddToScriptQueue(ts.localID, ts.itemID, "timer", EventQueueManager.llDetectNull, // Go through all timers
new object[] {}); foreach (TimerClass ts in Timers)
// set next interval {
// Time has passed?
if (ts.next.ToUniversalTime() < DateTime.Now.ToUniversalTime())
ts.next = DateTime.Now.ToUniversalTime().AddSeconds(ts.interval); {
} // Add it to queue
} m_ScriptEngine.m_EventQueueManager.AddToScriptQueue(ts.localID, ts.itemID, "timer", EventQueueManager.llDetectNull,
} // lock new object[] {});
} // set next interval
#endregion
ts.next = DateTime.Now.ToUniversalTime().AddSeconds(ts.interval);
#region HTTP REQUEST }
}
public void CheckHttpRequests() } // lock
{ }
if (m_ScriptEngine.World == null)
return; #endregion
IHttpRequests iHttpReq = #region HTTP REQUEST
m_ScriptEngine.World.RequestModuleInterface<IHttpRequests>();
public void CheckHttpRequests()
HttpRequestClass httpInfo = null; {
if (m_ScriptEngine.World == null)
if (iHttpReq != null) return;
httpInfo = iHttpReq.GetNextCompletedRequest();
IHttpRequests iHttpReq =
while (httpInfo != null) m_ScriptEngine.World.RequestModuleInterface<IHttpRequests>();
{
//Console.WriteLine("PICKED HTTP REQ:" + httpInfo.response_body + httpInfo.status); HttpRequestClass httpInfo = null;
// Deliver data to prim's remote_data handler if (iHttpReq != null)
// httpInfo = iHttpReq.GetNextCompletedRequest();
// TODO: Returning null for metadata, since the lsl function
// only returns the byte for HTTP_BODY_TRUNCATED, which is not while (httpInfo != null)
// implemented here yet anyway. Should be fixed if/when maxsize {
// is supported //Console.WriteLine("PICKED HTTP REQ:" + httpInfo.response_body + httpInfo.status);
object[] resobj = new object[] // Deliver data to prim's remote_data handler
{ //
httpInfo.reqID.ToString(), httpInfo.status, null, httpInfo.response_body // TODO: Returning null for metadata, since the lsl function
}; // only returns the byte for HTTP_BODY_TRUNCATED, which is not
// implemented here yet anyway. Should be fixed if/when maxsize
m_ScriptEngine.m_EventQueueManager.AddToScriptQueue( // is supported
httpInfo.localID, httpInfo.itemID, "http_response", EventQueueManager.llDetectNull, resobj
); object[] resobj = new object[]
{
httpInfo.Stop(); httpInfo.reqID.ToString(), httpInfo.status, null, httpInfo.response_body
httpInfo = null; };
httpInfo = iHttpReq.GetNextCompletedRequest(); m_ScriptEngine.m_EventQueueManager.AddToScriptQueue(
} httpInfo.localID, httpInfo.itemID, "http_response", EventQueueManager.llDetectNull, resobj
} );
#endregion httpInfo.Stop();
httpInfo = null;
public void CheckXMLRPCRequests()
{ httpInfo = iHttpReq.GetNextCompletedRequest();
if (m_ScriptEngine.World == null) }
return; }
IXMLRPC xmlrpc = m_ScriptEngine.World.RequestModuleInterface<IXMLRPC>(); #endregion
if (xmlrpc != null) public void CheckXMLRPCRequests()
{ {
while (xmlrpc.hasRequests()) if (m_ScriptEngine.World == null)
{ return;
RPCRequestInfo rInfo = xmlrpc.GetNextRequest();
//Console.WriteLine("PICKED REQUEST"); IXMLRPC xmlrpc = m_ScriptEngine.World.RequestModuleInterface<IXMLRPC>();
//Deliver data to prim's remote_data handler if (xmlrpc != null)
object[] resobj = new object[] {
{ while (xmlrpc.hasRequests())
2, rInfo.GetChannelKey().ToString(), rInfo.GetMessageID().ToString(), String.Empty, {
rInfo.GetIntValue(), RPCRequestInfo rInfo = xmlrpc.GetNextRequest();
rInfo.GetStrVal() //Console.WriteLine("PICKED REQUEST");
};
m_ScriptEngine.m_EventQueueManager.AddToScriptQueue( //Deliver data to prim's remote_data handler
rInfo.GetLocalID(), rInfo.GetItemID(), "remote_data", EventQueueManager.llDetectNull, resobj object[] resobj = new object[]
); {
} 2, rInfo.GetChannelKey().ToString(), rInfo.GetMessageID().ToString(), String.Empty,
} rInfo.GetIntValue(),
} rInfo.GetStrVal()
};
public void CheckListeners() m_ScriptEngine.m_EventQueueManager.AddToScriptQueue(
{ rInfo.GetLocalID(), rInfo.GetItemID(), "remote_data", EventQueueManager.llDetectNull, resobj
if (m_ScriptEngine.World == null) );
return; }
IWorldComm comms = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>(); }
}
while (comms.HasMessages())
{ public void CheckListeners()
ListenerInfo lInfo = comms.GetNextMessage(); {
if (m_ScriptEngine.World == null)
//Deliver data to prim's listen handler return;
object[] resobj = new object[] IWorldComm comms = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>();
{
lInfo.GetChannel(), lInfo.GetName(), lInfo.GetID().ToString(), lInfo.GetMessage() while (comms.HasMessages())
}; {
ListenerInfo lInfo = comms.GetNextMessage();
m_ScriptEngine.m_EventQueueManager.AddToScriptQueue(
lInfo.GetLocalID(), lInfo.GetItemID(), "listen", EventQueueManager.llDetectNull, resobj //Deliver data to prim's listen handler
); object[] resobj = new object[]
} {
} lInfo.GetChannel(), lInfo.GetName(), lInfo.GetID().ToString(), lInfo.GetMessage()
} };
m_ScriptEngine.m_EventQueueManager.AddToScriptQueue(
lInfo.GetLocalID(), lInfo.GetItemID(), "listen", EventQueueManager.llDetectNull, resobj
);
}
}
/// <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) public static void SendToDebug(string Message)
{ {
//if (Debug == true) //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); //SendToDebugEvent("\r\n" + DateTime.Now.ToString("[HH:mm:ss] ") + Message);
} }
public static void SendToLog(string Message) public static void SendToLog(string Message)
{ {
//if (Debug == true) //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); //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. /// Prepares events so they can be directly executed upon a script by EventQueueManager, then queues it.
/// </summary> /// </summary>
[Serializable] [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) public EventManager(ScriptEngine _ScriptEngine, bool performHookUp)
{ {
myScriptEngine = _ScriptEngine; myScriptEngine = _ScriptEngine;
ReadConfig();
// Hook up to events from OpenSim // Hook up to events from OpenSim
// We may not want to do it because someone is controlling us and will deliver events to us // We may not want to do it because someone is controlling us and will deliver events to us
if (performHookUp) 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.OnObjectGrab += touch_start;
myScriptEngine.World.EventManager.OnRezScript += OnRezScript; myScriptEngine.World.EventManager.OnRezScript += OnRezScript;
myScriptEngine.World.EventManager.OnRemoveScript += OnRemoveScript; 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) public void changed(uint localID, uint change)
{ {
// Add to queue for all scripts in localID, Object pass 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); // 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 /// Events are queued and executed in separate thread
/// </summary> /// </summary>
[Serializable] [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); numberOfThreads = m_ScriptEngine.ScriptConfigSource.GetInt("NumberOfScriptThreads", 2);
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);
// Now refresh config in all threads
lock (eventQueueThreadsLock)
{
foreach (EventQueueThreadClass EventQueueThread in eventQueueThreads)
{
EventQueueThread.ReadConfig();
}
}
} }
#endregion #endregion
@ -222,7 +231,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
{ {
foreach (EventQueueThreadClass EventQueueThread in eventQueueThreads) foreach (EventQueueThreadClass EventQueueThread in eventQueueThreads)
{ {
EventQueueThread.Shutdown(); AbortThreadClass(EventQueueThread);
} }
eventQueueThreads.Clear(); eventQueueThreads.Clear();
staticGlobalEventQueueThreads.Clear(); staticGlobalEventQueueThreads.Clear();
@ -243,7 +252,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
EventQueueThreadClass eqtc = new EventQueueThreadClass(this); EventQueueThreadClass eqtc = new EventQueueThreadClass(this);
eventQueueThreads.Add(eqtc); eventQueueThreads.Add(eqtc);
staticGlobalEventQueueThreads.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) private void AbortThreadClass(EventQueueThreadClass threadClass)
@ -252,16 +261,17 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
eventQueueThreads.Remove(threadClass); eventQueueThreads.Remove(threadClass);
if (staticGlobalEventQueueThreads.Contains(threadClass)) if (staticGlobalEventQueueThreads.Contains(threadClass))
staticGlobalEventQueueThreads.Remove(threadClass); staticGlobalEventQueueThreads.Remove(threadClass);
try try
{ {
threadClass.Shutdown(); threadClass.Stop();
} }
catch (Exception ex) catch (Exception ex)
{ {
m_ScriptEngine.Log.Error("EventQueueManager", "If you see this, could you please report it to Tedd:"); m_ScriptEngine.Log.Error(m_ScriptEngine.ScriptEngineName + ":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", "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 #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) public void AddToObjectQueue(uint localID, string FunctionName, Queue_llDetectParams_Struct qParams, params object[] param)
{ {
// Determine all scripts in Object and add to their queue // 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 // Do we have any scripts in this object at all? If not, return
@ -367,6 +377,10 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
/// </summary> /// </summary>
public void AdjustNumberOfScriptThreads() public void AdjustNumberOfScriptThreads()
{ {
// Is there anything here for us to do?
if (eventQueueThreads.Count == numberOfThreads)
return;
lock (eventQueueThreadsLock) lock (eventQueueThreadsLock)
{ {
int diff = numberOfThreads - eventQueueThreads.Count; int diff = numberOfThreads - eventQueueThreads.Count;
@ -424,5 +438,15 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
} }
} }
#endregion #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> /// <summary>
/// Because every thread needs some data set for it (time started to execute current function), it will do its work within a class /// Because every thread needs some data set for it (time started to execute current function), it will do its work within a class
/// </summary> /// </summary>
public class EventQueueThreadClass public class EventQueueThreadClass: iScriptEngineFunctionModule
{ {
/// <summary> /// <summary>
/// How many ms to sleep if queue is empty /// How many ms to sleep if queue is empty
/// </summary> /// </summary>
private int nothingToDoSleepms;// = 50; private int nothingToDoSleepms;// = 50;
private ThreadPriority MyThreadPriority;
public long LastExecutionStarted; public long LastExecutionStarted;
public bool InExecution = false; public bool InExecution = false;
@ -26,25 +27,27 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
private EventQueueManager eventQueueManager; private EventQueueManager eventQueueManager;
public Thread EventQueueThread; public Thread EventQueueThread;
private static int ThreadCount = 0; private static int ThreadCount = 0;
private ThreadPriority MyThreadPriority;
private string ScriptEngineName = "ScriptEngine.Common";
public EventQueueThreadClass(EventQueueManager eqm) public EventQueueThreadClass(EventQueueManager eqm)
{ {
eventQueueManager = eqm; eventQueueManager = eqm;
nothingToDoSleepms = eqm.m_ScriptEngine.ScriptConfigSource.GetInt("SleepTimeIfNoScriptExecutionMs", 50); ReadConfig();
Start(); Start();
} }
~EventQueueThreadClass() ~EventQueueThreadClass()
{ {
Shutdown(); Stop();
} }
/// <summary>
/// Start thread public void ReadConfig()
/// </summary>
private void Start()
{ {
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 // 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"); string pri = eventQueueManager.m_ScriptEngine.ScriptConfigSource.GetString("ScriptThreadPriority", "BelowNormal");
switch (pri.ToLower()) switch (pri.ToLower())
@ -70,6 +73,19 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
break; 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 = new Thread(EventQueueThreadLoop);
EventQueueThread.IsBackground = true; EventQueueThread.IsBackground = true;
@ -84,18 +100,20 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
ThreadCount++; ThreadCount++;
} }
public void Shutdown() public void Stop()
{ {
PleaseShutdown = true; // Set shutdown flag
Thread.Sleep(100); // Wait a bit
if (EventQueueThread != null && EventQueueThread.IsAlive == true) if (EventQueueThread != null && EventQueueThread.IsAlive == true)
{ {
try try
{ {
EventQueueThread.Abort(); EventQueueThread.Abort(); // Send abort
EventQueueThread.Join(); EventQueueThread.Join(); // Wait for it
} }
catch (Exception) 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> /// </summary>
private void EventQueueThreadLoop() private void EventQueueThreadLoop()
{ {
//myScriptEngine.m_logger.Verbose("ScriptEngine", "EventQueueManager Worker thread spawned"); //myScriptEngine.m_logger.Verbose(ScriptEngineName, "EventQueueManager Worker thread spawned");
try try
{ {
while (true) while (true)
{ {
try try
{ {
@ -117,7 +135,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
while (true) while (true)
{ {
// Every now and then check if we should shut down // 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 // Someone should shut down, lets get exclusive lock
lock (eventQueueManager.ThreadsToExitLock) lock (eventQueueManager.ThreadsToExitLock)
@ -125,9 +143,15 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
// Lets re-check in case someone grabbed it // Lets re-check in case someone grabbed it
if (eventQueueManager.ThreadsToExit > 0) if (eventQueueManager.ThreadsToExit > 0)
{ {
// We are go for shutdown // Its crowded here so we'll shut down
eventQueueManager.ThreadsToExit--; eventQueueManager.ThreadsToExit--;
Shutdown(); Stop();
return;
}
else
{
// We have been asked to shut down
Stop();
return; return;
} }
} }
@ -139,6 +163,9 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
EventQueueManager.QueueItemStruct QIS = BlankQIS; EventQueueManager.QueueItemStruct QIS = BlankQIS;
bool GotItem = false; bool GotItem = false;
if (PleaseShutdown)
return;
if (eventQueueManager.eventQueue.Count == 0) if (eventQueueManager.eventQueue.Count == 0)
{ {
// Nothing to do? Sleep a bit waiting for something to do // Nothing to do? Sleep a bit waiting for something to do
@ -147,7 +174,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
else else
{ {
// Something in queue, process // 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 // OBJECT BASED LOCK - TWO THREADS WORKING ON SAME OBJECT IS NOT GOOD
lock (eventQueueManager.queueLock) lock (eventQueueManager.queueLock)
@ -179,7 +206,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
try try
{ {
#if DEBUG #if DEBUG
eventQueueManager.m_ScriptEngine.Log.Debug("ScriptEngine", eventQueueManager.m_ScriptEngine.Log.Debug(ScriptEngineName,
"Executing event:\r\n" "Executing event:\r\n"
+ "QIS.localID: " + QIS.localID + "QIS.localID: " + QIS.localID
+ ", QIS.itemID: " + QIS.itemID + ", QIS.itemID: " + QIS.itemID
@ -235,7 +262,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
//else //else
//{ //{
// T oconsole // T oconsole
eventQueueManager.m_ScriptEngine.Log.Error("ScriptEngine", eventQueueManager.m_ScriptEngine.Log.Error(ScriptEngineName,
"Unable to send text in-world:\r\n" + "Unable to send text in-world:\r\n" +
text); text);
} }
@ -260,19 +287,28 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
} }
catch (ThreadAbortException tae) 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) 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 } // while
} // try } // try
catch (ThreadAbortException) 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> /// <summary>
/// This class does maintenance on script engine. /// This class does maintenance on script engine.
/// </summary> /// </summary>
public class MaintenanceThread public class MaintenanceThread : iScriptEngineFunctionModule
{ {
public ScriptEngine m_ScriptEngine; public ScriptEngine m_ScriptEngine;
private int MaintenanceLoopms; private int MaintenanceLoopms;
@ -28,7 +28,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
StopMaintenanceThread(); StopMaintenanceThread();
} }
private void ReadConfig() public void ReadConfig()
{ {
MaintenanceLoopms = m_ScriptEngine.ScriptConfigSource.GetInt("MaintenanceLoopms", 50); MaintenanceLoopms = m_ScriptEngine.ScriptConfigSource.GetInt("MaintenanceLoopms", 50);
} }
@ -80,48 +80,74 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
/// </summary> /// </summary>
public void MaintenanceLoop() 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; try
long Last_ReReadConfigFilens = DateTime.Now.Ticks;
while (true)
{ {
System.Threading.Thread.Sleep(MaintenanceLoopms); // Sleep long Last_maxFunctionExecutionTimens = 0; // DateTime.Now.Ticks;
long Last_ReReadConfigFilens = DateTime.Now.Ticks;
// Re-reading config every x seconds? while (true)
if (m_ScriptEngine.ReReadConfigFileSeconds > 0)
{ {
// Check if its time to re-read config System.Threading.Thread.Sleep(MaintenanceLoopms); // Sleep before next pass
if (DateTime.Now.Ticks - Last_ReReadConfigFilens > m_ScriptEngine.ReReadConfigFilens) if (PleaseShutdown)
return;
//
// Re-reading config every x seconds
//
if (m_ScriptEngine.RefreshConfigFileSeconds > 0)
{ {
// Its time to re-read config file // Check if its time to re-read config
m_ScriptEngine.ConfigSource.Reload(); // Re-read config if (DateTime.Now.Ticks - Last_ReReadConfigFilens > m_ScriptEngine.RefreshConfigFilens)
Last_ReReadConfigFilens = DateTime.Now.Ticks; // Reset time {
// 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(); m_ScriptEngine.m_EventQueueManager.AdjustNumberOfScriptThreads();
}
//
// Check if any script has exceeded its max execution time // Check if any script has exceeded its max execution time
if (m_ScriptEngine.m_EventQueueManager.EnforceMaxExecutionTime) //
{ if (m_ScriptEngine.m_EventQueueManager.EnforceMaxExecutionTime)
if (DateTime.Now.Ticks - Last_maxFunctionExecutionTimens > m_ScriptEngine.m_EventQueueManager.maxFunctionExecutionTimens)
{ {
m_ScriptEngine.m_EventQueueManager.CheckScriptMaxExecTime(); // Do check // We are enforcing execution time
Last_maxFunctionExecutionTimens = DateTime.Now.Ticks; // Reset 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 (Exception ex)
catch (ThreadAbortException tae) {
{ m_ScriptEngine.Log.Error(m_ScriptEngine.ScriptEngineName, "Exception in MaintenanceLoopThread. Thread will recover after 5 sec throttle. Exception: " + ex.ToString());
Thread.Sleep(5000);
}
} }
} }
#endregion #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> /// </summary>
/// ///
[Serializable] [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 Scene World;
public EventManager m_EventManager; // Handles and queues incoming events from OpenSim public EventManager m_EventManager; // Handles and queues incoming events from OpenSim
public EventQueueManager m_EventQueueManager; // Executes events public EventQueueManager m_EventQueueManager; // Executes events, handles script threads
public ScriptManager m_ScriptManager; // Load, unload and execute scripts public ScriptManager m_ScriptManager; // Load, unload and execute scripts
public AppDomainManager m_AppDomainManager; public AppDomainManager m_AppDomainManager; // Handles loading/unloading of scripts into AppDomains
public LSLLongCmdHandler m_LSLLongCmdHandler; 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 IConfigSource ConfigSource;
public IConfig ScriptConfigSource; public IConfig ScriptConfigSource;
public abstract string ScriptConfigSourceName { get; } public abstract string ScriptEngineName { get; }
/// <summary> /// <summary>
/// How many seconds between re-reading config-file. 0 = never. ScriptEngine will try to adjust to new config changes. /// How many seconds between re-reading config-file. 0 = never. ScriptEngine will try to adjust to new config changes.
/// </summary> /// </summary>
public int ReReadConfigFileSeconds { public int RefreshConfigFileSeconds {
get { return (int)(ReReadConfigFilens / 10000); } get { return (int)(RefreshConfigFilens / 10000); }
set { ReReadConfigFilens = value * 10000; } set { RefreshConfigFilens = value * 10000; }
} }
public long ReReadConfigFilens = 0; public long RefreshConfigFilens = 0;
public ScriptManager GetScriptManager() public ScriptManager GetScriptManager()
{ {
@ -88,21 +89,22 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
{ {
World = Sceneworld; World = Sceneworld;
m_log = logger; 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 // 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);
m_ScriptManager = newScriptManager; m_ScriptManager = newScriptManager;
//m_ScriptManager = new ScriptManager(this); m_AppDomainManager = new AppDomainManager(this);
m_AppDomainManager = new AppDomainManager(ScriptConfigSource.GetInt("ScriptsPerAppDomain", 1)); m_ASYNCLSLCommandManager = new AsyncLSLCommandManager(this);
m_LSLLongCmdHandler = new LSLLongCmdHandler(this); m_MaintenanceThread = new MaintenanceThread(this);
ReadConfig();
ReReadConfigFileSeconds = ScriptConfigSource.GetInt("ReReadConfig", 0);
// Should we iterate the region for scripts that needs starting? // Should we iterate the region for scripts that needs starting?
@ -118,6 +120,26 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
{ {
return this.m_EventManager; 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 #region IRegionModule
@ -134,7 +156,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
public string Name public string Name
{ {
get { return "DotNetEngine"; } get { return "Common." + ScriptEngineName; }
} }
public bool IsSharedModule public bool IsSharedModule
@ -146,5 +168,15 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
#endregion #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. // This so that scripts starting or stopping will not slow down other theads or whole system.
// //
[Serializable] [Serializable]
public abstract class ScriptManager public abstract class ScriptManager : iScriptEngineFunctionModule
{ {
#region Declares #region Declares
private Thread scriptLoadUnloadThread; private Thread scriptLoadUnloadThread;
private int scriptLoadUnloadThread_IdleSleepms = 100; private int scriptLoadUnloadThread_IdleSleepms;
private Queue<LUStruct> LUQueue = new Queue<LUStruct>(); private Queue<LUStruct> LUQueue = new Queue<LUStruct>();
@ -95,6 +95,11 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
#endregion #endregion
public void ReadConfig()
{
scriptLoadUnloadThread_IdleSleepms = m_scriptEngine.ScriptConfigSource.GetInt("ScriptLoadUnloadLoopms", 30);
}
#region Object init/shutdown #region Object init/shutdown
public ScriptEngineBase.ScriptEngine m_scriptEngine; public ScriptEngineBase.ScriptEngine m_scriptEngine;
@ -102,6 +107,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
public ScriptManager(ScriptEngineBase.ScriptEngine scriptEngine) public ScriptManager(ScriptEngineBase.ScriptEngine scriptEngine)
{ {
m_scriptEngine = scriptEngine; m_scriptEngine = scriptEngine;
ReadConfig();
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
scriptLoadUnloadThread = new Thread(ScriptLoadUnloadThreadLoop); scriptLoadUnloadThread = new Thread(ScriptLoadUnloadThreadLoop);
scriptLoadUnloadThread.Name = "ScriptLoadUnloadThread"; scriptLoadUnloadThread.Name = "ScriptLoadUnloadThread";
@ -238,7 +244,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
Console.WriteLine("ScriptEngine: Inside ExecuteEvent for event " + FunctionName); Console.WriteLine("ScriptEngine: Inside ExecuteEvent for event " + FunctionName);
#endif #endif
// Execute a function in the script // 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); //ScriptBaseInterface Script = (ScriptBaseInterface)GetScript(localID, itemID);
IScript Script = GetScript(localID, itemID); IScript Script = GetScript(localID, itemID);
if (Script == null) if (Script == null)
@ -345,5 +351,16 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
} }
#endregion #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); return new ScriptManager(this);
} }
public override string ScriptConfigSourceName public override string ScriptEngineName
{ {
get { return "ScriptEngine.DotNetEngine"; } get { return "ScriptEngine.DotNetEngine"; }
} }

View File

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

View File

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

View File

@ -127,7 +127,7 @@ namespace OpenSim.Region.ScriptEngine.LSOEngine
// Stop long command on script // Stop long command on script
m_scriptEngine.m_LSLLongCmdHandler.RemoveScript(localID, itemID); m_scriptEngine.m_ASYNCLSLCommandManager.RemoveScript(localID, itemID);
IScript LSLBC = GetScript(localID, itemID); IScript LSLBC = GetScript(localID, itemID);
if (LSLBC == null) 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. ; 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. ; 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 ; Number of threads to use for script event execution
; Threads are shared across all regions ; Threads are shared across all regions
NumberOfScriptThreads=2 NumberOfScriptThreads=2
@ -136,6 +143,7 @@ ScriptThreadPriority=BelowNormal
; Number of threads will be <NumberOfScriptThreads>*<NumberOfRegions> ; Number of threads will be <NumberOfScriptThreads>*<NumberOfRegions>
; false: All regions share <NumberOfScriptThreads> for all their scripts ; 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. ; 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 PrivateRegionThreads=false
; How long MAX should a script event be allowed to run (per event execution)? ; 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. ; Each AppDomain has some memory overhead. But leaving dead scripts in memory also has memory overhead.
ScriptsPerAppDomain=1 ScriptsPerAppDomain=1
; ReRead ScriptEngine config options how often? ; Script loading / unloading sleep
ReReadConfig=0 ; 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