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 MaintenanceThreadThreadPoolClientBranch
parent
a6726b0c9d
commit
d02a90823f
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
|
@ -1,295 +1,313 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSim Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS AS IS AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* 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
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using libsecondlife;
|
||||
using OpenSim.Region.Environment.Interfaces;
|
||||
using OpenSim.Region.Environment.Modules;
|
||||
|
||||
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
|
||||
{
|
||||
private Thread cmdHandlerThread;
|
||||
private int cmdHandlerThreadCycleSleepms = 100;
|
||||
|
||||
private ScriptEngine m_ScriptEngine;
|
||||
|
||||
public LSLLongCmdHandler(ScriptEngine _ScriptEngine)
|
||||
{
|
||||
m_ScriptEngine = _ScriptEngine;
|
||||
|
||||
// Start the thread that will be doing the work
|
||||
cmdHandlerThread = new Thread(CmdHandlerThreadLoop);
|
||||
cmdHandlerThread.Name = "CmdHandlerThread";
|
||||
cmdHandlerThread.Priority = ThreadPriority.BelowNormal;
|
||||
cmdHandlerThread.IsBackground = true;
|
||||
cmdHandlerThread.Start();
|
||||
}
|
||||
|
||||
~LSLLongCmdHandler()
|
||||
{
|
||||
// Shut down thread
|
||||
try
|
||||
{
|
||||
if (cmdHandlerThread != null)
|
||||
{
|
||||
if (cmdHandlerThread.IsAlive == true)
|
||||
{
|
||||
cmdHandlerThread.Abort();
|
||||
cmdHandlerThread.Join();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void CmdHandlerThreadLoop()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
// Check timers
|
||||
CheckTimerEvents();
|
||||
Thread.Sleep(25);
|
||||
// Check HttpRequests
|
||||
CheckHttpRequests();
|
||||
Thread.Sleep(25);
|
||||
// Check XMLRPCRequests
|
||||
CheckXMLRPCRequests();
|
||||
Thread.Sleep(25);
|
||||
// Check Listeners
|
||||
CheckListeners();
|
||||
Thread.Sleep(25);
|
||||
|
||||
// Sleep before next cycle
|
||||
//Thread.Sleep(cmdHandlerThreadCycleSleepms);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a specific script (and all its pending commands)
|
||||
/// </summary>
|
||||
/// <param name="m_localID"></param>
|
||||
/// <param name="m_itemID"></param>
|
||||
public void RemoveScript(uint localID, LLUUID itemID)
|
||||
{
|
||||
// Remove a specific script
|
||||
|
||||
// Remove from: Timers
|
||||
UnSetTimerEvents(localID, itemID);
|
||||
// Remove from: HttpRequest
|
||||
IHttpRequests iHttpReq =
|
||||
m_ScriptEngine.World.RequestModuleInterface<IHttpRequests>();
|
||||
iHttpReq.StopHttpRequest(localID, itemID);
|
||||
}
|
||||
|
||||
#region TIMER
|
||||
|
||||
//
|
||||
// TIMER
|
||||
//
|
||||
private class TimerClass
|
||||
{
|
||||
public uint localID;
|
||||
public LLUUID itemID;
|
||||
public double interval;
|
||||
public DateTime next;
|
||||
}
|
||||
|
||||
private List<TimerClass> Timers = new List<TimerClass>();
|
||||
private object TimerListLock = new object();
|
||||
|
||||
public void SetTimerEvent(uint m_localID, LLUUID m_itemID, double sec)
|
||||
{
|
||||
Console.WriteLine("SetTimerEvent");
|
||||
|
||||
// Always remove first, in case this is a re-set
|
||||
UnSetTimerEvents(m_localID, m_itemID);
|
||||
if (sec == 0) // Disabling timer
|
||||
return;
|
||||
|
||||
// Add to timer
|
||||
TimerClass ts = new TimerClass();
|
||||
ts.localID = m_localID;
|
||||
ts.itemID = m_itemID;
|
||||
ts.interval = sec;
|
||||
ts.next = DateTime.Now.ToUniversalTime().AddSeconds(ts.interval);
|
||||
lock (TimerListLock)
|
||||
{
|
||||
Timers.Add(ts);
|
||||
}
|
||||
}
|
||||
|
||||
public void UnSetTimerEvents(uint m_localID, LLUUID m_itemID)
|
||||
{
|
||||
// Remove from timer
|
||||
lock (TimerListLock)
|
||||
{
|
||||
List<TimerClass> NewTimers = new List<TimerClass>();
|
||||
foreach (TimerClass ts in Timers)
|
||||
{
|
||||
if (ts.localID != m_localID && ts.itemID != m_itemID)
|
||||
{
|
||||
NewTimers.Add(ts);
|
||||
}
|
||||
}
|
||||
Timers.Clear();
|
||||
Timers = NewTimers;
|
||||
}
|
||||
}
|
||||
|
||||
public void CheckTimerEvents()
|
||||
{
|
||||
// Nothing to do here?
|
||||
if (Timers.Count == 0)
|
||||
return;
|
||||
|
||||
lock (TimerListLock)
|
||||
{
|
||||
// Go through all timers
|
||||
foreach (TimerClass ts in Timers)
|
||||
{
|
||||
// Time has passed?
|
||||
if (ts.next.ToUniversalTime() < DateTime.Now.ToUniversalTime())
|
||||
{
|
||||
// Add it to queue
|
||||
m_ScriptEngine.m_EventQueueManager.AddToScriptQueue(ts.localID, ts.itemID, "timer", EventQueueManager.llDetectNull,
|
||||
new object[] {});
|
||||
// set next interval
|
||||
|
||||
|
||||
ts.next = DateTime.Now.ToUniversalTime().AddSeconds(ts.interval);
|
||||
}
|
||||
}
|
||||
} // lock
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region HTTP REQUEST
|
||||
|
||||
public void CheckHttpRequests()
|
||||
{
|
||||
if (m_ScriptEngine.World == null)
|
||||
return;
|
||||
|
||||
IHttpRequests iHttpReq =
|
||||
m_ScriptEngine.World.RequestModuleInterface<IHttpRequests>();
|
||||
|
||||
HttpRequestClass httpInfo = null;
|
||||
|
||||
if (iHttpReq != null)
|
||||
httpInfo = iHttpReq.GetNextCompletedRequest();
|
||||
|
||||
while (httpInfo != null)
|
||||
{
|
||||
//Console.WriteLine("PICKED HTTP REQ:" + httpInfo.response_body + httpInfo.status);
|
||||
|
||||
// Deliver data to prim's remote_data handler
|
||||
//
|
||||
// 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
|
||||
// is supported
|
||||
|
||||
object[] resobj = new object[]
|
||||
{
|
||||
httpInfo.reqID.ToString(), httpInfo.status, null, httpInfo.response_body
|
||||
};
|
||||
|
||||
m_ScriptEngine.m_EventQueueManager.AddToScriptQueue(
|
||||
httpInfo.localID, httpInfo.itemID, "http_response", EventQueueManager.llDetectNull, resobj
|
||||
);
|
||||
|
||||
httpInfo.Stop();
|
||||
httpInfo = null;
|
||||
|
||||
httpInfo = iHttpReq.GetNextCompletedRequest();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void CheckXMLRPCRequests()
|
||||
{
|
||||
if (m_ScriptEngine.World == null)
|
||||
return;
|
||||
|
||||
IXMLRPC xmlrpc = m_ScriptEngine.World.RequestModuleInterface<IXMLRPC>();
|
||||
|
||||
if (xmlrpc != null)
|
||||
{
|
||||
while (xmlrpc.hasRequests())
|
||||
{
|
||||
RPCRequestInfo rInfo = xmlrpc.GetNextRequest();
|
||||
//Console.WriteLine("PICKED REQUEST");
|
||||
|
||||
//Deliver data to prim's remote_data handler
|
||||
object[] resobj = new object[]
|
||||
{
|
||||
2, rInfo.GetChannelKey().ToString(), rInfo.GetMessageID().ToString(), String.Empty,
|
||||
rInfo.GetIntValue(),
|
||||
rInfo.GetStrVal()
|
||||
};
|
||||
m_ScriptEngine.m_EventQueueManager.AddToScriptQueue(
|
||||
rInfo.GetLocalID(), rInfo.GetItemID(), "remote_data", EventQueueManager.llDetectNull, resobj
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CheckListeners()
|
||||
{
|
||||
if (m_ScriptEngine.World == null)
|
||||
return;
|
||||
IWorldComm comms = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>();
|
||||
|
||||
while (comms.HasMessages())
|
||||
{
|
||||
ListenerInfo lInfo = comms.GetNextMessage();
|
||||
|
||||
//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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSim Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS AS IS AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* 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
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using libsecondlife;
|
||||
using OpenSim.Region.Environment.Interfaces;
|
||||
using OpenSim.Region.Environment.Modules;
|
||||
|
||||
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 AsyncLSLCommandManager : iScriptEngineFunctionModule
|
||||
{
|
||||
private Thread cmdHandlerThread;
|
||||
private int cmdHandlerThreadCycleSleepms;
|
||||
|
||||
private ScriptEngine m_ScriptEngine;
|
||||
|
||||
public AsyncLSLCommandManager(ScriptEngine _ScriptEngine)
|
||||
{
|
||||
m_ScriptEngine = _ScriptEngine;
|
||||
ReadConfig();
|
||||
|
||||
// Start the thread that will be doing the work
|
||||
cmdHandlerThread = new Thread(CmdHandlerThreadLoop);
|
||||
cmdHandlerThread.Name = "CmdHandlerThread";
|
||||
cmdHandlerThread.Priority = ThreadPriority.BelowNormal;
|
||||
cmdHandlerThread.IsBackground = true;
|
||||
cmdHandlerThread.Start();
|
||||
}
|
||||
|
||||
public void ReadConfig()
|
||||
{
|
||||
cmdHandlerThreadCycleSleepms = m_ScriptEngine.ScriptConfigSource.GetInt("AsyncLLCommandLoopms", 50);
|
||||
}
|
||||
|
||||
|
||||
~AsyncLSLCommandManager()
|
||||
{
|
||||
// Shut down thread
|
||||
try
|
||||
{
|
||||
if (cmdHandlerThread != null)
|
||||
{
|
||||
if (cmdHandlerThread.IsAlive == true)
|
||||
{
|
||||
cmdHandlerThread.Abort();
|
||||
cmdHandlerThread.Join();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void CmdHandlerThreadLoop()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
// Check timers
|
||||
CheckTimerEvents();
|
||||
Thread.Sleep(25);
|
||||
// Check HttpRequests
|
||||
CheckHttpRequests();
|
||||
Thread.Sleep(25);
|
||||
// Check XMLRPCRequests
|
||||
CheckXMLRPCRequests();
|
||||
Thread.Sleep(25);
|
||||
// Check Listeners
|
||||
CheckListeners();
|
||||
Thread.Sleep(25);
|
||||
|
||||
// Sleep before next cycle
|
||||
//Thread.Sleep(cmdHandlerThreadCycleSleepms);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a specific script (and all its pending commands)
|
||||
/// </summary>
|
||||
/// <param name="m_localID"></param>
|
||||
/// <param name="m_itemID"></param>
|
||||
public void RemoveScript(uint localID, LLUUID itemID)
|
||||
{
|
||||
// Remove a specific script
|
||||
|
||||
// Remove from: Timers
|
||||
UnSetTimerEvents(localID, itemID);
|
||||
// Remove from: HttpRequest
|
||||
IHttpRequests iHttpReq =
|
||||
m_ScriptEngine.World.RequestModuleInterface<IHttpRequests>();
|
||||
iHttpReq.StopHttpRequest(localID, itemID);
|
||||
}
|
||||
|
||||
#region TIMER
|
||||
|
||||
//
|
||||
// TIMER
|
||||
//
|
||||
private class TimerClass
|
||||
{
|
||||
public uint localID;
|
||||
public LLUUID itemID;
|
||||
public double interval;
|
||||
public DateTime next;
|
||||
}
|
||||
|
||||
private List<TimerClass> Timers = new List<TimerClass>();
|
||||
private object TimerListLock = new object();
|
||||
|
||||
public void SetTimerEvent(uint m_localID, LLUUID m_itemID, double sec)
|
||||
{
|
||||
Console.WriteLine("SetTimerEvent");
|
||||
|
||||
// Always remove first, in case this is a re-set
|
||||
UnSetTimerEvents(m_localID, m_itemID);
|
||||
if (sec == 0) // Disabling timer
|
||||
return;
|
||||
|
||||
// Add to timer
|
||||
TimerClass ts = new TimerClass();
|
||||
ts.localID = m_localID;
|
||||
ts.itemID = m_itemID;
|
||||
ts.interval = sec;
|
||||
ts.next = DateTime.Now.ToUniversalTime().AddSeconds(ts.interval);
|
||||
lock (TimerListLock)
|
||||
{
|
||||
Timers.Add(ts);
|
||||
}
|
||||
}
|
||||
|
||||
public void UnSetTimerEvents(uint m_localID, LLUUID m_itemID)
|
||||
{
|
||||
// Remove from timer
|
||||
lock (TimerListLock)
|
||||
{
|
||||
List<TimerClass> NewTimers = new List<TimerClass>();
|
||||
foreach (TimerClass ts in Timers)
|
||||
{
|
||||
if (ts.localID != m_localID && ts.itemID != m_itemID)
|
||||
{
|
||||
NewTimers.Add(ts);
|
||||
}
|
||||
}
|
||||
Timers.Clear();
|
||||
Timers = NewTimers;
|
||||
}
|
||||
}
|
||||
|
||||
public void CheckTimerEvents()
|
||||
{
|
||||
// Nothing to do here?
|
||||
if (Timers.Count == 0)
|
||||
return;
|
||||
|
||||
lock (TimerListLock)
|
||||
{
|
||||
// Go through all timers
|
||||
foreach (TimerClass ts in Timers)
|
||||
{
|
||||
// Time has passed?
|
||||
if (ts.next.ToUniversalTime() < DateTime.Now.ToUniversalTime())
|
||||
{
|
||||
// Add it to queue
|
||||
m_ScriptEngine.m_EventQueueManager.AddToScriptQueue(ts.localID, ts.itemID, "timer", EventQueueManager.llDetectNull,
|
||||
new object[] {});
|
||||
// set next interval
|
||||
|
||||
|
||||
ts.next = DateTime.Now.ToUniversalTime().AddSeconds(ts.interval);
|
||||
}
|
||||
}
|
||||
} // lock
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region HTTP REQUEST
|
||||
|
||||
public void CheckHttpRequests()
|
||||
{
|
||||
if (m_ScriptEngine.World == null)
|
||||
return;
|
||||
|
||||
IHttpRequests iHttpReq =
|
||||
m_ScriptEngine.World.RequestModuleInterface<IHttpRequests>();
|
||||
|
||||
HttpRequestClass httpInfo = null;
|
||||
|
||||
if (iHttpReq != null)
|
||||
httpInfo = iHttpReq.GetNextCompletedRequest();
|
||||
|
||||
while (httpInfo != null)
|
||||
{
|
||||
//Console.WriteLine("PICKED HTTP REQ:" + httpInfo.response_body + httpInfo.status);
|
||||
|
||||
// Deliver data to prim's remote_data handler
|
||||
//
|
||||
// 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
|
||||
// is supported
|
||||
|
||||
object[] resobj = new object[]
|
||||
{
|
||||
httpInfo.reqID.ToString(), httpInfo.status, null, httpInfo.response_body
|
||||
};
|
||||
|
||||
m_ScriptEngine.m_EventQueueManager.AddToScriptQueue(
|
||||
httpInfo.localID, httpInfo.itemID, "http_response", EventQueueManager.llDetectNull, resobj
|
||||
);
|
||||
|
||||
httpInfo.Stop();
|
||||
httpInfo = null;
|
||||
|
||||
httpInfo = iHttpReq.GetNextCompletedRequest();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void CheckXMLRPCRequests()
|
||||
{
|
||||
if (m_ScriptEngine.World == null)
|
||||
return;
|
||||
|
||||
IXMLRPC xmlrpc = m_ScriptEngine.World.RequestModuleInterface<IXMLRPC>();
|
||||
|
||||
if (xmlrpc != null)
|
||||
{
|
||||
while (xmlrpc.hasRequests())
|
||||
{
|
||||
RPCRequestInfo rInfo = xmlrpc.GetNextRequest();
|
||||
//Console.WriteLine("PICKED REQUEST");
|
||||
|
||||
//Deliver data to prim's remote_data handler
|
||||
object[] resobj = new object[]
|
||||
{
|
||||
2, rInfo.GetChannelKey().ToString(), rInfo.GetMessageID().ToString(), String.Empty,
|
||||
rInfo.GetIntValue(),
|
||||
rInfo.GetStrVal()
|
||||
};
|
||||
m_ScriptEngine.m_EventQueueManager.AddToScriptQueue(
|
||||
rInfo.GetLocalID(), rInfo.GetItemID(), "remote_data", EventQueueManager.llDetectNull, resobj
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CheckListeners()
|
||||
{
|
||||
if (m_ScriptEngine.World == null)
|
||||
return;
|
||||
IWorldComm comms = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>();
|
||||
|
||||
while (comms.HasMessages())
|
||||
{
|
||||
ListenerInfo lInfo = comms.GetNextMessage();
|
||||
|
||||
//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;
|
||||
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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"; }
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"; }
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue