From 0081c060d0bb66e842a381e1a15a6cf01c0bd793 Mon Sep 17 00:00:00 2001 From: Tedd Hansen Date: Sat, 12 Jan 2008 14:45:59 +0000 Subject: Set eol --- .../Common/ScriptEngineBase/EventQueueManager.cs | 726 ++++++++++----------- 1 file changed, 363 insertions(+), 363 deletions(-) (limited to 'OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs') diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs index ad9ff45..8081e2c 100644 --- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs +++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs @@ -1,364 +1,364 @@ -/* -* 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.Collections.Generic; -using System.Threading; -using libsecondlife; -using OpenSim.Framework; -using OpenSim.Region.Environment.Scenes.Scripting; - -namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase -{ - /// - /// EventQueueManager handles event queues - /// Events are queued and executed in separate thread - /// - [Serializable] - public class EventQueueManager - { - - // - // Class is instanced in "ScriptEngine" and used by "EventManager" also instanced in "ScriptEngine". - // - // Class purpose is to queue and execute functions that are received by "EventManager": - // - allowing "EventManager" to release its event thread immediately, thus not interrupting server execution. - // - allowing us to prioritize and control execution of script functions. - // Class can use multiple threads for simultaneous execution. Mutexes are used for thread safety. - // - // 1. Hold an execution queue for scripts - // 2. Use threads to process queue, each thread executes one script function on each pass. - // 3. Catch any script error and process it - // - // - // Notes: - // * Current execution load balancing is optimized for 1 thread, and can cause unfair execute balancing between scripts. - // Not noticeable unless server is under high load. - // * This class contains the number of threads used for script executions. Since we are not microthreading scripts yet, - // increase number of threads to allow more concurrent script executions in OpenSim. - // - - - /// - /// List of threads processing event queue - /// - private List eventQueueThreads = new List(); - - private object queueLock = new object(); // Mutex lock object - - /// - /// How many ms to sleep if queue is empty - /// - private int nothingToDoSleepms = 50; - - /// - /// How many threads to process queue with - /// - private int numberOfThreads = 2; - - /// - /// Queue containing events waiting to be executed - /// - private Queue eventQueue = new Queue(); - - /// - /// Queue item structure - /// - private struct QueueItemStruct - { - public uint localID; - public LLUUID itemID; - public string functionName; - public object[] param; - } - - /// - /// List of localID locks for mutex processing of script events - /// - private List objectLocks = new List(); - - private object tryLockLock = new object(); // Mutex lock object - - private ScriptEngine m_ScriptEngine; - - public EventQueueManager(ScriptEngine _ScriptEngine) - { - m_ScriptEngine = _ScriptEngine; - - // - // Start event queue processing threads (worker threads) - // - for (int ThreadCount = 0; ThreadCount <= numberOfThreads; ThreadCount++) - { - Thread EventQueueThread = new Thread(EventQueueThreadLoop); - eventQueueThreads.Add(EventQueueThread); - EventQueueThread.IsBackground = true; - EventQueueThread.Priority = ThreadPriority.BelowNormal; - EventQueueThread.Name = "EventQueueManagerThread_" + ThreadCount; - EventQueueThread.Start(); - } - } - - ~EventQueueManager() - { - // Kill worker threads - foreach (Thread EventQueueThread in new ArrayList(eventQueueThreads)) - { - if (EventQueueThread != null && EventQueueThread.IsAlive == true) - { - try - { - EventQueueThread.Abort(); - EventQueueThread.Join(); - } - catch (Exception) - { - //myScriptEngine.Log.Verbose("ScriptEngine", "EventQueueManager Exception killing worker thread: " + e.ToString()); - } - } - } - eventQueueThreads.Clear(); - // Todo: Clean up our queues - eventQueue.Clear(); - } - - /// - /// Queue processing thread loop - /// - private void EventQueueThreadLoop() - { - //myScriptEngine.m_logger.Verbose("ScriptEngine", "EventQueueManager Worker thread spawned"); - try - { - QueueItemStruct BlankQIS = new QueueItemStruct(); - while (true) - { - try - { - QueueItemStruct QIS = BlankQIS; - bool GotItem = false; - - if (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 (queueLock) - { - GotItem = false; - for (int qc = 0; qc < eventQueue.Count; qc++) - { - // Get queue item - QIS = eventQueue.Dequeue(); - - // Check if object is being processed by someone else - if (TryLock(QIS.localID) == false) - { - // Object is already being processed, requeue it - 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 - m_ScriptEngine.Log.Debug("ScriptEngine", "Executing event:\r\n" - + "QIS.localID: " + QIS.localID - + ", QIS.itemID: " + QIS.itemID - + ", QIS.functionName: " + QIS.functionName); -#endif - m_ScriptEngine.m_ScriptManager.ExecuteEvent(QIS.localID, QIS.itemID, - QIS.functionName, QIS.param); - } - catch (Exception e) - { - // 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 = m_ScriptEngine.World.GetSceneObjectPart(QIS.localID); - //if (m_host != null) - //{ - m_ScriptEngine.World.SimChat(Helpers.StringToField(text), ChatTypeEnum.Say, 0, - m_host.AbsolutePosition, m_host.Name, m_host.UUID); - } - catch - { - //} - //else - //{ - // T oconsole - m_ScriptEngine.Log.Error("ScriptEngine", - "Unable to send text in-world:\r\n" + text); - } - } - finally - { - ReleaseLock(QIS.localID); - } - } - } // Something in queue - } - catch (ThreadAbortException tae) - { - throw tae; - } - catch (Exception e) - { - m_ScriptEngine.Log.Error("ScriptEngine", "Exception in EventQueueThreadLoop: " + e.ToString()); - } - } // while - } // try - catch (ThreadAbortException) - { - //myScriptEngine.Log.Verbose("ScriptEngine", "EventQueueManager Worker thread killed: " + tae.Message); - } - } - - /// - /// Try to get a mutex lock on localID - /// - /// - /// - private bool TryLock(uint localID) - { - lock (tryLockLock) - { - if (objectLocks.Contains(localID) == true) - { - return false; - } - else - { - objectLocks.Add(localID); - return true; - } - } - } - - /// - /// Release mutex lock on localID - /// - /// - private void ReleaseLock(uint localID) - { - lock (tryLockLock) - { - if (objectLocks.Contains(localID) == true) - { - objectLocks.Remove(localID); - } - } - } - - - /// - /// Add event to event execution queue - /// - /// - /// Name of the function, will be state + "_event_" + FunctionName - /// Array of parameters to match event mask - public void AddToObjectQueue(uint localID, string FunctionName, params object[] param) - { - // Determine all scripts in Object and add to their queue - //myScriptEngine.m_logger.Verbose("ScriptEngine", "EventQueueManager Adding localID: " + localID + ", FunctionName: " + FunctionName); - - - // Do we have any scripts in this object at all? If not, return - if (m_ScriptEngine.m_ScriptManager.Scripts.ContainsKey(localID) == false) - { - //Console.WriteLine("Event \"" + FunctionName + "\" for localID: " + localID + ". No scripts found on this localID."); - return; - } - - Dictionary.KeyCollection scriptKeys = - m_ScriptEngine.m_ScriptManager.GetScriptKeys(localID); - - foreach (LLUUID itemID in scriptKeys) - { - // Add to each script in that object - // TODO: Some scripts may not subscribe to this event. Should we NOT add it? Does it matter? - AddToScriptQueue(localID, itemID, FunctionName, param); - } - } - - /// - /// Add event to event execution queue - /// - /// - /// - /// Name of the function, will be state + "_event_" + FunctionName - /// Array of parameters to match event mask - public void AddToScriptQueue(uint localID, LLUUID itemID, string FunctionName, params object[] param) - { - lock (queueLock) - { - // Create a structure and add data - QueueItemStruct QIS = new QueueItemStruct(); - QIS.localID = localID; - QIS.itemID = itemID; - QIS.functionName = FunctionName; - QIS.param = param; - - // Add it to queue - eventQueue.Enqueue(QIS); - } - } - } +/* +* 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.Collections.Generic; +using System.Threading; +using libsecondlife; +using OpenSim.Framework; +using OpenSim.Region.Environment.Scenes.Scripting; + +namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase +{ + /// + /// EventQueueManager handles event queues + /// Events are queued and executed in separate thread + /// + [Serializable] + public class EventQueueManager + { + + // + // Class is instanced in "ScriptEngine" and used by "EventManager" also instanced in "ScriptEngine". + // + // Class purpose is to queue and execute functions that are received by "EventManager": + // - allowing "EventManager" to release its event thread immediately, thus not interrupting server execution. + // - allowing us to prioritize and control execution of script functions. + // Class can use multiple threads for simultaneous execution. Mutexes are used for thread safety. + // + // 1. Hold an execution queue for scripts + // 2. Use threads to process queue, each thread executes one script function on each pass. + // 3. Catch any script error and process it + // + // + // Notes: + // * Current execution load balancing is optimized for 1 thread, and can cause unfair execute balancing between scripts. + // Not noticeable unless server is under high load. + // * This class contains the number of threads used for script executions. Since we are not microthreading scripts yet, + // increase number of threads to allow more concurrent script executions in OpenSim. + // + + + /// + /// List of threads processing event queue + /// + private List eventQueueThreads = new List(); + + private object queueLock = new object(); // Mutex lock object + + /// + /// How many ms to sleep if queue is empty + /// + private int nothingToDoSleepms = 50; + + /// + /// How many threads to process queue with + /// + private int numberOfThreads = 2; + + /// + /// Queue containing events waiting to be executed + /// + private Queue eventQueue = new Queue(); + + /// + /// Queue item structure + /// + private struct QueueItemStruct + { + public uint localID; + public LLUUID itemID; + public string functionName; + public object[] param; + } + + /// + /// List of localID locks for mutex processing of script events + /// + private List objectLocks = new List(); + + private object tryLockLock = new object(); // Mutex lock object + + private ScriptEngine m_ScriptEngine; + + public EventQueueManager(ScriptEngine _ScriptEngine) + { + m_ScriptEngine = _ScriptEngine; + + // + // Start event queue processing threads (worker threads) + // + for (int ThreadCount = 0; ThreadCount <= numberOfThreads; ThreadCount++) + { + Thread EventQueueThread = new Thread(EventQueueThreadLoop); + eventQueueThreads.Add(EventQueueThread); + EventQueueThread.IsBackground = true; + EventQueueThread.Priority = ThreadPriority.BelowNormal; + EventQueueThread.Name = "EventQueueManagerThread_" + ThreadCount; + EventQueueThread.Start(); + } + } + + ~EventQueueManager() + { + // Kill worker threads + foreach (Thread EventQueueThread in new ArrayList(eventQueueThreads)) + { + if (EventQueueThread != null && EventQueueThread.IsAlive == true) + { + try + { + EventQueueThread.Abort(); + EventQueueThread.Join(); + } + catch (Exception) + { + //myScriptEngine.Log.Verbose("ScriptEngine", "EventQueueManager Exception killing worker thread: " + e.ToString()); + } + } + } + eventQueueThreads.Clear(); + // Todo: Clean up our queues + eventQueue.Clear(); + } + + /// + /// Queue processing thread loop + /// + private void EventQueueThreadLoop() + { + //myScriptEngine.m_logger.Verbose("ScriptEngine", "EventQueueManager Worker thread spawned"); + try + { + QueueItemStruct BlankQIS = new QueueItemStruct(); + while (true) + { + try + { + QueueItemStruct QIS = BlankQIS; + bool GotItem = false; + + if (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 (queueLock) + { + GotItem = false; + for (int qc = 0; qc < eventQueue.Count; qc++) + { + // Get queue item + QIS = eventQueue.Dequeue(); + + // Check if object is being processed by someone else + if (TryLock(QIS.localID) == false) + { + // Object is already being processed, requeue it + 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 + m_ScriptEngine.Log.Debug("ScriptEngine", "Executing event:\r\n" + + "QIS.localID: " + QIS.localID + + ", QIS.itemID: " + QIS.itemID + + ", QIS.functionName: " + QIS.functionName); +#endif + m_ScriptEngine.m_ScriptManager.ExecuteEvent(QIS.localID, QIS.itemID, + QIS.functionName, QIS.param); + } + catch (Exception e) + { + // 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 = m_ScriptEngine.World.GetSceneObjectPart(QIS.localID); + //if (m_host != null) + //{ + m_ScriptEngine.World.SimChat(Helpers.StringToField(text), ChatTypeEnum.Say, 0, + m_host.AbsolutePosition, m_host.Name, m_host.UUID); + } + catch + { + //} + //else + //{ + // T oconsole + m_ScriptEngine.Log.Error("ScriptEngine", + "Unable to send text in-world:\r\n" + text); + } + } + finally + { + ReleaseLock(QIS.localID); + } + } + } // Something in queue + } + catch (ThreadAbortException tae) + { + throw tae; + } + catch (Exception e) + { + m_ScriptEngine.Log.Error("ScriptEngine", "Exception in EventQueueThreadLoop: " + e.ToString()); + } + } // while + } // try + catch (ThreadAbortException) + { + //myScriptEngine.Log.Verbose("ScriptEngine", "EventQueueManager Worker thread killed: " + tae.Message); + } + } + + /// + /// Try to get a mutex lock on localID + /// + /// + /// + private bool TryLock(uint localID) + { + lock (tryLockLock) + { + if (objectLocks.Contains(localID) == true) + { + return false; + } + else + { + objectLocks.Add(localID); + return true; + } + } + } + + /// + /// Release mutex lock on localID + /// + /// + private void ReleaseLock(uint localID) + { + lock (tryLockLock) + { + if (objectLocks.Contains(localID) == true) + { + objectLocks.Remove(localID); + } + } + } + + + /// + /// Add event to event execution queue + /// + /// + /// Name of the function, will be state + "_event_" + FunctionName + /// Array of parameters to match event mask + public void AddToObjectQueue(uint localID, string FunctionName, params object[] param) + { + // Determine all scripts in Object and add to their queue + //myScriptEngine.m_logger.Verbose("ScriptEngine", "EventQueueManager Adding localID: " + localID + ", FunctionName: " + FunctionName); + + + // Do we have any scripts in this object at all? If not, return + if (m_ScriptEngine.m_ScriptManager.Scripts.ContainsKey(localID) == false) + { + //Console.WriteLine("Event \"" + FunctionName + "\" for localID: " + localID + ". No scripts found on this localID."); + return; + } + + Dictionary.KeyCollection scriptKeys = + m_ScriptEngine.m_ScriptManager.GetScriptKeys(localID); + + foreach (LLUUID itemID in scriptKeys) + { + // Add to each script in that object + // TODO: Some scripts may not subscribe to this event. Should we NOT add it? Does it matter? + AddToScriptQueue(localID, itemID, FunctionName, param); + } + } + + /// + /// Add event to event execution queue + /// + /// + /// + /// Name of the function, will be state + "_event_" + FunctionName + /// Array of parameters to match event mask + public void AddToScriptQueue(uint localID, LLUUID itemID, string FunctionName, params object[] param) + { + lock (queueLock) + { + // Create a structure and add data + QueueItemStruct QIS = new QueueItemStruct(); + QIS.localID = localID; + QIS.itemID = itemID; + QIS.functionName = FunctionName; + QIS.param = param; + + // Add it to queue + eventQueue.Enqueue(QIS); + } + } + } } \ No newline at end of file -- cgit v1.1