aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs')
-rw-r--r--OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs242
1 files changed, 146 insertions, 96 deletions
diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs
index d5826b7..04c084a 100644
--- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs
+++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs
@@ -65,12 +65,26 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
65 // increase number of threads to allow more concurrent script executions in OpenSim. 65 // increase number of threads to allow more concurrent script executions in OpenSim.
66 // 66 //
67 67
68 public ScriptEngine m_ScriptEngine;
69
70 /// <summary>
71 /// List of threads (classes) processing event queue
72 /// </summary>
73 internal List<EventQueueThreadClass> eventQueueThreads;
74 /// <summary>
75 /// Global static list of threads (classes) processing event queue -- used by max enforcment thread
76 /// </summary>
77 private List<EventQueueThreadClass> staticGlobalEventQueueThreads;
78 /// <summary>
79 /// Locking access to eventQueueThreads AND staticGlobalEventQueueThreads. Note that this may or may not be static depending on PrivateRegionThreads config setting.
80 /// </summary>
81 private object eventQueueThreadsLock;
68 82
69 /// <summary> 83 /// <summary>
70 /// List of threads processing event queue 84 /// Used internally to specify how many threads should exit gracefully
71 /// </summary> 85 /// </summary>
72 private List<EventQueueThreadClass> eventQueueThreads;// = new List<EventQueueThreadClass>(); 86 public int ThreadsToExit;
73 private object eventQueueThreadsLock;// = new object(); 87 public object ThreadsToExitLock = new object();
74 88
75 private static List<EventQueueThreadClass> staticEventQueueThreads;// = new List<EventQueueThreadClass>(); 89 private static List<EventQueueThreadClass> staticEventQueueThreads;// = new List<EventQueueThreadClass>();
76 private static object staticEventQueueThreadsLock;// = new object(); 90 private static object staticEventQueueThreadsLock;// = new object();
@@ -80,22 +94,43 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
80 /// <summary> 94 /// <summary>
81 /// How many threads to process queue with 95 /// How many threads to process queue with
82 /// </summary> 96 /// </summary>
83 private int numberOfThreads; 97 internal int numberOfThreads;
84 98
99 /// <summary>
100 /// Maximum time one function can use for execution before we perform a thread kill.
101 /// </summary>
102 private int maxFunctionExecutionTimems
103 {
104 get { return (int)(maxFunctionExecutionTimens / 10000); }
105 set { maxFunctionExecutionTimens = value * 10000; }
106 }
85 107
86 /// <summary> 108 /// <summary>
87 /// Maximum time one function can use for execution before we perform a thread kill 109 /// Contains nanoseconds version of maxFunctionExecutionTimems so that it matches time calculations better (performance reasons).
110 /// WARNING! ONLY UPDATE maxFunctionExecutionTimems, NEVER THIS DIRECTLY.
111 /// </summary>
112 public long maxFunctionExecutionTimens;
113 /// <summary>
114 /// Enforce max execution time
115 /// </summary>
116 public bool EnforceMaxExecutionTime;
117 /// <summary>
118 /// Kill script (unload) when it exceeds execution time
88 /// </summary> 119 /// </summary>
89 private int maxFunctionExecutionTimems;
90 private bool EnforceMaxExecutionTime;
91 private bool KillScriptOnMaxFunctionExecutionTime; 120 private bool KillScriptOnMaxFunctionExecutionTime;
92 121
122 /// <summary>
123 /// List of localID locks for mutex processing of script events
124 /// </summary>
125 private List<uint> objectLocks = new List<uint>();
126 private object tryLockLock = new object(); // Mutex lock object
93 127
94 /// <summary> 128 /// <summary>
95 /// Queue containing events waiting to be executed 129 /// Queue containing events waiting to be executed
96 /// </summary> 130 /// </summary>
97 public Queue<QueueItemStruct> eventQueue = new Queue<QueueItemStruct>(); 131 public Queue<QueueItemStruct> eventQueue = new Queue<QueueItemStruct>();
98 132
133 #region " Queue structures "
99 /// <summary> 134 /// <summary>
100 /// Queue item structure 135 /// Queue item structure
101 /// </summary> 136 /// </summary>
@@ -128,18 +163,9 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
128 public int[] _int; 163 public int[] _int;
129 public string[] _string; 164 public string[] _string;
130 } 165 }
166 #endregion
131 167
132 /// <summary> 168 #region " Initialization / Startup "
133 /// List of localID locks for mutex processing of script events
134 /// </summary>
135 private List<uint> objectLocks = new List<uint>();
136
137 private object tryLockLock = new object(); // Mutex lock object
138
139 public ScriptEngine m_ScriptEngine;
140
141 public Thread ExecutionTimeoutEnforcingThread;
142
143 public EventQueueManager(ScriptEngine _ScriptEngine) 169 public EventQueueManager(ScriptEngine _ScriptEngine)
144 { 170 {
145 m_ScriptEngine = _ScriptEngine; 171 m_ScriptEngine = _ScriptEngine;
@@ -167,66 +193,79 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
167 eventQueueThreadsLock = staticEventQueueThreadsLock; 193 eventQueueThreadsLock = staticEventQueueThreadsLock;
168 } 194 }
169 195
170 numberOfThreads = m_ScriptEngine.ScriptConfigSource.GetInt("NumberOfScriptThreads", 2); 196 ReadConfig();
171 197
198 }
199
200 private void ReadConfig()
201 {
202 numberOfThreads = m_ScriptEngine.ScriptConfigSource.GetInt("NumberOfScriptThreads", 2);
172 maxFunctionExecutionTimems = m_ScriptEngine.ScriptConfigSource.GetInt("MaxEventExecutionTimeMs", 5000); 203 maxFunctionExecutionTimems = m_ScriptEngine.ScriptConfigSource.GetInt("MaxEventExecutionTimeMs", 5000);
173 EnforceMaxExecutionTime = m_ScriptEngine.ScriptConfigSource.GetBoolean("EnforceMaxEventExecutionTime", false); 204 EnforceMaxExecutionTime = m_ScriptEngine.ScriptConfigSource.GetBoolean("EnforceMaxEventExecutionTime", false);
174 KillScriptOnMaxFunctionExecutionTime = m_ScriptEngine.ScriptConfigSource.GetBoolean("DeactivateScriptOnTimeout", false); 205 KillScriptOnMaxFunctionExecutionTime = m_ScriptEngine.ScriptConfigSource.GetBoolean("DeactivateScriptOnTimeout", false);
175 206
207 }
176 208
177 // Start function max exec time enforcement thread 209 #endregion
178 if (EnforceMaxExecutionTime) 210
179 { 211 #region " Shutdown all threads "
180 ExecutionTimeoutEnforcingThread = new Thread(ExecutionTimeoutEnforcingLoop); 212 ~EventQueueManager()
181 ExecutionTimeoutEnforcingThread.Name = "ExecutionTimeoutEnforcingThread"; 213 {
182 ExecutionTimeoutEnforcingThread.IsBackground = true; 214 Stop();
183 ExecutionTimeoutEnforcingThread.Start(); 215 }
184 }
185 216
186 // 217 private void Stop()
187 // Start event queue processing threads (worker threads) 218 {
188 //
189 219
220 // Kill worker threads
190 lock (eventQueueThreadsLock) 221 lock (eventQueueThreadsLock)
191 { 222 {
192 for (int ThreadCount = eventQueueThreads.Count; ThreadCount < numberOfThreads; ThreadCount++) 223 foreach (EventQueueThreadClass EventQueueThread in eventQueueThreads)
193 { 224 {
194 StartNewThreadClass(); 225 EventQueueThread.Shutdown();
195 } 226 }
227 eventQueueThreads.Clear();
228 staticGlobalEventQueueThreads.Clear();
229 }
230 // Remove all entries from our event queue
231 lock (queueLock)
232 {
233 eventQueue.Clear();
196 } 234 }
197 } 235 }
198 236
199 ~EventQueueManager() 237 #endregion
238
239
240 #region " Start / stop script execution threads (ThreadClasses) "
241 private void StartNewThreadClass()
200 { 242 {
243 EventQueueThreadClass eqtc = new EventQueueThreadClass(this);
244 eventQueueThreads.Add(eqtc);
245 staticGlobalEventQueueThreads.Add(eqtc);
246 m_ScriptEngine.Log.Debug("DotNetEngine", "Started new script execution thread. Current thread count: " + eventQueueThreads.Count);
247
248 }
249 private void AbortThreadClass(EventQueueThreadClass threadClass)
250 {
251 if (eventQueueThreads.Contains(threadClass))
252 eventQueueThreads.Remove(threadClass);
253 if (staticGlobalEventQueueThreads.Contains(threadClass))
254 staticGlobalEventQueueThreads.Remove(threadClass);
201 try 255 try
202 { 256 {
203 if (ExecutionTimeoutEnforcingThread != null) 257 threadClass.Shutdown();
204 {
205 if (ExecutionTimeoutEnforcingThread.IsAlive)
206 {
207 ExecutionTimeoutEnforcingThread.Abort();
208 }
209 }
210 } 258 }
211 catch (Exception ex) 259 catch (Exception ex)
212 { 260 {
261 m_ScriptEngine.Log.Error("EventQueueManager", "If you see this, could you please report it to Tedd:");
262 m_ScriptEngine.Log.Error("EventQueueManager", "Script thread execution timeout kill ended in exception: " + ex.ToString());
213 } 263 }
214 264 m_ScriptEngine.Log.Debug("DotNetEngine", "Killed script execution thread. Remaining thread count: " + eventQueueThreads.Count);
215
216 // Kill worker threads
217 lock (eventQueueThreadsLock)
218 {
219 foreach (EventQueueThreadClass EventQueueThread in new ArrayList(eventQueueThreads))
220 {
221 EventQueueThread.Shutdown();
222 }
223 eventQueueThreads.Clear();
224 }
225 // Todo: Clean up our queues
226 eventQueue.Clear();
227 } 265 }
266 #endregion
228 267
229 268 #region " Mutex locks for queue access "
230 /// <summary> 269 /// <summary>
231 /// Try to get a mutex lock on localID 270 /// Try to get a mutex lock on localID
232 /// </summary> 271 /// </summary>
@@ -262,8 +301,9 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
262 } 301 }
263 } 302 }
264 } 303 }
304 #endregion
265 305
266 306 #region " Add events to execution queue "
267 /// <summary> 307 /// <summary>
268 /// Add event to event execution queue 308 /// Add event to event execution queue
269 /// </summary> 309 /// </summary>
@@ -317,62 +357,72 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
317 eventQueue.Enqueue(QIS); 357 eventQueue.Enqueue(QIS);
318 } 358 }
319 } 359 }
360 #endregion
361
362 #region " Maintenance thread "
320 363
321 /// <summary> 364 /// <summary>
322 /// A thread should run in this loop and check all running scripts 365 /// 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.
366 /// Called from MaintenanceThread
323 /// </summary> 367 /// </summary>
324 public void ExecutionTimeoutEnforcingLoop() 368 public void AdjustNumberOfScriptThreads()
325 { 369 {
326 try 370 lock (eventQueueThreadsLock)
327 { 371 {
328 while (true) 372 int diff = numberOfThreads - eventQueueThreads.Count;
373 // Positive number: Start
374 // Negative number: too many are running
375 if (diff > 0)
329 { 376 {
330 System.Threading.Thread.Sleep(maxFunctionExecutionTimems); 377 // We need to add more threads
331 lock (eventQueueThreadsLock) 378 for (int ThreadCount = eventQueueThreads.Count; ThreadCount < numberOfThreads; ThreadCount++)
332 { 379 {
333 foreach (EventQueueThreadClass EventQueueThread in new ArrayList(eventQueueThreads)) 380 StartNewThreadClass();
334 { 381 }
335 if (EventQueueThread.InExecution) 382 }
336 { 383 if (diff < 0)
337 if (DateTime.Now.Subtract(EventQueueThread.LastExecutionStarted).Milliseconds > 384 {
338 maxFunctionExecutionTimems) 385 // We need to kill some threads
339 { 386 lock (ThreadsToExitLock)
340 // We need to kill this thread! 387 {
341 EventQueueThread.KillCurrentScript = KillScriptOnMaxFunctionExecutionTime; 388 ThreadsToExit = Math.Abs(diff);
342 AbortThreadClass(EventQueueThread);
343 // Then start another
344 StartNewThreadClass();
345 }
346 }
347 }
348 } 389 }
349 } 390 }
350 }
351 catch (ThreadAbortException tae)
352 {
353 } 391 }
354 } 392 }
355 393
356 private void AbortThreadClass(EventQueueThreadClass threadClass) 394 /// <summary>
395 /// Check if any thread class has been executing an event too long
396 /// </summary>
397 public void CheckScriptMaxExecTime()
357 { 398 {
358 try 399 // Iterate through all ScriptThreadClasses and check how long their current function has been executing
359 { 400 lock (eventQueueThreadsLock)
360 threadClass.Shutdown();
361 }
362 catch (Exception ex)
363 { 401 {
364 Console.WriteLine("Could you please report this to Tedd:"); 402 foreach (EventQueueThreadClass EventQueueThread in staticGlobalEventQueueThreads)
365 Console.WriteLine("Script thread execution timeout kill ended in exception: " + ex.ToString()); 403 {
404 // Is thread currently executing anything?
405 if (EventQueueThread.InExecution)
406 {
407 // Has execution time expired?
408 if (DateTime.Now.Ticks - EventQueueThread.LastExecutionStarted >
409 maxFunctionExecutionTimens)
410 {
411 // Yes! We need to kill this thread!
412
413 // Set flag if script should be removed or not
414 EventQueueThread.KillCurrentScript = KillScriptOnMaxFunctionExecutionTime;
415
416 // Abort this thread
417 AbortThreadClass(EventQueueThread);
418
419 // We do not need to start another, MaintenenceThread will do that for us
420 //StartNewThreadClass();
421 }
422 }
423 }
366 } 424 }
367 m_ScriptEngine.Log.Debug("DotNetEngine", "Killed script execution thread, count: " + eventQueueThreads.Count);
368 }
369
370 private void StartNewThreadClass()
371 {
372 EventQueueThreadClass eqtc = new EventQueueThreadClass(this);
373 eventQueueThreads.Add(eqtc);
374 m_ScriptEngine.Log.Debug("DotNetEngine", "Started new script execution thread, count: " + eventQueueThreads.Count);
375
376 } 425 }
426 #endregion
377 } 427 }
378} \ No newline at end of file 428} \ No newline at end of file