aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorTedd Hansen2008-02-01 22:18:55 +0000
committerTedd Hansen2008-02-01 22:18:55 +0000
commita6726b0c9de4ef875348f9dcbf21a9c93bde8a09 (patch)
treebcf0fbbe7306172e2933f59799813f84ed5a621f
parentThank you, Kinoc for the ChatModule.cs updates. (diff)
downloadopensim-SC-a6726b0c9de4ef875348f9dcbf21a9c93bde8a09.zip
opensim-SC-a6726b0c9de4ef875348f9dcbf21a9c93bde8a09.tar.gz
opensim-SC-a6726b0c9de4ef875348f9dcbf21a9c93bde8a09.tar.bz2
opensim-SC-a6726b0c9de4ef875348f9dcbf21a9c93bde8a09.tar.xz
SCRIPT SUPPORT IS STILL BROKEN.
Bugfix: Scripts exceeding max and set to be killed were not killed, only removed. Added ability to re-read configuration while OpenSim is running All regions now sharing one MaintenanceThread New MaintenanceThread: - checks for script execution timeout - re-reads config - starts/stops threads if thread active count becomes too high/low compared to config Speed increase on event execution: - Reuse of try{}catch{} blocks - Time calculation on event execution
-rw-r--r--OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs242
-rw-r--r--OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueThreadClass.cs220
-rw-r--r--OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/MaintenanceThread.cs127
-rw-r--r--OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptEngine.cs12
-rw-r--r--bin/OpenSim.ini.example3
5 files changed, 415 insertions, 189 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
diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueThreadClass.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueThreadClass.cs
index 57caad5..e610c36 100644
--- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueThreadClass.cs
+++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueThreadClass.cs
@@ -19,7 +19,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
19 /// </summary> 19 /// </summary>
20 private int nothingToDoSleepms;// = 50; 20 private int nothingToDoSleepms;// = 50;
21 21
22 public DateTime LastExecutionStarted; 22 public long LastExecutionStarted;
23 public bool InExecution = false; 23 public bool InExecution = false;
24 public bool KillCurrentScript = false; 24 public bool KillCurrentScript = false;
25 25
@@ -109,120 +109,154 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
109 //myScriptEngine.m_logger.Verbose("ScriptEngine", "EventQueueManager Worker thread spawned"); 109 //myScriptEngine.m_logger.Verbose("ScriptEngine", "EventQueueManager Worker thread spawned");
110 try 110 try
111 { 111 {
112 EventQueueManager.QueueItemStruct BlankQIS = new EventQueueManager.QueueItemStruct(); 112 while (true)
113 while (true)
114 { 113 {
115 try 114 try
116 { 115 {
117 EventQueueManager.QueueItemStruct QIS = BlankQIS; 116 EventQueueManager.QueueItemStruct BlankQIS = new EventQueueManager.QueueItemStruct();
118 bool GotItem = false; 117 while (true)
119
120 if (eventQueueManager.eventQueue.Count == 0)
121 {
122 // Nothing to do? Sleep a bit waiting for something to do
123 Thread.Sleep(nothingToDoSleepms);
124 }
125 else
126 { 118 {
127 // Something in queue, process 119 // Every now and then check if we should shut down
128 //myScriptEngine.m_logger.Verbose("ScriptEngine", "Processing event for localID: " + QIS.localID + ", itemID: " + QIS.itemID + ", FunctionName: " + QIS.FunctionName); 120 if (eventQueueManager.ThreadsToExit > 0)
129
130 // OBJECT BASED LOCK - TWO THREADS WORKING ON SAME OBJECT IS NOT GOOD
131 lock (eventQueueManager.queueLock)
132 { 121 {
133 GotItem = false; 122 // Someone should shut down, lets get exclusive lock
134 for (int qc = 0; qc < eventQueueManager.eventQueue.Count; qc++) 123 lock (eventQueueManager.ThreadsToExitLock)
135 { 124 {
136 // Get queue item 125 // Lets re-check in case someone grabbed it
137 QIS = eventQueueManager.eventQueue.Dequeue(); 126 if (eventQueueManager.ThreadsToExit > 0)
138
139 // Check if object is being processed by someone else
140 if (eventQueueManager.TryLock(QIS.localID) == false)
141 {
142 // Object is already being processed, requeue it
143 eventQueueManager.eventQueue.Enqueue(QIS);
144 }
145 else
146 { 127 {
147 // We have lock on an object and can process it 128 // We are go for shutdown
148 GotItem = true; 129 eventQueueManager.ThreadsToExit--;
149 break; 130 Shutdown();
131 return;
150 } 132 }
151 } // go through queue 133 }
152 } // lock 134 }
153 135
154 if (GotItem == true) 136
137 //try
138 // {
139 EventQueueManager.QueueItemStruct QIS = BlankQIS;
140 bool GotItem = false;
141
142 if (eventQueueManager.eventQueue.Count == 0)
155 { 143 {
156 // Execute function 144 // Nothing to do? Sleep a bit waiting for something to do
157 try 145 Thread.Sleep(nothingToDoSleepms);
158 { 146 }
159#if DEBUG 147 else
160 eventQueueManager.m_ScriptEngine.Log.Debug("ScriptEngine", "Executing event:\r\n" 148 {
161 + "QIS.localID: " + QIS.localID 149 // Something in queue, process
162 + ", QIS.itemID: " + QIS.itemID 150 //myScriptEngine.m_logger.Verbose("ScriptEngine", "Processing event for localID: " + QIS.localID + ", itemID: " + QIS.itemID + ", FunctionName: " + QIS.FunctionName);
163 + ", QIS.functionName: " + QIS.functionName); 151
164#endif 152 // OBJECT BASED LOCK - TWO THREADS WORKING ON SAME OBJECT IS NOT GOOD
165 LastExecutionStarted = DateTime.Now; 153 lock (eventQueueManager.queueLock)
166 KillCurrentScript = false;
167 InExecution = true;
168 eventQueueManager.m_ScriptEngine.m_ScriptManager.ExecuteEvent(QIS.localID, QIS.itemID,
169 QIS.functionName, QIS.llDetectParams, QIS.param);
170 InExecution = false;
171 }
172 catch (Exception e)
173 { 154 {
174 InExecution = false; 155 GotItem = false;
175 // DISPLAY ERROR INWORLD 156 for (int qc = 0; qc < eventQueueManager.eventQueue.Count; qc++)
176 string text = "Error executing script function \"" + QIS.functionName + "\":\r\n";
177 if (e.InnerException != null)
178 { 157 {
179 // Send inner exception 158 // Get queue item
180 text += e.InnerException.Message.ToString(); 159 QIS = eventQueueManager.eventQueue.Dequeue();
181 } 160
182 else 161 // Check if object is being processed by someone else
183 { 162 if (eventQueueManager.TryLock(QIS.localID) == false)
184 text += "\r\n"; 163 {
185 // Send normal 164 // Object is already being processed, requeue it
186 text += e.Message.ToString(); 165 eventQueueManager.eventQueue.Enqueue(QIS);
187 } 166 }
188 if (KillCurrentScript) 167 else
189 text += "\r\nScript will be deactivated!"; 168 {
169 // We have lock on an object and can process it
170 GotItem = true;
171 break;
172 }
173 } // go through queue
174 } // lock
190 175
176 if (GotItem == true)
177 {
178 // Execute function
191 try 179 try
192 { 180 {
193 if (text.Length > 1500) 181#if DEBUG
194 text = text.Substring(0, 1500); 182 eventQueueManager.m_ScriptEngine.Log.Debug("ScriptEngine",
195 IScriptHost m_host = eventQueueManager.m_ScriptEngine.World.GetSceneObjectPart(QIS.localID); 183 "Executing event:\r\n"
196 //if (m_host != null) 184 + "QIS.localID: " + QIS.localID
197 //{ 185 + ", QIS.itemID: " + QIS.itemID
198 eventQueueManager.m_ScriptEngine.World.SimChat(Helpers.StringToField(text), ChatTypeEnum.Say, 0, 186 + ", QIS.functionName: " +
199 m_host.AbsolutePosition, m_host.Name, m_host.UUID); 187 QIS.functionName);
200 } 188#endif
201 catch 189 LastExecutionStarted = DateTime.Now.Ticks;
202 { 190 KillCurrentScript = false;
203 //} 191 InExecution = true;
204 //else 192 eventQueueManager.m_ScriptEngine.m_ScriptManager.ExecuteEvent(QIS.localID,
205 //{ 193 QIS.itemID,
206 // T oconsole 194 QIS.functionName,
207 eventQueueManager.m_ScriptEngine.Log.Error("ScriptEngine", 195 QIS.llDetectParams,
208 "Unable to send text in-world:\r\n" + text); 196 QIS.param);
197 InExecution = false;
209 } 198 }
210 finally 199 catch (Exception e)
211 { 200 {
212 // So we are done sending message in-world 201 InExecution = false;
202 // DISPLAY ERROR INWORLD
203 string text = "Error executing script function \"" + QIS.functionName +
204 "\":\r\n";
205 if (e.InnerException != null)
206 {
207 // Send inner exception
208 text += e.InnerException.Message.ToString();
209 }
210 else
211 {
212 text += "\r\n";
213 // Send normal
214 text += e.Message.ToString();
215 }
213 if (KillCurrentScript) 216 if (KillCurrentScript)
217 text += "\r\nScript will be deactivated!";
218
219 try
220 {
221 if (text.Length > 1500)
222 text = text.Substring(0, 1500);
223 IScriptHost m_host =
224 eventQueueManager.m_ScriptEngine.World.GetSceneObjectPart(QIS.localID);
225 //if (m_host != null)
226 //{
227 eventQueueManager.m_ScriptEngine.World.SimChat(Helpers.StringToField(text),
228 ChatTypeEnum.Say, 0,
229 m_host.AbsolutePosition,
230 m_host.Name, m_host.UUID);
231 }
232 catch
214 { 233 {
215 eventQueueManager.m_ScriptEngine.m_ScriptManager.RemoveScript(QIS.localID, QIS.itemID); 234 //}
235 //else
236 //{
237 // T oconsole
238 eventQueueManager.m_ScriptEngine.Log.Error("ScriptEngine",
239 "Unable to send text in-world:\r\n" +
240 text);
241 }
242 finally
243 {
244 // So we are done sending message in-world
245 if (KillCurrentScript)
246 {
247 eventQueueManager.m_ScriptEngine.m_ScriptManager.StopScript(
248 QIS.localID, QIS.itemID);
249 }
216 } 250 }
217 } 251 }
252 finally
253 {
254 InExecution = false;
255 eventQueueManager.ReleaseLock(QIS.localID);
256 }
218 } 257 }
219 finally 258 } // Something in queue
220 { 259 }
221 InExecution = false;
222 eventQueueManager.ReleaseLock(QIS.localID);
223 }
224 }
225 } // Something in queue
226 } 260 }
227 catch (ThreadAbortException tae) 261 catch (ThreadAbortException tae)
228 { 262 {
diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/MaintenanceThread.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/MaintenanceThread.cs
new file mode 100644
index 0000000..9536291
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/MaintenanceThread.cs
@@ -0,0 +1,127 @@
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.Threading;
5
6namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
7{
8 /// <summary>
9 /// This class does maintenance on script engine.
10 /// </summary>
11 public class MaintenanceThread
12 {
13 public ScriptEngine m_ScriptEngine;
14 private int MaintenanceLoopms;
15
16 public MaintenanceThread(ScriptEngine _ScriptEngine)
17 {
18 m_ScriptEngine = _ScriptEngine;
19
20 ReadConfig();
21
22 // Start maintenance thread
23 StartMaintenanceThread();
24 }
25
26 ~MaintenanceThread()
27 {
28 StopMaintenanceThread();
29 }
30
31 private void ReadConfig()
32 {
33 MaintenanceLoopms = m_ScriptEngine.ScriptConfigSource.GetInt("MaintenanceLoopms", 50);
34 }
35
36
37 #region " Maintenance thread "
38 /// <summary>
39 /// Maintenance thread. Enforcing max execution time for example.
40 /// </summary>
41 public static Thread MaintenanceThreadThread;
42
43 /// <summary>
44 /// Starts maintenance thread
45 /// </summary>
46 private void StartMaintenanceThread()
47 {
48 StopMaintenanceThread();
49
50 MaintenanceThreadThread = new Thread(MaintenanceLoop);
51 MaintenanceThreadThread.Name = "ScriptMaintenanceThread";
52 MaintenanceThreadThread.IsBackground = true;
53 MaintenanceThreadThread.Start();
54 }
55
56 /// <summary>
57 /// Stops maintenance thread
58 /// </summary>
59 private void StopMaintenanceThread()
60 {
61 try
62 {
63 if (MaintenanceThreadThread != null)
64 {
65 if (MaintenanceThreadThread.IsAlive)
66 {
67 MaintenanceThreadThread.Abort();
68 }
69 }
70 }
71 catch (Exception ex)
72 {
73 m_ScriptEngine.Log.Error("EventQueueManager", "Exception stopping maintenence thread: " + ex.ToString());
74 }
75
76 }
77
78 /// <summary>
79 /// A thread should run in this loop and check all running scripts
80 /// </summary>
81 public void MaintenanceLoop()
82 {
83 try
84 {
85 long Last_maxFunctionExecutionTimens = 0;// DateTime.Now.Ticks;
86 long Last_ReReadConfigFilens = DateTime.Now.Ticks;
87 while (true)
88 {
89 System.Threading.Thread.Sleep(MaintenanceLoopms); // Sleep
90
91 // Re-reading config every x seconds?
92 if (m_ScriptEngine.ReReadConfigFileSeconds > 0)
93 {
94 // Check if its time to re-read config
95 if (DateTime.Now.Ticks - Last_ReReadConfigFilens > m_ScriptEngine.ReReadConfigFilens)
96 {
97 // Its time to re-read config file
98 m_ScriptEngine.ConfigSource.Reload(); // Re-read config
99 Last_ReReadConfigFilens = DateTime.Now.Ticks; // Reset time
100 }
101 }
102
103 // Adjust number of running script threads if not correct
104 if (m_ScriptEngine.m_EventQueueManager.eventQueueThreads.Count != m_ScriptEngine.m_EventQueueManager.numberOfThreads)
105 {
106 m_ScriptEngine.m_EventQueueManager.AdjustNumberOfScriptThreads();
107 }
108
109
110 // Check if any script has exceeded its max execution time
111 if (m_ScriptEngine.m_EventQueueManager.EnforceMaxExecutionTime)
112 {
113 if (DateTime.Now.Ticks - Last_maxFunctionExecutionTimens > m_ScriptEngine.m_EventQueueManager.maxFunctionExecutionTimens)
114 {
115 m_ScriptEngine.m_EventQueueManager.CheckScriptMaxExecTime(); // Do check
116 Last_maxFunctionExecutionTimens = DateTime.Now.Ticks; // Reset time
117 }
118 }
119 }
120 }
121 catch (ThreadAbortException tae)
122 {
123 }
124 }
125 #endregion
126 }
127}
diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptEngine.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptEngine.cs
index fa40947..cfcc36e 100644
--- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptEngine.cs
+++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptEngine.cs
@@ -55,6 +55,15 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
55 public IConfig ScriptConfigSource; 55 public IConfig ScriptConfigSource;
56 public abstract string ScriptConfigSourceName { get; } 56 public abstract string ScriptConfigSourceName { get; }
57 57
58 /// <summary>
59 /// How many seconds between re-reading config-file. 0 = never. ScriptEngine will try to adjust to new config changes.
60 /// </summary>
61 public int ReReadConfigFileSeconds {
62 get { return (int)(ReReadConfigFilens / 10000); }
63 set { ReReadConfigFilens = value * 10000; }
64 }
65 public long ReReadConfigFilens = 0;
66
58 public ScriptManager GetScriptManager() 67 public ScriptManager GetScriptManager()
59 { 68 {
60 return _GetScriptManager(); 69 return _GetScriptManager();
@@ -93,6 +102,9 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
93 m_AppDomainManager = new AppDomainManager(ScriptConfigSource.GetInt("ScriptsPerAppDomain", 1)); 102 m_AppDomainManager = new AppDomainManager(ScriptConfigSource.GetInt("ScriptsPerAppDomain", 1));
94 m_LSLLongCmdHandler = new LSLLongCmdHandler(this); 103 m_LSLLongCmdHandler = new LSLLongCmdHandler(this);
95 104
105 ReReadConfigFileSeconds = ScriptConfigSource.GetInt("ReReadConfig", 0);
106
107
96 // Should we iterate the region for scripts that needs starting? 108 // Should we iterate the region for scripts that needs starting?
97 // Or can we assume we are loaded before anything else so we can use proper events? 109 // Or can we assume we are loaded before anything else so we can use proper events?
98 } 110 }
diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example
index 0e3349e..9d664bf 100644
--- a/bin/OpenSim.ini.example
+++ b/bin/OpenSim.ini.example
@@ -135,6 +135,7 @@ ScriptThreadPriority=BelowNormal
135; true: Each region will get <NumberOfScriptThreads> dedicated to scripts within that region 135; true: Each region will get <NumberOfScriptThreads> dedicated to scripts within that region
136; Number of threads will be <NumberOfScriptThreads>*<NumberOfRegions> 136; Number of threads will be <NumberOfScriptThreads>*<NumberOfRegions>
137; false: All regions share <NumberOfScriptThreads> for all their scripts 137; false: All regions share <NumberOfScriptThreads> for all their scripts
138; Note! If you run multiple script engines based on "OpenSim.Region.ScriptEngine.Common" then all of them will share the same threads.
138PrivateRegionThreads=false 139PrivateRegionThreads=false
139 140
140; How long MAX should a script event be allowed to run (per event execution)? 141; How long MAX should a script event be allowed to run (per event execution)?
@@ -163,3 +164,5 @@ SleepTimeIfNoScriptExecutionMs=50
163; Each AppDomain has some memory overhead. But leaving dead scripts in memory also has memory overhead. 164; Each AppDomain has some memory overhead. But leaving dead scripts in memory also has memory overhead.
164ScriptsPerAppDomain=1 165ScriptsPerAppDomain=1
165 166
167; ReRead ScriptEngine config options how often?
168ReReadConfig=0