aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Grid/ScriptEngine/DotNetEngine/EventQueueManager.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Grid/ScriptEngine/DotNetEngine/EventQueueManager.cs321
1 files changed, 321 insertions, 0 deletions
diff --git a/OpenSim/Grid/ScriptEngine/DotNetEngine/EventQueueManager.cs b/OpenSim/Grid/ScriptEngine/DotNetEngine/EventQueueManager.cs
new file mode 100644
index 0000000..8ee005a
--- /dev/null
+++ b/OpenSim/Grid/ScriptEngine/DotNetEngine/EventQueueManager.cs
@@ -0,0 +1,321 @@
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.Generic;
31using System.Text;
32using System.Threading;
33using System.Reflection;
34using OpenSim.Region.Environment.Scenes.Scripting;
35using libsecondlife;
36using OpenSim.Grid.ScriptEngine.DotNetEngine.Compiler.LSL;
37
38namespace OpenSim.Grid.ScriptEngine.DotNetEngine
39{
40 /// <summary>
41 /// EventQueueManager handles event queues
42 /// Events are queued and executed in separate thread
43 /// </summary>
44 [Serializable]
45 class EventQueueManager
46 {
47 /// <summary>
48 /// List of threads processing event queue
49 /// </summary>
50 private List<Thread> eventQueueThreads = new List<Thread>();
51 private object queueLock = new object(); // Mutex lock object
52 /// <summary>
53 /// How many ms to sleep if queue is empty
54 /// </summary>
55 private int nothingToDoSleepms = 50;
56 /// <summary>
57 /// How many threads to process queue with
58 /// </summary>
59 private int numberOfThreads = 2;
60 /// <summary>
61 /// Queue containing events waiting to be executed
62 /// </summary>
63 private Queue<QueueItemStruct> eventQueue = new Queue<QueueItemStruct>();
64 /// <summary>
65 /// Queue item structure
66 /// </summary>
67 private struct QueueItemStruct
68 {
69 public uint localID;
70 public LLUUID itemID;
71 public string functionName;
72 public object[] param;
73 }
74
75 /// <summary>
76 /// List of localID locks for mutex processing of script events
77 /// </summary>
78 private List<uint> objectLocks = new List<uint>();
79 private object tryLockLock = new object(); // Mutex lock object
80
81 private ScriptEngine m_ScriptEngine;
82 public EventQueueManager(ScriptEngine _ScriptEngine)
83 {
84 m_ScriptEngine = _ScriptEngine;
85
86 //
87 // Start event queue processing threads (worker threads)
88 //
89 for (int ThreadCount = 0; ThreadCount <= numberOfThreads; ThreadCount++)
90 {
91 Thread EventQueueThread = new Thread(EventQueueThreadLoop);
92 eventQueueThreads.Add(EventQueueThread);
93 EventQueueThread.IsBackground = true;
94 EventQueueThread.Priority = ThreadPriority.BelowNormal;
95 EventQueueThread.Name = "EventQueueManagerThread_" + ThreadCount;
96 EventQueueThread.Start();
97 }
98 }
99 ~EventQueueManager()
100 {
101
102 // Kill worker threads
103 foreach (Thread EventQueueThread in new System.Collections.ArrayList(eventQueueThreads))
104 {
105 if (EventQueueThread != null && EventQueueThread.IsAlive == true)
106 {
107 try
108 {
109 EventQueueThread.Abort();
110 EventQueueThread.Join();
111 }
112 catch (Exception)
113 {
114 //myScriptEngine.Log.Verbose("ScriptEngine", "EventQueueManager Exception killing worker thread: " + e.ToString());
115 }
116 }
117 }
118 eventQueueThreads.Clear();
119 // Todo: Clean up our queues
120 eventQueue.Clear();
121
122 }
123
124 /// <summary>
125 /// Queue processing thread loop
126 /// </summary>
127 private void EventQueueThreadLoop()
128 {
129 //myScriptEngine.m_logger.Verbose("ScriptEngine", "EventQueueManager Worker thread spawned");
130 try
131 {
132 QueueItemStruct BlankQIS = new QueueItemStruct();
133 while (true)
134 {
135 try
136 {
137 QueueItemStruct QIS = BlankQIS;
138 bool GotItem = false;
139
140 if (eventQueue.Count == 0)
141 {
142 // Nothing to do? Sleep a bit waiting for something to do
143 Thread.Sleep(nothingToDoSleepms);
144 }
145 else
146 {
147 // Something in queue, process
148 //myScriptEngine.m_logger.Verbose("ScriptEngine", "Processing event for localID: " + QIS.localID + ", itemID: " + QIS.itemID + ", FunctionName: " + QIS.FunctionName);
149
150 // OBJECT BASED LOCK - TWO THREADS WORKING ON SAME OBJECT IS NOT GOOD
151 lock (queueLock)
152 {
153 GotItem = false;
154 for (int qc = 0; qc < eventQueue.Count; qc++)
155 {
156 // Get queue item
157 QIS = eventQueue.Dequeue();
158
159 // Check if object is being processed by someone else
160 if (TryLock(QIS.localID) == false)
161 {
162 // Object is already being processed, requeue it
163 eventQueue.Enqueue(QIS);
164 }
165 else
166 {
167 // We have lock on an object and can process it
168 GotItem = true;
169 break;
170 }
171 } // go through queue
172 } // lock
173
174 if (GotItem == true)
175 {
176 // Execute function
177 try
178 {
179 m_ScriptEngine.m_ScriptManager.ExecuteEvent(QIS.localID, QIS.itemID, QIS.functionName, QIS.param);
180 }
181 catch (Exception e)
182 {
183 // DISPLAY ERROR INWORLD
184 string text = "Error executing script function \"" + QIS.functionName + "\":\r\n";
185 if (e.InnerException != null)
186 { // Send inner exception
187 text += e.InnerException.Message.ToString();
188 }
189 else
190 { // Send normal
191 text += e.Message.ToString();
192 }
193 try
194 {
195 if (text.Length > 1500)
196 text = text.Substring(0, 1500);
197 IScriptHost m_host = m_ScriptEngine.World.GetSceneObjectPart(QIS.localID);
198 //if (m_host != null)
199 //{
200 m_ScriptEngine.World.SimChat(Helpers.StringToField(text), 1, 0, m_host.AbsolutePosition, m_host.Name, m_host.UUID);
201 } catch {
202 //}
203 //else
204 //{
205 // T oconsole
206 Console.WriteLine("Unable to send text in-world:\r\n" + text);
207 }
208
209 }
210 finally
211 {
212 ReleaseLock(QIS.localID);
213 }
214 }
215
216 } // Something in queue
217 } catch (ThreadAbortException tae) {
218 throw tae;
219 } catch (Exception e) {
220 Console.WriteLine("Exception in EventQueueThreadLoop: " + e.ToString());
221 }
222 } // while
223 } // try
224 catch (ThreadAbortException)
225 {
226 //myScriptEngine.Log.Verbose("ScriptEngine", "EventQueueManager Worker thread killed: " + tae.Message);
227 }
228 }
229
230 /// <summary>
231 /// Try to get a mutex lock on localID
232 /// </summary>
233 /// <param name="localID"></param>
234 /// <returns></returns>
235 private bool TryLock(uint localID)
236 {
237 lock (tryLockLock)
238 {
239 if (objectLocks.Contains(localID) == true)
240 {
241 return false;
242 }
243 else
244 {
245 objectLocks.Add(localID);
246 return true;
247 }
248 }
249 }
250
251 /// <summary>
252 /// Release mutex lock on localID
253 /// </summary>
254 /// <param name="localID"></param>
255 private void ReleaseLock(uint localID)
256 {
257 lock (tryLockLock)
258 {
259 if (objectLocks.Contains(localID) == true)
260 {
261 objectLocks.Remove(localID);
262 }
263 }
264 }
265
266
267 /// <summary>
268 /// Add event to event execution queue
269 /// </summary>
270 /// <param name="localID"></param>
271 /// <param name="FunctionName">Name of the function, will be state + "_event_" + FunctionName</param>
272 /// <param name="param">Array of parameters to match event mask</param>
273 public void AddToObjectQueue(uint localID, string FunctionName, object[] param)
274 {
275 // Determine all scripts in Object and add to their queue
276 //myScriptEngine.m_logger.Verbose("ScriptEngine", "EventQueueManager Adding localID: " + localID + ", FunctionName: " + FunctionName);
277
278
279 // Do we have any scripts in this object at all? If not, return
280 if (m_ScriptEngine.m_ScriptManager.Scripts.ContainsKey(localID) == false)
281 {
282 //Console.WriteLine("Event \"" + FunctionName + "\" for localID: " + localID + ". No scripts found on this localID.");
283 return;
284 }
285
286 Dictionary<LLUUID, LSL_BaseClass>.KeyCollection scriptKeys = m_ScriptEngine.m_ScriptManager.GetScriptKeys(localID);
287
288 foreach ( LLUUID itemID in scriptKeys )
289 {
290 // Add to each script in that object
291 // TODO: Some scripts may not subscribe to this event. Should we NOT add it? Does it matter?
292 AddToScriptQueue(localID, itemID, FunctionName, param);
293 }
294
295 }
296
297 /// <summary>
298 /// Add event to event execution queue
299 /// </summary>
300 /// <param name="localID"></param>
301 /// <param name="itemID"></param>
302 /// <param name="FunctionName">Name of the function, will be state + "_event_" + FunctionName</param>
303 /// <param name="param">Array of parameters to match event mask</param>
304 public void AddToScriptQueue(uint localID, LLUUID itemID, string FunctionName, object[] param)
305 {
306 lock (queueLock)
307 {
308 // Create a structure and add data
309 QueueItemStruct QIS = new QueueItemStruct();
310 QIS.localID = localID;
311 QIS.itemID = itemID;
312 QIS.functionName = FunctionName;
313 QIS.param = param;
314
315 // Add it to queue
316 eventQueue.Enqueue(QIS);
317 }
318 }
319
320 }
321}