From 5d6e89eaf924e741be95248d3ce5bc4ddbd5de3c Mon Sep 17 00:00:00 2001
From: Tedd Hansen
Date: Fri, 1 Feb 2008 19:07:05 +0000
Subject: Highly experimental A separate thread is used to enforce max function
(event) execution time for scripts.
---
.../ScriptEngineBase/EventQueueThreadClass.cs | 201 +++++++++++++++++++++
1 file changed, 201 insertions(+)
create mode 100644 OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueThreadClass.cs
(limited to 'OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueThreadClass.cs')
diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueThreadClass.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueThreadClass.cs
new file mode 100644
index 0000000..ad79fbc
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueThreadClass.cs
@@ -0,0 +1,201 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+using libsecondlife;
+using OpenSim.Framework;
+using OpenSim.Region.Environment.Scenes.Scripting;
+
+namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
+{
+ ///
+ /// Because every thread needs some data set for it (time started to execute current function), it will do its work within a class
+ ///
+ public class EventQueueThreadClass
+ {
+ ///
+ /// How many ms to sleep if queue is empty
+ ///
+ private int nothingToDoSleepms = 50;
+
+ public DateTime LastExecutionStarted;
+ public bool InExecution = false;
+
+ private EventQueueManager eventQueueManager;
+ public Thread EventQueueThread;
+ private static int ThreadCount = 0;
+
+ public EventQueueThreadClass(EventQueueManager eqm)
+ {
+ eventQueueManager = eqm;
+ Start();
+ }
+
+ ~EventQueueThreadClass()
+ {
+ Shutdown();
+ }
+
+ ///
+ /// Start thread
+ ///
+ private void Start()
+ {
+ EventQueueThread = new Thread(EventQueueThreadLoop);
+ EventQueueThread.IsBackground = true;
+ EventQueueThread.Priority = ThreadPriority.BelowNormal;
+ EventQueueThread.Name = "EventQueueManagerThread_" + ThreadCount;
+ EventQueueThread.Start();
+
+ // Look at this... Don't you wish everyone did that solid coding everywhere? :P
+ if (ThreadCount == int.MaxValue)
+ ThreadCount = 0;
+ ThreadCount++;
+ }
+
+ public void Shutdown()
+ {
+ if (EventQueueThread != null && EventQueueThread.IsAlive == true)
+ {
+ try
+ {
+ EventQueueThread.Abort();
+ EventQueueThread.Join();
+ }
+ catch (Exception)
+ {
+ //myScriptEngine.Log.Verbose("ScriptEngine", "EventQueueManager Exception killing worker thread: " + e.ToString());
+ }
+ }
+ }
+
+
+ ///
+ /// Queue processing thread loop
+ ///
+ private void EventQueueThreadLoop()
+ {
+ //myScriptEngine.m_logger.Verbose("ScriptEngine", "EventQueueManager Worker thread spawned");
+ try
+ {
+ EventQueueManager.QueueItemStruct BlankQIS = new EventQueueManager.QueueItemStruct();
+ while (true)
+ {
+ try
+ {
+ EventQueueManager.QueueItemStruct QIS = BlankQIS;
+ bool GotItem = false;
+
+ if (eventQueueManager.eventQueue.Count == 0)
+ {
+ // Nothing to do? Sleep a bit waiting for something to do
+ Thread.Sleep(nothingToDoSleepms);
+ }
+ else
+ {
+ // Something in queue, process
+ //myScriptEngine.m_logger.Verbose("ScriptEngine", "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)
+ {
+ GotItem = false;
+ for (int qc = 0; qc < eventQueueManager.eventQueue.Count; qc++)
+ {
+ // Get queue item
+ QIS = eventQueueManager.eventQueue.Dequeue();
+
+ // Check if object is being processed by someone else
+ if (eventQueueManager.TryLock(QIS.localID) == false)
+ {
+ // Object is already being processed, requeue it
+ eventQueueManager.eventQueue.Enqueue(QIS);
+ }
+ else
+ {
+ // We have lock on an object and can process it
+ GotItem = true;
+ break;
+ }
+ } // go through queue
+ } // lock
+
+ if (GotItem == true)
+ {
+ // Execute function
+ try
+ {
+#if DEBUG
+ eventQueueManager.m_ScriptEngine.Log.Debug("ScriptEngine", "Executing event:\r\n"
+ + "QIS.localID: " + QIS.localID
+ + ", QIS.itemID: " + QIS.itemID
+ + ", QIS.functionName: " + QIS.functionName);
+#endif
+ LastExecutionStarted = DateTime.Now;
+ InExecution = true;
+ eventQueueManager.m_ScriptEngine.m_ScriptManager.ExecuteEvent(QIS.localID, QIS.itemID,
+ QIS.functionName, QIS.llDetectParams, QIS.param);
+ InExecution = false;
+ }
+ catch (Exception e)
+ {
+ InExecution = false;
+ // DISPLAY ERROR INWORLD
+ string text = "Error executing script function \"" + QIS.functionName + "\":\r\n";
+ if (e.InnerException != null)
+ {
+ // Send inner exception
+ text += e.InnerException.Message.ToString();
+ }
+ else
+ {
+ text += "\r\n";
+ // Send normal
+ text += e.Message.ToString();
+ }
+ try
+ {
+ if (text.Length > 1500)
+ text = text.Substring(0, 1500);
+ IScriptHost m_host = eventQueueManager.m_ScriptEngine.World.GetSceneObjectPart(QIS.localID);
+ //if (m_host != null)
+ //{
+ eventQueueManager.m_ScriptEngine.World.SimChat(Helpers.StringToField(text), ChatTypeEnum.Say, 0,
+ m_host.AbsolutePosition, m_host.Name, m_host.UUID);
+ }
+ catch
+ {
+ //}
+ //else
+ //{
+ // T oconsole
+ eventQueueManager.m_ScriptEngine.Log.Error("ScriptEngine",
+ "Unable to send text in-world:\r\n" + text);
+ }
+ }
+ finally
+ {
+ InExecution = false;
+ eventQueueManager.ReleaseLock(QIS.localID);
+ }
+ }
+ } // Something in queue
+ }
+ catch (ThreadAbortException tae)
+ {
+ throw tae;
+ }
+ catch (Exception e)
+ {
+ eventQueueManager.m_ScriptEngine.Log.Error("ScriptEngine", "Exception in EventQueueThreadLoop: " + e.ToString());
+ }
+ } // while
+ } // try
+ catch (ThreadAbortException)
+ {
+ //myScriptEngine.Log.Verbose("ScriptEngine", "EventQueueManager Worker thread killed: " + tae.Message);
+ }
+ }
+
+ }
+}
--
cgit v1.1