aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueThreadClass.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueThreadClass.cs')
-rw-r--r--OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueThreadClass.cs381
1 files changed, 381 insertions, 0 deletions
diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueThreadClass.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueThreadClass.cs
new file mode 100644
index 0000000..db3f89f
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueThreadClass.cs
@@ -0,0 +1,381 @@
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
28using System;
29using System.Collections;
30using System.Reflection;
31using System.Text.RegularExpressions;
32using System.Threading;
33using System.Globalization;
34using OpenMetaverse;
35using log4net;
36using OpenSim.Framework;
37using OpenSim.Region.Environment.Scenes.Scripting;
38
39namespace OpenSim.Region.ScriptEngine.DotNetEngine
40{
41 /// <summary>
42 /// Because every thread needs some data set for it (time started to execute current function), it will do its work within a class
43 /// </summary>
44 public class EventQueueThreadClass : iScriptEngineFunctionModule
45 {
46 // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
47
48 /// <summary>
49 /// How many ms to sleep if queue is empty
50 /// </summary>
51 private static int nothingToDoSleepms;// = 50;
52 private static ThreadPriority MyThreadPriority;
53
54 public long LastExecutionStarted;
55 public bool InExecution = false;
56 public bool KillCurrentScript = false;
57
58 //private EventQueueManager eventQueueManager;
59 public Thread EventQueueThread;
60 private static int ThreadCount = 0;
61
62 private string ScriptEngineName = "ScriptEngine.Common";
63
64 public EventQueueThreadClass()//EventQueueManager eqm
65 {
66 //eventQueueManager = eqm;
67 ReadConfig();
68 Start();
69 }
70
71 ~EventQueueThreadClass()
72 {
73 Stop();
74 }
75
76 public void ReadConfig()
77 {
78 lock (ScriptEngine.ScriptEngines)
79 {
80 foreach (ScriptEngine m_ScriptEngine in ScriptEngine.ScriptEngines)
81 {
82 ScriptEngineName = m_ScriptEngine.ScriptEngineName;
83 nothingToDoSleepms = m_ScriptEngine.ScriptConfigSource.GetInt("SleepTimeIfNoScriptExecutionMs", 50);
84
85 // Later with ScriptServer we might want to ask OS for stuff too, so doing this a bit manually
86 string pri = m_ScriptEngine.ScriptConfigSource.GetString("ScriptThreadPriority", "BelowNormal");
87 switch (pri.ToLower())
88 {
89 case "lowest":
90 MyThreadPriority = ThreadPriority.Lowest;
91 break;
92 case "belownormal":
93 MyThreadPriority = ThreadPriority.BelowNormal;
94 break;
95 case "normal":
96 MyThreadPriority = ThreadPriority.Normal;
97 break;
98 case "abovenormal":
99 MyThreadPriority = ThreadPriority.AboveNormal;
100 break;
101 case "highest":
102 MyThreadPriority = ThreadPriority.Highest;
103 break;
104 default:
105 MyThreadPriority = ThreadPriority.BelowNormal; // Default
106 m_ScriptEngine.Log.Error("[ScriptEngine.DotNetEngine]: Unknown priority type \"" + pri +
107 "\" in config file. Defaulting to \"BelowNormal\".");
108 break;
109 }
110 }
111 }
112 // Now set that priority
113 if (EventQueueThread != null)
114 if (EventQueueThread.IsAlive)
115 EventQueueThread.Priority = MyThreadPriority;
116 }
117
118 /// <summary>
119 /// Start thread
120 /// </summary>
121 private void Start()
122 {
123 EventQueueThread = new Thread(EventQueueThreadLoop);
124 EventQueueThread.IsBackground = true;
125
126 EventQueueThread.Priority = MyThreadPriority;
127 EventQueueThread.Name = "EventQueueManagerThread_" + ThreadCount;
128 EventQueueThread.Start();
129 ThreadTracker.Add(EventQueueThread);
130
131 // Look at this... Don't you wish everyone did that solid coding everywhere? :P
132 if (ThreadCount == int.MaxValue)
133 ThreadCount = 0;
134 ThreadCount++;
135 }
136
137 public void Stop()
138 {
139 //PleaseShutdown = true; // Set shutdown flag
140 //Thread.Sleep(100); // Wait a bit
141 if (EventQueueThread != null && EventQueueThread.IsAlive == true)
142 {
143 try
144 {
145 EventQueueThread.Abort(); // Send abort
146 //EventQueueThread.Join(); // Wait for it
147 }
148 catch (Exception)
149 {
150 //myScriptEngine.Log.Info("[" + ScriptEngineName + "]: EventQueueManager Exception killing worker thread: " + e.ToString());
151 }
152 }
153 }
154
155 private EventQueueManager.QueueItemStruct BlankQIS = new EventQueueManager.QueueItemStruct();
156 private ScriptEngine lastScriptEngine;
157 /// <summary>
158 /// Queue processing thread loop
159 /// </summary>
160 private void EventQueueThreadLoop()
161 {
162 CultureInfo USCulture = new CultureInfo("en-US");
163 Thread.CurrentThread.CurrentCulture = USCulture;
164
165 //myScriptEngine.Log.Info("[" + ScriptEngineName + "]: EventQueueManager Worker thread spawned");
166 try
167 {
168 while (true)
169 {
170 try
171 {
172 while (true)
173 {
174 DoProcessQueue();
175 }
176 }
177 catch (ThreadAbortException)
178 {
179 if (lastScriptEngine != null)
180 lastScriptEngine.Log.Info("[" + ScriptEngineName + "]: ThreadAbortException while executing function.");
181 }
182 catch (Exception e)
183 {
184 if (lastScriptEngine != null)
185 lastScriptEngine.Log.Error("[" + ScriptEngineName + "]: Exception in EventQueueThreadLoop: " + e.ToString());
186 }
187 }
188 }
189 catch (ThreadAbortException)
190 {
191 //myScriptEngine.Log.Info("[" + ScriptEngineName + "]: EventQueueManager Worker thread killed: " + tae.Message);
192 }
193 }
194
195 public void DoProcessQueue()
196 {
197 //lock (ScriptEngine.ScriptEngines)
198 //{
199 foreach (ScriptEngine m_ScriptEngine in new ArrayList(ScriptEngine.ScriptEngines))
200 {
201 lastScriptEngine = m_ScriptEngine;
202 // Every now and then check if we should shut down
203 //if (PleaseShutdown || EventQueueManager.ThreadsToExit > 0)
204 //{
205 // // Someone should shut down, lets get exclusive lock
206 // lock (EventQueueManager.ThreadsToExitLock)
207 // {
208 // // Lets re-check in case someone grabbed it
209 // if (EventQueueManager.ThreadsToExit > 0)
210 // {
211 // // Its crowded here so we'll shut down
212 // EventQueueManager.ThreadsToExit--;
213 // Stop();
214 // return;
215 // }
216 // else
217 // {
218 // // We have been asked to shut down
219 // Stop();
220 // return;
221 // }
222 // }
223 //}
224
225 //try
226 // {
227 EventQueueManager.QueueItemStruct QIS = BlankQIS;
228 bool GotItem = false;
229
230 //if (PleaseShutdown)
231 // return;
232
233 if (m_ScriptEngine.m_EventQueueManager == null || m_ScriptEngine.m_EventQueueManager.eventQueue == null)
234 continue;
235
236 if (m_ScriptEngine.m_EventQueueManager.eventQueue.Count == 0)
237 {
238 // Nothing to do? Sleep a bit waiting for something to do
239 Thread.Sleep(nothingToDoSleepms);
240 }
241 else
242 {
243 // Something in queue, process
244 //myScriptEngine.Log.Info("[" + ScriptEngineName + "]: Processing event for localID: " + QIS.localID + ", itemID: " + QIS.itemID + ", FunctionName: " + QIS.FunctionName);
245
246 // OBJECT BASED LOCK - TWO THREADS WORKING ON SAME OBJECT IS NOT GOOD
247 lock (m_ScriptEngine.m_EventQueueManager.eventQueue)
248 {
249 GotItem = false;
250 for (int qc = 0; qc < m_ScriptEngine.m_EventQueueManager.eventQueue.Count; qc++)
251 {
252 // Get queue item
253 QIS = m_ScriptEngine.m_EventQueueManager.eventQueue.Dequeue();
254
255 // Check if object is being processed by someone else
256 if (m_ScriptEngine.m_EventQueueManager.TryLock(QIS.localID) == false)
257 {
258 // Object is already being processed, requeue it
259 m_ScriptEngine.m_EventQueueManager.eventQueue.Enqueue(QIS);
260 }
261 else
262 {
263 // We have lock on an object and can process it
264 GotItem = true;
265 break;
266 }
267 }
268 }
269
270 if (GotItem == true)
271 {
272 // Execute function
273 try
274 {
275 ///cfk 2-7-08 dont need this right now and the default Linux build has DEBUG defined
276#if DEBUG
277 //eventQueueManager.m_ScriptEngine.Log.Debug("[" + ScriptEngineName + "]: " +
278 // "Executing event:\r\n"
279 // + "QIS.localID: " + QIS.localID
280 // + ", QIS.itemID: " + QIS.itemID
281 // + ", QIS.functionName: " +
282 // QIS.functionName);
283#endif
284 // Only pipe event if land supports it.
285 if (m_ScriptEngine.World.PipeEventsForScript(QIS.localID))
286 {
287 LastExecutionStarted = DateTime.Now.Ticks;
288 KillCurrentScript = false;
289 InExecution = true;
290 m_ScriptEngine.m_ScriptManager.ExecuteEvent(QIS.localID,
291 QIS.itemID,
292 QIS.functionName,
293 QIS.llDetectParams,
294 QIS.param);
295 InExecution = false;
296 }
297 }
298 catch (Exception e)
299 {
300 InExecution = false;
301 // DISPLAY ERROR INWORLD
302 string text = "Error executing script function \"" + QIS.functionName +
303 "\":\r\n";
304 if (e.InnerException != null)
305 {
306 // Send inner exception
307 string line = " (unknown line)";
308 Regex rx = new Regex(@"SecondLife\.Script\..+[\s:](?<line>\d+)\.?\r?$", RegexOptions.Compiled);
309 if (rx.Match(e.InnerException.ToString()).Success)
310 line = " (line " + rx.Match(e.InnerException.ToString()).Result("${line}") + ")";
311 text += e.InnerException.Message.ToString() + line;
312 }
313 else
314 {
315 text += "\r\n";
316 // Send normal
317 text += e.Message.ToString();
318 }
319 if (KillCurrentScript)
320 text += "\r\nScript will be deactivated!";
321
322 try
323 {
324 if (text.Length > 1500)
325 text = text.Substring(0, 1500);
326 IScriptHost m_host =
327 m_ScriptEngine.World.GetSceneObjectPart(QIS.localID);
328 //if (m_host != null)
329 //{
330 m_ScriptEngine.World.SimChat(Utils.StringToBytes(text),
331 ChatTypeEnum.DebugChannel, 2147483647,
332 m_host.AbsolutePosition,
333 m_host.Name, m_host.UUID, false);
334 }
335 catch (Exception)
336 {
337 //}
338 //else
339 //{
340 // T oconsole
341 m_ScriptEngine.m_EventQueueManager.m_ScriptEngine.Log.Error("[" + ScriptEngineName +
342 "]: " +
343 "Unable to send text in-world:\r\n" +
344 text);
345 }
346 finally
347 {
348 // So we are done sending message in-world
349 if (KillCurrentScript)
350 {
351 m_ScriptEngine.m_EventQueueManager.m_ScriptEngine.m_ScriptManager.StopScript(
352 QIS.localID, QIS.itemID);
353 }
354 }
355
356 // Pass it on so it's displayed on the console
357 // and in the logs (mikem 2008.06.02).
358 throw e.InnerException;
359 }
360 finally
361 {
362 InExecution = false;
363 m_ScriptEngine.m_EventQueueManager.ReleaseLock(QIS.localID);
364 }
365 }
366 }
367 }
368 // }
369 }
370
371 ///// <summary>
372 ///// If set to true then threads and stuff should try to make a graceful exit
373 ///// </summary>
374 //public bool PleaseShutdown
375 //{
376 // get { return _PleaseShutdown; }
377 // set { _PleaseShutdown = value; }
378 //}
379 //private bool _PleaseShutdown = false;
380 }
381}