diff options
Diffstat (limited to 'OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs')
-rw-r--r-- | OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs | 242 |
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 |