aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs')
-rw-r--r--OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs364
1 files changed, 0 insertions, 364 deletions
diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs
deleted file mode 100644
index e7cb489..0000000
--- a/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs
+++ /dev/null
@@ -1,364 +0,0 @@
1/*
2* Copyright (c) Contributors, http://opensimulator.org/
3* See CONTRIBUTORS.TXT for a full list of copyright holders.
4*
5* Redistribution and use in source and binary forms, with or without
6* modification, are permitted provided that the following conditions are met:
7* * Redistributions of source code must retain the above copyright
8* notice, this list of conditions and the following disclaimer.
9* * Redistributions in binary form must reproduce the above copyright
10* notice, this list of conditions and the following disclaimer in the
11* documentation and/or other materials provided with the distribution.
12* * Neither the name of the OpenSim Project nor the
13* names of its contributors may be used to endorse or promote products
14* derived from this software without specific prior written permission.
15*
16* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*
27*/
28/* Original code: Tedd Hansen */
29using System;
30using System.Collections;
31using System.Collections.Generic;
32using System.Threading;
33using libsecondlife;
34using OpenSim.Framework;
35using OpenSim.Region.Environment.Scenes.Scripting;
36using OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL;
37
38namespace OpenSim.Region.ScriptEngine.DotNetEngine
39{
40 /// <summary>
41 /// EventQueueManager handles event queues
42 /// Events are queued and executed in separate thread
43 /// </summary>
44 [Serializable]
45 internal class EventQueueManager
46 {
47
48 //
49 // Class is instanced in "ScriptEngine" and used by "EventManager" also instanced in "ScriptEngine".
50 //
51 // Class purpose is to queue and execute functions that are received by "EventManager":
52 // - allowing "EventManager" to release its event thread immediately, thus not interrupting server execution.
53 // - allowing us to prioritize and control execution of script functions.
54 // Class can use multiple threads for simultaneous execution. Mutexes are used for thread safety.
55 //
56 // 1. Hold an execution queue for scripts
57 // 2. Use threads to process queue, each thread executes one script function on each pass.
58 // 3. Catch any script error and process it
59 //
60 //
61 // Notes:
62 // * Current execution load balancing is optimized for 1 thread, and can cause unfair execute balancing between scripts.
63 // Not noticeable unless server is under high load.
64 // * This class contains the number of threads used for script executions. Since we are not microthreading scripts yet,
65 // increase number of threads to allow more concurrent script executions in OpenSim.
66 //
67
68
69 /// <summary>
70 /// List of threads processing event queue
71 /// </summary>
72 private List<Thread> eventQueueThreads = new List<Thread>();
73
74 private object queueLock = new object(); // Mutex lock object
75
76 /// <summary>
77 /// How many ms to sleep if queue is empty
78 /// </summary>
79 private int nothingToDoSleepms = 50;
80
81 /// <summary>
82 /// How many threads to process queue with
83 /// </summary>
84 private int numberOfThreads = 2;
85
86 /// <summary>
87 /// Queue containing events waiting to be executed
88 /// </summary>
89 private Queue<QueueItemStruct> eventQueue = new Queue<QueueItemStruct>();
90
91 /// <summary>
92 /// Queue item structure
93 /// </summary>
94 private struct QueueItemStruct
95 {
96 public uint localID;
97 public LLUUID itemID;
98 public string functionName;
99 public object[] param;
100 }
101
102 /// <summary>
103 /// List of localID locks for mutex processing of script events
104 /// </summary>
105 private List<uint> objectLocks = new List<uint>();
106
107 private object tryLockLock = new object(); // Mutex lock object
108
109 private ScriptEngine m_ScriptEngine;
110
111 public EventQueueManager(ScriptEngine _ScriptEngine)
112 {
113 m_ScriptEngine = _ScriptEngine;
114
115 //
116 // Start event queue processing threads (worker threads)
117 //
118 for (int ThreadCount = 0; ThreadCount <= numberOfThreads; ThreadCount++)
119 {
120 Thread EventQueueThread = new Thread(EventQueueThreadLoop);
121 eventQueueThreads.Add(EventQueueThread);
122 EventQueueThread.IsBackground = true;
123 EventQueueThread.Priority = ThreadPriority.BelowNormal;
124 EventQueueThread.Name = "EventQueueManagerThread_" + ThreadCount;
125 EventQueueThread.Start();
126 }
127 }
128
129 ~EventQueueManager()
130 {
131 // Kill worker threads
132 foreach (Thread EventQueueThread in new ArrayList(eventQueueThreads))
133 {
134 if (EventQueueThread != null && EventQueueThread.IsAlive == true)
135 {
136 try
137 {
138 EventQueueThread.Abort();
139 EventQueueThread.Join();
140 }
141 catch (Exception)
142 {
143 //myScriptEngine.Log.Verbose("ScriptEngine", "EventQueueManager Exception killing worker thread: " + e.ToString());
144 }
145 }
146 }
147 eventQueueThreads.Clear();
148 // Todo: Clean up our queues
149 eventQueue.Clear();
150 }
151
152 /// <summary>
153 /// Queue processing thread loop
154 /// </summary>
155 private void EventQueueThreadLoop()
156 {
157 //myScriptEngine.m_logger.Verbose("ScriptEngine", "EventQueueManager Worker thread spawned");
158 try
159 {
160 QueueItemStruct BlankQIS = new QueueItemStruct();
161 while (true)
162 {
163 try
164 {
165 QueueItemStruct QIS = BlankQIS;
166 bool GotItem = false;
167
168 if (eventQueue.Count == 0)
169 {
170 // Nothing to do? Sleep a bit waiting for something to do
171 Thread.Sleep(nothingToDoSleepms);
172 }
173 else
174 {
175 // Something in queue, process
176 //myScriptEngine.m_logger.Verbose("ScriptEngine", "Processing event for localID: " + QIS.localID + ", itemID: " + QIS.itemID + ", FunctionName: " + QIS.FunctionName);
177
178 // OBJECT BASED LOCK - TWO THREADS WORKING ON SAME OBJECT IS NOT GOOD
179 lock (queueLock)
180 {
181 GotItem = false;
182 for (int qc = 0; qc < eventQueue.Count; qc++)
183 {
184 // Get queue item
185 QIS = eventQueue.Dequeue();
186
187 // Check if object is being processed by someone else
188 if (TryLock(QIS.localID) == false)
189 {
190 // Object is already being processed, requeue it
191 eventQueue.Enqueue(QIS);
192 }
193 else
194 {
195 // We have lock on an object and can process it
196 GotItem = true;
197 break;
198 }
199 } // go through queue
200 } // lock
201
202 if (GotItem == true)
203 {
204 // Execute function
205 try
206 {
207#if DEBUG
208 m_ScriptEngine.Log.Debug("ScriptEngine", "Executing event:\r\n"
209 + "QIS.localID: " + QIS.localID
210 + ", QIS.itemID: " + QIS.itemID
211 + ", QIS.functionName: " + QIS.functionName);
212#endif
213 m_ScriptEngine.m_ScriptManager.ExecuteEvent(QIS.localID, QIS.itemID,
214 QIS.functionName, QIS.param);
215 }
216 catch (Exception e)
217 {
218 // DISPLAY ERROR INWORLD
219 string text = "Error executing script function \"" + QIS.functionName + "\":\r\n";
220 //if (e.InnerException != null)
221 //{
222 // Send inner exception
223 text += e.InnerException.Message.ToString();
224 //}
225 //else
226 //{
227 text += "\r\n";
228 // Send normal
229 text += e.Message.ToString();
230 //}
231 try
232 {
233 if (text.Length > 1500)
234 text = text.Substring(0, 1500);
235 IScriptHost m_host = m_ScriptEngine.World.GetSceneObjectPart(QIS.localID);
236 //if (m_host != null)
237 //{
238 m_ScriptEngine.World.SimChat(Helpers.StringToField(text), ChatTypeEnum.Say, 0,
239 m_host.AbsolutePosition, m_host.Name, m_host.UUID);
240 }
241 catch
242 {
243 //}
244 //else
245 //{
246 // T oconsole
247 m_ScriptEngine.Log.Error("ScriptEngine",
248 "Unable to send text in-world:\r\n" + text);
249 }
250 }
251 finally
252 {
253 ReleaseLock(QIS.localID);
254 }
255 }
256 } // Something in queue
257 }
258 catch (ThreadAbortException tae)
259 {
260 throw tae;
261 }
262 catch (Exception e)
263 {
264 m_ScriptEngine.Log.Error("ScriptEngine", "Exception in EventQueueThreadLoop: " + e.ToString());
265 }
266 } // while
267 } // try
268 catch (ThreadAbortException)
269 {
270 //myScriptEngine.Log.Verbose("ScriptEngine", "EventQueueManager Worker thread killed: " + tae.Message);
271 }
272 }
273
274 /// <summary>
275 /// Try to get a mutex lock on localID
276 /// </summary>
277 /// <param name="localID"></param>
278 /// <returns></returns>
279 private bool TryLock(uint localID)
280 {
281 lock (tryLockLock)
282 {
283 if (objectLocks.Contains(localID) == true)
284 {
285 return false;
286 }
287 else
288 {
289 objectLocks.Add(localID);
290 return true;
291 }
292 }
293 }
294
295 /// <summary>
296 /// Release mutex lock on localID
297 /// </summary>
298 /// <param name="localID"></param>
299 private void ReleaseLock(uint localID)
300 {
301 lock (tryLockLock)
302 {
303 if (objectLocks.Contains(localID) == true)
304 {
305 objectLocks.Remove(localID);
306 }
307 }
308 }
309
310
311 /// <summary>
312 /// Add event to event execution queue
313 /// </summary>
314 /// <param name="localID"></param>
315 /// <param name="FunctionName">Name of the function, will be state + "_event_" + FunctionName</param>
316 /// <param name="param">Array of parameters to match event mask</param>
317 public void AddToObjectQueue(uint localID, string FunctionName, params object[] param)
318 {
319 // Determine all scripts in Object and add to their queue
320 //myScriptEngine.m_logger.Verbose("ScriptEngine", "EventQueueManager Adding localID: " + localID + ", FunctionName: " + FunctionName);
321
322
323 // Do we have any scripts in this object at all? If not, return
324 if (m_ScriptEngine.m_ScriptManager.Scripts.ContainsKey(localID) == false)
325 {
326 //Console.WriteLine("Event \"" + FunctionName + "\" for localID: " + localID + ". No scripts found on this localID.");
327 return;
328 }
329
330 Dictionary<LLUUID, LSL_BaseClass>.KeyCollection scriptKeys =
331 m_ScriptEngine.m_ScriptManager.GetScriptKeys(localID);
332
333 foreach (LLUUID itemID in scriptKeys)
334 {
335 // Add to each script in that object
336 // TODO: Some scripts may not subscribe to this event. Should we NOT add it? Does it matter?
337 AddToScriptQueue(localID, itemID, FunctionName, param);
338 }
339 }
340
341 /// <summary>
342 /// Add event to event execution queue
343 /// </summary>
344 /// <param name="localID"></param>
345 /// <param name="itemID"></param>
346 /// <param name="FunctionName">Name of the function, will be state + "_event_" + FunctionName</param>
347 /// <param name="param">Array of parameters to match event mask</param>
348 public void AddToScriptQueue(uint localID, LLUUID itemID, string FunctionName, params object[] param)
349 {
350 lock (queueLock)
351 {
352 // Create a structure and add data
353 QueueItemStruct QIS = new QueueItemStruct();
354 QIS.localID = localID;
355 QIS.itemID = itemID;
356 QIS.functionName = FunctionName;
357 QIS.param = param;
358
359 // Add it to queue
360 eventQueue.Enqueue(QIS);
361 }
362 }
363 }
364} \ No newline at end of file