aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs460
1 files changed, 0 insertions, 460 deletions
diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs
deleted file mode 100644
index 5865452..0000000
--- a/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs
+++ /dev/null
@@ -1,460 +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 OpenSimulator 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.Collections.Generic;
31using System.Reflection;
32using OpenMetaverse;
33using OpenSim.Region.ScriptEngine.Shared;
34using log4net;
35
36namespace OpenSim.Region.ScriptEngine.DotNetEngine
37{
38 /// <summary>
39 /// EventQueueManager handles event queues
40 /// Events are queued and executed in separate thread
41 /// </summary>
42 [Serializable]
43 public class EventQueueManager
44 {
45 //
46 // Class is instanced in "ScriptEngine" and used by "EventManager" which is also instanced in "ScriptEngine".
47 //
48 // Class purpose is to queue and execute functions that are received by "EventManager":
49 // - allowing "EventManager" to release its event thread immediately, thus not interrupting server execution.
50 // - allowing us to prioritize and control execution of script functions.
51 // Class can use multiple threads for simultaneous execution. Mutexes are used for thread safety.
52 //
53 // 1. Hold an execution queue for scripts
54 // 2. Use threads to process queue, each thread executes one script function on each pass.
55 // 3. Catch any script error and process it
56 //
57 //
58 // Notes:
59 // * Current execution load balancing is optimized for 1 thread, and can cause unfair execute balancing between scripts.
60 // Not noticeable unless server is under high load.
61 //
62
63 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
64
65 public ScriptEngine m_ScriptEngine;
66
67 /// <summary>
68 /// List of threads (classes) processing event queue
69 /// Note that this may or may not be a reference to a static object depending on PrivateRegionThreads config setting.
70 /// </summary>
71 internal static List<EventQueueThreadClass> eventQueueThreads = new List<EventQueueThreadClass>(); // Thread pool that we work on
72 /// <summary>
73 /// Locking access to eventQueueThreads AND staticGlobalEventQueueThreads.
74 /// </summary>
75// private object eventQueueThreadsLock = new object();
76 // Static objects for referencing the objects above if we don't have private threads:
77 //internal static List<EventQueueThreadClass> staticEventQueueThreads; // A static reference used if we don't use private threads
78// internal static object staticEventQueueThreadsLock; // Statick lock object reference for same reason
79
80 /// <summary>
81 /// Global static list of all threads (classes) processing event queue -- used by max enforcment thread
82 /// </summary>
83 //private List<EventQueueThreadClass> staticGlobalEventQueueThreads = new List<EventQueueThreadClass>();
84
85 /// <summary>
86 /// Used internally to specify how many threads should exit gracefully
87 /// </summary>
88 public static int ThreadsToExit;
89 public static object ThreadsToExitLock = new object();
90
91
92 //public object queueLock = new object(); // Mutex lock object
93
94 /// <summary>
95 /// How many threads to process queue with
96 /// </summary>
97 internal static int numberOfThreads;
98
99 internal static int EventExecutionMaxQueueSize;
100
101 /// <summary>
102 /// Maximum time one function can use for execution before we perform a thread kill.
103 /// </summary>
104 private static int maxFunctionExecutionTimems
105 {
106 get { return (int)(maxFunctionExecutionTimens / 10000); }
107 set { maxFunctionExecutionTimens = value * 10000; }
108 }
109
110 /// <summary>
111 /// Contains nanoseconds version of maxFunctionExecutionTimems so that it matches time calculations better (performance reasons).
112 /// WARNING! ONLY UPDATE maxFunctionExecutionTimems, NEVER THIS DIRECTLY.
113 /// </summary>
114 public static long maxFunctionExecutionTimens;
115
116 /// <summary>
117 /// Enforce max execution time
118 /// </summary>
119 public static bool EnforceMaxExecutionTime;
120
121 /// <summary>
122 /// Kill script (unload) when it exceeds execution time
123 /// </summary>
124 private static bool KillScriptOnMaxFunctionExecutionTime;
125
126 /// <summary>
127 /// List of localID locks for mutex processing of script events
128 /// </summary>
129 private List<uint> objectLocks = new List<uint>();
130 private object tryLockLock = new object(); // Mutex lock object
131
132 /// <summary>
133 /// Queue containing events waiting to be executed
134 /// </summary>
135 public Queue<QueueItemStruct> eventQueue = new Queue<QueueItemStruct>();
136
137 #region " Queue structures "
138 /// <summary>
139 /// Queue item structure
140 /// </summary>
141 public struct QueueItemStruct
142 {
143 public uint localID;
144 public UUID itemID;
145 public string functionName;
146 public DetectParams[] llDetectParams;
147 public object[] param;
148 public Dictionary<KeyValuePair<int,int>,KeyValuePair<int,int>>
149 LineMap;
150 }
151
152 #endregion
153
154 #region " Initialization / Startup "
155 public EventQueueManager(ScriptEngine _ScriptEngine)
156 {
157 m_ScriptEngine = _ScriptEngine;
158
159 ReadConfig();
160 AdjustNumberOfScriptThreads();
161 }
162
163 public void ReadConfig()
164 {
165 // Refresh config
166 numberOfThreads = m_ScriptEngine.ScriptConfigSource.GetInt("NumberOfScriptThreads", 2);
167 maxFunctionExecutionTimems = m_ScriptEngine.ScriptConfigSource.GetInt("MaxEventExecutionTimeMs", 5000);
168 EnforceMaxExecutionTime = m_ScriptEngine.ScriptConfigSource.GetBoolean("EnforceMaxEventExecutionTime", true);
169 KillScriptOnMaxFunctionExecutionTime = m_ScriptEngine.ScriptConfigSource.GetBoolean("DeactivateScriptOnTimeout", false);
170 EventExecutionMaxQueueSize = m_ScriptEngine.ScriptConfigSource.GetInt("EventExecutionMaxQueueSize", 300);
171
172 // Now refresh config in all threads
173 lock (eventQueueThreads)
174 {
175 foreach (EventQueueThreadClass EventQueueThread in eventQueueThreads)
176 {
177 EventQueueThread.ReadConfig();
178 }
179 }
180 }
181
182 #endregion
183
184 #region " Shutdown all threads "
185 ~EventQueueManager()
186 {
187 Stop();
188 }
189
190 private void Stop()
191 {
192 if (eventQueueThreads != null)
193 {
194 // Kill worker threads
195 lock (eventQueueThreads)
196 {
197 foreach (EventQueueThreadClass EventQueueThread in new ArrayList(eventQueueThreads))
198 {
199 AbortThreadClass(EventQueueThread);
200 }
201 //eventQueueThreads.Clear();
202 //staticGlobalEventQueueThreads.Clear();
203 }
204 }
205
206 // Remove all entries from our event queue
207 lock (eventQueue)
208 {
209 eventQueue.Clear();
210 }
211 }
212
213 #endregion
214
215 #region " Start / stop script execution threads (ThreadClasses) "
216 private void StartNewThreadClass()
217 {
218 EventQueueThreadClass eqtc = new EventQueueThreadClass();
219 eventQueueThreads.Add(eqtc);
220 //m_log.Debug("[" + m_ScriptEngine.ScriptEngineName + "]: Started new script execution thread. Current thread count: " + eventQueueThreads.Count);
221 }
222
223 private void AbortThreadClass(EventQueueThreadClass threadClass)
224 {
225 if (eventQueueThreads.Contains(threadClass))
226 eventQueueThreads.Remove(threadClass);
227
228 try
229 {
230 threadClass.Stop();
231 }
232 catch (Exception)
233 {
234 //m_log.Error("[" + m_ScriptEngine.ScriptEngineName + ":EventQueueManager]: If you see this, could you please report it to Tedd:");
235 //m_log.Error("[" + m_ScriptEngine.ScriptEngineName + ":EventQueueManager]: Script thread execution timeout kill ended in exception: " + ex.ToString());
236 }
237 //m_log.Debug("[" + m_ScriptEngine.ScriptEngineName + "]: Killed script execution thread. Remaining thread count: " + eventQueueThreads.Count);
238 }
239 #endregion
240
241 #region " Mutex locks for queue access "
242 /// <summary>
243 /// Try to get a mutex lock on localID
244 /// </summary>
245 /// <param name="localID"></param>
246 /// <returns></returns>
247 public bool TryLock(uint localID)
248 {
249 lock (tryLockLock)
250 {
251 if (objectLocks.Contains(localID) == true)
252 {
253 return false;
254 }
255 else
256 {
257 objectLocks.Add(localID);
258 return true;
259 }
260 }
261 }
262
263 /// <summary>
264 /// Release mutex lock on localID
265 /// </summary>
266 /// <param name="localID"></param>
267 public void ReleaseLock(uint localID)
268 {
269 lock (tryLockLock)
270 {
271 if (objectLocks.Contains(localID) == true)
272 {
273 objectLocks.Remove(localID);
274 }
275 }
276 }
277 #endregion
278
279 #region " Check execution queue for a specified Event"
280 /// <summary>
281 /// checks to see if a specified event type is already in the queue
282 /// </summary>
283 /// <param name="localID">Region object ID</param>
284 /// <param name="FunctionName">Name of the function, will be state + "_event_" + FunctionName</param>
285 /// <returns>true if event is found , false if not found</returns>
286 ///
287 public bool CheckEeventQueueForEvent(uint localID, string FunctionName)
288 {
289 if (eventQueue.Count > 0)
290 {
291 lock (eventQueue)
292 {
293 foreach (EventQueueManager.QueueItemStruct QIS in eventQueue)
294 {
295 if ((QIS.functionName == FunctionName) && (QIS.localID == localID))
296 return true;
297 }
298 }
299 }
300 return false;
301 }
302 #endregion
303
304 #region " Add events to execution queue "
305 /// <summary>
306 /// Add event to event execution queue
307 /// </summary>
308 /// <param name="localID">Region object ID</param>
309 /// <param name="FunctionName">Name of the function, will be state + "_event_" + FunctionName</param>
310 /// <param name="param">Array of parameters to match event mask</param>
311 public bool AddToObjectQueue(uint localID, string FunctionName, DetectParams[] qParams, params object[] param)
312 {
313 // Determine all scripts in Object and add to their queue
314 //myScriptEngine.log.Info("[" + ScriptEngineName + "]: EventQueueManager Adding localID: " + localID + ", FunctionName: " + FunctionName);
315
316 // Do we have any scripts in this object at all? If not, return
317 if (m_ScriptEngine.m_ScriptManager.Scripts.ContainsKey(localID) == false)
318 {
319 //m_log.Debug("Event \String.Empty + FunctionName + "\" for localID: " + localID + ". No scripts found on this localID.");
320 return false;
321 }
322
323 List<UUID> scriptKeys =
324 m_ScriptEngine.m_ScriptManager.GetScriptKeys(localID);
325
326 foreach (UUID itemID in scriptKeys)
327 {
328 // Add to each script in that object
329 // TODO: Some scripts may not subscribe to this event. Should we NOT add it? Does it matter?
330 AddToScriptQueue(localID, itemID, FunctionName, qParams, param);
331 }
332 return true;
333 }
334
335 /// <summary>
336 /// Add event to event execution queue
337 /// </summary>
338 /// <param name="localID">Region object ID</param>
339 /// <param name="itemID">Region script ID</param>
340 /// <param name="FunctionName">Name of the function, will be state + "_event_" + FunctionName</param>
341 /// <param name="param">Array of parameters to match event mask</param>
342 public bool AddToScriptQueue(uint localID, UUID itemID, string FunctionName, DetectParams[] qParams, params object[] param)
343 {
344 List<UUID> keylist = m_ScriptEngine.m_ScriptManager.GetScriptKeys(localID);
345
346 if (!keylist.Contains(itemID)) // We don't manage that script
347 {
348 return false;
349 }
350
351 lock (eventQueue)
352 {
353 if (eventQueue.Count >= EventExecutionMaxQueueSize)
354 {
355 m_log.Error("[" + m_ScriptEngine.ScriptEngineName + "]: ERROR: Event execution queue item count is at " + eventQueue.Count + ". Config variable \"EventExecutionMaxQueueSize\" is set to " + EventExecutionMaxQueueSize + ", so ignoring new event.");
356 m_log.Error("[" + m_ScriptEngine.ScriptEngineName + "]: Event ignored: localID: " + localID + ", itemID: " + itemID + ", FunctionName: " + FunctionName);
357 return false;
358 }
359
360 InstanceData id = m_ScriptEngine.m_ScriptManager.GetScript(
361 localID, itemID);
362
363 // Create a structure and add data
364 QueueItemStruct QIS = new QueueItemStruct();
365 QIS.localID = localID;
366 QIS.itemID = itemID;
367 QIS.functionName = FunctionName;
368 QIS.llDetectParams = qParams;
369 QIS.param = param;
370 if (id != null)
371 QIS.LineMap = id.LineMap;
372
373 // Add it to queue
374 eventQueue.Enqueue(QIS);
375 }
376 return true;
377 }
378 #endregion
379
380 #region " Maintenance thread "
381
382 /// <summary>
383 /// Adjust number of script thread classes. It can start new, but if it needs to stop it will just set number of threads in "ThreadsToExit" and threads will have to exit themselves.
384 /// Called from MaintenanceThread
385 /// </summary>
386 public void AdjustNumberOfScriptThreads()
387 {
388 // Is there anything here for us to do?
389 if (eventQueueThreads.Count == numberOfThreads)
390 return;
391
392 lock (eventQueueThreads)
393 {
394 int diff = numberOfThreads - eventQueueThreads.Count;
395 // Positive number: Start
396 // Negative number: too many are running
397 if (diff > 0)
398 {
399 // We need to add more threads
400 for (int ThreadCount = eventQueueThreads.Count; ThreadCount < numberOfThreads; ThreadCount++)
401 {
402 StartNewThreadClass();
403 }
404 }
405 if (diff < 0)
406 {
407 // We need to kill some threads
408 lock (ThreadsToExitLock)
409 {
410 ThreadsToExit = Math.Abs(diff);
411 }
412 }
413 }
414 }
415
416 /// <summary>
417 /// Check if any thread class has been executing an event too long
418 /// </summary>
419 public void CheckScriptMaxExecTime()
420 {
421 // Iterate through all ScriptThreadClasses and check how long their current function has been executing
422 lock (eventQueueThreads)
423 {
424 foreach (EventQueueThreadClass EventQueueThread in eventQueueThreads)
425 {
426 // Is thread currently executing anything?
427 if (EventQueueThread.InExecution)
428 {
429 // Has execution time expired?
430 if (DateTime.Now.Ticks - EventQueueThread.LastExecutionStarted >
431 maxFunctionExecutionTimens)
432 {
433 // Yes! We need to kill this thread!
434
435 // Set flag if script should be removed or not
436 EventQueueThread.KillCurrentScript = KillScriptOnMaxFunctionExecutionTime;
437
438 // Abort this thread
439 AbortThreadClass(EventQueueThread);
440
441 // We do not need to start another, MaintenenceThread will do that for us
442 //StartNewThreadClass();
443 }
444 }
445 }
446 }
447 }
448 #endregion
449
450 ///// <summary>
451 ///// If set to true then threads and stuff should try to make a graceful exit
452 ///// </summary>
453 //public bool PleaseShutdown
454 //{
455 // get { return _PleaseShutdown; }
456 // set { _PleaseShutdown = value; }
457 //}
458 //private bool _PleaseShutdown = false;
459 }
460}