aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs
diff options
context:
space:
mode:
authorTedd Hansen2008-02-01 19:07:05 +0000
committerTedd Hansen2008-02-01 19:07:05 +0000
commit5d6e89eaf924e741be95248d3ce5bc4ddbd5de3c (patch)
tree9861db7e5006d2c7b259fa2c8c266e066a7286b2 /OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs
parentFixed errors being thrown by invalid PSYS_SRC_TARGET_KEY's in llParticleSyste... (diff)
downloadopensim-SC_OLD-5d6e89eaf924e741be95248d3ce5bc4ddbd5de3c.zip
opensim-SC_OLD-5d6e89eaf924e741be95248d3ce5bc4ddbd5de3c.tar.gz
opensim-SC_OLD-5d6e89eaf924e741be95248d3ce5bc4ddbd5de3c.tar.bz2
opensim-SC_OLD-5d6e89eaf924e741be95248d3ce5bc4ddbd5de3c.tar.xz
Highly experimental
A separate thread is used to enforce max function (event) execution time for scripts.
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs248
1 files changed, 99 insertions, 149 deletions
diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs
index 949d5b6..70db724 100644
--- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs
+++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs
@@ -69,29 +69,31 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
69 /// <summary> 69 /// <summary>
70 /// List of threads processing event queue 70 /// List of threads processing event queue
71 /// </summary> 71 /// </summary>
72 private List<Thread> eventQueueThreads = new List<Thread>(); 72 private List<EventQueueThreadClass> eventQueueThreads = new List<EventQueueThreadClass>();
73 private object eventQueueThreadsLock = new object();
73 74
74 private object queueLock = new object(); // Mutex lock object 75 public object queueLock = new object(); // Mutex lock object
75 76
76 /// <summary> 77 /// <summary>
77 /// How many ms to sleep if queue is empty 78 /// How many threads to process queue with
78 /// </summary> 79 /// </summary>
79 private int nothingToDoSleepms = 50; 80 private int numberOfThreads = 2;
80 81
81 /// <summary> 82 /// <summary>
82 /// How many threads to process queue with 83 /// Maximum time one function can use for execution before we perform a thread kill
83 /// </summary> 84 /// </summary>
84 private int numberOfThreads = 2; 85 private int maxFunctionExecutionTimems = 50;
86 private bool EnforceMaxExecutionTime = true;
85 87
86 /// <summary> 88 /// <summary>
87 /// Queue containing events waiting to be executed 89 /// Queue containing events waiting to be executed
88 /// </summary> 90 /// </summary>
89 private Queue<QueueItemStruct> eventQueue = new Queue<QueueItemStruct>(); 91 public Queue<QueueItemStruct> eventQueue = new Queue<QueueItemStruct>();
90 92
91 /// <summary> 93 /// <summary>
92 /// Queue item structure 94 /// Queue item structure
93 /// </summary> 95 /// </summary>
94 private struct QueueItemStruct 96 public struct QueueItemStruct
95 { 97 {
96 public uint localID; 98 public uint localID;
97 public LLUUID itemID; 99 public LLUUID itemID;
@@ -118,7 +120,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
118 public LSL_Types.Vector3[] _Vector3; 120 public LSL_Types.Vector3[] _Vector3;
119 public bool[] _bool; 121 public bool[] _bool;
120 public int[] _int; 122 public int[] _int;
121 public string [] _string; 123 public string[] _string;
122 } 124 }
123 125
124 /// <summary> 126 /// <summary>
@@ -128,177 +130,72 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
128 130
129 private object tryLockLock = new object(); // Mutex lock object 131 private object tryLockLock = new object(); // Mutex lock object
130 132
131 private ScriptEngine m_ScriptEngine; 133 public ScriptEngine m_ScriptEngine;
134
135 public Thread ExecutionTimeoutEnforcingThread;
132 136
133 public EventQueueManager(ScriptEngine _ScriptEngine) 137 public EventQueueManager(ScriptEngine _ScriptEngine)
134 { 138 {
135 m_ScriptEngine = _ScriptEngine; 139 m_ScriptEngine = _ScriptEngine;
136 140
141 // Start function max exec time enforcement thread
142 if (EnforceMaxExecutionTime)
143 {
144 ExecutionTimeoutEnforcingThread = new Thread(ExecutionTimeoutEnforcingLoop);
145 ExecutionTimeoutEnforcingThread.Name = "ExecutionTimeoutEnforcingThread";
146 ExecutionTimeoutEnforcingThread.IsBackground = true;
147 ExecutionTimeoutEnforcingThread.Start();
148 }
149
137 // 150 //
138 // Start event queue processing threads (worker threads) 151 // Start event queue processing threads (worker threads)
139 // 152 //
140 for (int ThreadCount = 0; ThreadCount <= numberOfThreads; ThreadCount++) 153 lock (eventQueueThreadsLock)
141 { 154 {
142 Thread EventQueueThread = new Thread(EventQueueThreadLoop); 155 for (int ThreadCount = 0; ThreadCount <= numberOfThreads; ThreadCount++)
143 eventQueueThreads.Add(EventQueueThread); 156 {
144 EventQueueThread.IsBackground = true; 157 StartNewThreadClass();
145 EventQueueThread.Priority = ThreadPriority.BelowNormal; 158 }
146 EventQueueThread.Name = "EventQueueManagerThread_" + ThreadCount;
147 EventQueueThread.Start();
148 } 159 }
149 } 160 }
150 161
151 ~EventQueueManager() 162 ~EventQueueManager()
152 { 163 {
153 // Kill worker threads 164 try
154 foreach (Thread EventQueueThread in new ArrayList(eventQueueThreads))
155 { 165 {
156 if (EventQueueThread != null && EventQueueThread.IsAlive == true) 166 if (ExecutionTimeoutEnforcingThread != null)
157 { 167 {
158 try 168 if (ExecutionTimeoutEnforcingThread.IsAlive)
159 {
160 EventQueueThread.Abort();
161 EventQueueThread.Join();
162 }
163 catch (Exception)
164 { 169 {
165 //myScriptEngine.Log.Verbose("ScriptEngine", "EventQueueManager Exception killing worker thread: " + e.ToString()); 170 ExecutionTimeoutEnforcingThread.Abort();
166 } 171 }
167 } 172 }
168 } 173 }
169 eventQueueThreads.Clear(); 174 catch (Exception ex)
170 // Todo: Clean up our queues
171 eventQueue.Clear();
172 }
173
174 /// <summary>
175 /// Queue processing thread loop
176 /// </summary>
177 private void EventQueueThreadLoop()
178 {
179 //myScriptEngine.m_logger.Verbose("ScriptEngine", "EventQueueManager Worker thread spawned");
180 try
181 { 175 {
182 QueueItemStruct BlankQIS = new QueueItemStruct(); 176 }
183 while (true)
184 {
185 try
186 {
187 QueueItemStruct QIS = BlankQIS;
188 bool GotItem = false;
189 177
190 if (eventQueue.Count == 0)
191 {
192 // Nothing to do? Sleep a bit waiting for something to do
193 Thread.Sleep(nothingToDoSleepms);
194 }
195 else
196 {
197 // Something in queue, process
198 //myScriptEngine.m_logger.Verbose("ScriptEngine", "Processing event for localID: " + QIS.localID + ", itemID: " + QIS.itemID + ", FunctionName: " + QIS.FunctionName);
199 178
200 // OBJECT BASED LOCK - TWO THREADS WORKING ON SAME OBJECT IS NOT GOOD 179 // Kill worker threads
201 lock (queueLock) 180 lock (eventQueueThreadsLock)
202 {
203 GotItem = false;
204 for (int qc = 0; qc < eventQueue.Count; qc++)
205 {
206 // Get queue item
207 QIS = eventQueue.Dequeue();
208
209 // Check if object is being processed by someone else
210 if (TryLock(QIS.localID) == false)
211 {
212 // Object is already being processed, requeue it
213 eventQueue.Enqueue(QIS);
214 }
215 else
216 {
217 // We have lock on an object and can process it
218 GotItem = true;
219 break;
220 }
221 } // go through queue
222 } // lock
223
224 if (GotItem == true)
225 {
226 // Execute function
227 try
228 {
229#if DEBUG
230 m_ScriptEngine.Log.Debug("ScriptEngine", "Executing event:\r\n"
231 + "QIS.localID: " + QIS.localID
232 + ", QIS.itemID: " + QIS.itemID
233 + ", QIS.functionName: " + QIS.functionName);
234#endif
235 m_ScriptEngine.m_ScriptManager.ExecuteEvent(QIS.localID, QIS.itemID,
236 QIS.functionName, QIS.llDetectParams, QIS.param);
237 }
238 catch (Exception e)
239 {
240 // DISPLAY ERROR INWORLD
241 string text = "Error executing script function \"" + QIS.functionName + "\":\r\n";
242 if (e.InnerException != null)
243 {
244 // Send inner exception
245 text += e.InnerException.Message.ToString();
246 }
247 else
248 {
249 text += "\r\n";
250 // Send normal
251 text += e.Message.ToString();
252 }
253 try
254 {
255 if (text.Length > 1500)
256 text = text.Substring(0, 1500);
257 IScriptHost m_host = m_ScriptEngine.World.GetSceneObjectPart(QIS.localID);
258 //if (m_host != null)
259 //{
260 m_ScriptEngine.World.SimChat(Helpers.StringToField(text), ChatTypeEnum.Say, 0,
261 m_host.AbsolutePosition, m_host.Name, m_host.UUID);
262 }
263 catch
264 {
265 //}
266 //else
267 //{
268 // T oconsole
269 m_ScriptEngine.Log.Error("ScriptEngine",
270 "Unable to send text in-world:\r\n" + text);
271 }
272 }
273 finally
274 {
275 ReleaseLock(QIS.localID);
276 }
277 }
278 } // Something in queue
279 }
280 catch (ThreadAbortException tae)
281 {
282 throw tae;
283 }
284 catch (Exception e)
285 {
286 m_ScriptEngine.Log.Error("ScriptEngine", "Exception in EventQueueThreadLoop: " + e.ToString());
287 }
288 } // while
289 } // try
290 catch (ThreadAbortException)
291 { 181 {
292 //myScriptEngine.Log.Verbose("ScriptEngine", "EventQueueManager Worker thread killed: " + tae.Message); 182 foreach (EventQueueThreadClass EventQueueThread in new ArrayList(eventQueueThreads))
183 {
184 EventQueueThread.Shutdown();
185 }
186 eventQueueThreads.Clear();
293 } 187 }
188 // Todo: Clean up our queues
189 eventQueue.Clear();
294 } 190 }
295 191
192
296 /// <summary> 193 /// <summary>
297 /// Try to get a mutex lock on localID 194 /// Try to get a mutex lock on localID
298 /// </summary> 195 /// </summary>
299 /// <param name="localID"></param> 196 /// <param name="localID"></param>
300 /// <returns></returns> 197 /// <returns></returns>
301 private bool TryLock(uint localID) 198 public bool TryLock(uint localID)
302 { 199 {
303 lock (tryLockLock) 200 lock (tryLockLock)
304 { 201 {
@@ -318,7 +215,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
318 /// Release mutex lock on localID 215 /// Release mutex lock on localID
319 /// </summary> 216 /// </summary>
320 /// <param name="localID"></param> 217 /// <param name="localID"></param>
321 private void ReleaseLock(uint localID) 218 public void ReleaseLock(uint localID)
322 { 219 {
323 lock (tryLockLock) 220 lock (tryLockLock)
324 { 221 {
@@ -383,5 +280,58 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
383 eventQueue.Enqueue(QIS); 280 eventQueue.Enqueue(QIS);
384 } 281 }
385 } 282 }
283
284 /// <summary>
285 /// A thread should run in this loop and check all running scripts
286 /// </summary>
287 public void ExecutionTimeoutEnforcingLoop()
288 {
289 try
290 {
291 while (true)
292 {
293 System.Threading.Thread.Sleep(maxFunctionExecutionTimems);
294 lock (eventQueueThreadsLock)
295 {
296 foreach (EventQueueThreadClass EventQueueThread in new ArrayList(eventQueueThreads))
297 {
298 if (EventQueueThread.InExecution)
299 {
300 if (DateTime.Now.Subtract(EventQueueThread.LastExecutionStarted).Milliseconds >
301 maxFunctionExecutionTimems)
302 {
303 // We need to kill this thread!
304 AbortThreadClass(EventQueueThread);
305 // Then start another
306 StartNewThreadClass();
307 }
308 }
309 }
310 }
311 }
312 }
313 catch (ThreadAbortException tae)
314 {
315 }
316 }
317
318 private static void AbortThreadClass(EventQueueThreadClass threadClass)
319 {
320 try
321 {
322 threadClass.Shutdown();
323 }
324 catch (Exception ex)
325 {
326 Console.WriteLine("Could you please report this to Tedd:");
327 Console.WriteLine("Script thread execution timeout kill ended in exception: " + ex.ToString());
328 }
329 }
330
331 private void StartNewThreadClass()
332 {
333 EventQueueThreadClass eqtc = new EventQueueThreadClass(this);
334 eventQueueThreads.Add(eqtc);
335 }
386 } 336 }
387} \ No newline at end of file 337} \ No newline at end of file