244 lines
11 KiB
C#
244 lines
11 KiB
C#
/*
|
|
* 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;
|
|
using System.Reflection;
|
|
using System.Threading;
|
|
using log4net;
|
|
using OpenSim.Framework;
|
|
|
|
namespace OpenSim.Region.ScriptEngine.DotNetEngine
|
|
{
|
|
/// <summary>
|
|
/// This class does maintenance on script engine.
|
|
/// </summary>
|
|
public class MaintenanceThread
|
|
{
|
|
|
|
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
|
|
|
//public ScriptEngine m_ScriptEngine;
|
|
private int MaintenanceLoopms;
|
|
private int MaintenanceLoopTicks_ScriptLoadUnload;
|
|
private int MaintenanceLoopTicks_Other;
|
|
|
|
|
|
public MaintenanceThread()
|
|
{
|
|
//m_ScriptEngine = _ScriptEngine;
|
|
|
|
ReadConfig();
|
|
|
|
// Start maintenance thread
|
|
StartMaintenanceThread();
|
|
}
|
|
|
|
~MaintenanceThread()
|
|
{
|
|
StopMaintenanceThread();
|
|
}
|
|
|
|
public void ReadConfig()
|
|
{
|
|
// Bad hack, but we need a m_ScriptEngine :)
|
|
lock (ScriptEngine.ScriptEngines)
|
|
{
|
|
foreach (ScriptEngine m_ScriptEngine in ScriptEngine.ScriptEngines)
|
|
{
|
|
MaintenanceLoopms = m_ScriptEngine.ScriptConfigSource.GetInt("MaintenanceLoopms", 50);
|
|
MaintenanceLoopTicks_ScriptLoadUnload =
|
|
m_ScriptEngine.ScriptConfigSource.GetInt("MaintenanceLoopTicks_ScriptLoadUnload", 1);
|
|
MaintenanceLoopTicks_Other =
|
|
m_ScriptEngine.ScriptConfigSource.GetInt("MaintenanceLoopTicks_Other", 10);
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
#region " Maintenance thread "
|
|
/// <summary>
|
|
/// Maintenance thread. Enforcing max execution time for example.
|
|
/// </summary>
|
|
public Thread MaintenanceThreadThread;
|
|
|
|
/// <summary>
|
|
/// Starts maintenance thread
|
|
/// </summary>
|
|
private void StartMaintenanceThread()
|
|
{
|
|
if (MaintenanceThreadThread == null)
|
|
{
|
|
MaintenanceThreadThread = new Thread(MaintenanceLoop);
|
|
MaintenanceThreadThread.Name = "ScriptMaintenanceThread";
|
|
MaintenanceThreadThread.IsBackground = true;
|
|
MaintenanceThreadThread.Start();
|
|
ThreadTracker.Add(MaintenanceThreadThread);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stops maintenance thread
|
|
/// </summary>
|
|
private void StopMaintenanceThread()
|
|
{
|
|
#if DEBUG
|
|
//m_ScriptEngine.Log.Debug("[" + m_ScriptEngine.ScriptEngineName + "]: StopMaintenanceThread() called");
|
|
#endif
|
|
//PleaseShutdown = true;
|
|
Thread.Sleep(100);
|
|
try
|
|
{
|
|
if (MaintenanceThreadThread != null && MaintenanceThreadThread.IsAlive)
|
|
{
|
|
MaintenanceThreadThread.Abort();
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
//m_ScriptEngine.Log.Error("[" + m_ScriptEngine.ScriptEngineName + "]: Exception stopping maintenence thread: " + ex.ToString());
|
|
}
|
|
}
|
|
|
|
// private ScriptEngine lastScriptEngine; // Keep track of what ScriptEngine instance we are at so we can give exception
|
|
/// <summary>
|
|
/// A thread should run in this loop and check all running scripts
|
|
/// </summary>
|
|
public void MaintenanceLoop()
|
|
{
|
|
//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.");
|
|
|
|
long Last_maxFunctionExecutionTimens = 0; // DateTime.Now.Ticks;
|
|
long Last_ReReadConfigFilens = DateTime.Now.Ticks;
|
|
int MaintenanceLoopTicks_ScriptLoadUnload_Count = 0;
|
|
int MaintenanceLoopTicks_Other_Count = 0;
|
|
bool MaintenanceLoopTicks_ScriptLoadUnload_ResetCount = false;
|
|
bool MaintenanceLoopTicks_Other_ResetCount = false;
|
|
|
|
while (true)
|
|
{
|
|
try
|
|
{
|
|
while (true)
|
|
{
|
|
Thread.Sleep(MaintenanceLoopms); // Sleep before next pass
|
|
|
|
// Reset counters?
|
|
if (MaintenanceLoopTicks_ScriptLoadUnload_ResetCount)
|
|
{
|
|
MaintenanceLoopTicks_ScriptLoadUnload_ResetCount = false;
|
|
MaintenanceLoopTicks_ScriptLoadUnload_Count = 0;
|
|
}
|
|
if (MaintenanceLoopTicks_Other_ResetCount)
|
|
{
|
|
MaintenanceLoopTicks_Other_ResetCount = false;
|
|
MaintenanceLoopTicks_Other_Count = 0;
|
|
}
|
|
|
|
// Increase our counters
|
|
MaintenanceLoopTicks_ScriptLoadUnload_Count++;
|
|
MaintenanceLoopTicks_Other_Count++;
|
|
|
|
|
|
//lock (ScriptEngine.ScriptEngines)
|
|
//{
|
|
foreach (ScriptEngine m_ScriptEngine in new ArrayList(ScriptEngine.ScriptEngines))
|
|
{
|
|
// lastScriptEngine = m_ScriptEngine;
|
|
// Re-reading config every x seconds
|
|
if (MaintenanceLoopTicks_Other_Count >= MaintenanceLoopTicks_Other)
|
|
{
|
|
MaintenanceLoopTicks_Other_ResetCount = true;
|
|
if (m_ScriptEngine.RefreshConfigFilens > 0)
|
|
{
|
|
// Check if its time to re-read config
|
|
if (DateTime.Now.Ticks - Last_ReReadConfigFilens >
|
|
m_ScriptEngine.RefreshConfigFilens)
|
|
{
|
|
//Console.WriteLine("Time passed: " + (DateTime.Now.Ticks - Last_ReReadConfigFilens) + ">" + m_ScriptEngine.RefreshConfigFilens);
|
|
// Its time to re-read config file
|
|
m_ScriptEngine.ReadConfig();
|
|
Last_ReReadConfigFilens = DateTime.Now.Ticks; // Reset time
|
|
}
|
|
|
|
|
|
// Adjust number of running script threads if not correct
|
|
if (m_ScriptEngine.m_EventQueueManager != null)
|
|
m_ScriptEngine.m_EventQueueManager.AdjustNumberOfScriptThreads();
|
|
|
|
// Check if any script has exceeded its max execution time
|
|
if (EventQueueManager.EnforceMaxExecutionTime)
|
|
{
|
|
// We are enforcing execution time
|
|
if (DateTime.Now.Ticks - Last_maxFunctionExecutionTimens >
|
|
EventQueueManager.maxFunctionExecutionTimens)
|
|
{
|
|
// Its time to check again
|
|
m_ScriptEngine.m_EventQueueManager.CheckScriptMaxExecTime(); // Do check
|
|
Last_maxFunctionExecutionTimens = DateTime.Now.Ticks; // Reset time
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (MaintenanceLoopTicks_ScriptLoadUnload_Count >= MaintenanceLoopTicks_ScriptLoadUnload)
|
|
{
|
|
MaintenanceLoopTicks_ScriptLoadUnload_ResetCount = true;
|
|
// LOAD / UNLOAD SCRIPTS
|
|
if (m_ScriptEngine.m_ScriptManager != null)
|
|
m_ScriptEngine.m_ScriptManager.DoScriptLoadUnload();
|
|
}
|
|
}
|
|
//}
|
|
}
|
|
}
|
|
catch(ThreadAbortException)
|
|
{
|
|
m_log.Error("Thread aborted in MaintenanceLoopThread. If this is during shutdown, please ignore");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
m_log.ErrorFormat("Exception in MaintenanceLoopThread. Thread will recover after 5 sec throttle. Exception: {0}", ex.ToString());
|
|
}
|
|
}
|
|
}
|
|
#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;
|
|
}
|
|
}
|