diff options
author | Tedd Hansen | 2008-02-22 14:09:38 +0000 |
---|---|---|
committer | Tedd Hansen | 2008-02-22 14:09:38 +0000 |
commit | a43bb10000ca0b58af2f33750ea17cb94bfbad7d (patch) | |
tree | 9f913c185d3ee90a3e88507bbd32e7c6bbab0c9d /OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueThreadClass.cs | |
parent | Maintenance thread in charge of loading/unloading of scripts. 1 thread less p... (diff) | |
download | opensim-SC_OLD-a43bb10000ca0b58af2f33750ea17cb94bfbad7d.zip opensim-SC_OLD-a43bb10000ca0b58af2f33750ea17cb94bfbad7d.tar.gz opensim-SC_OLD-a43bb10000ca0b58af2f33750ea17cb94bfbad7d.tar.bz2 opensim-SC_OLD-a43bb10000ca0b58af2f33750ea17cb94bfbad7d.tar.xz |
Execution threads are now shared between regions too. Default thread count regardless of number of regions is now 3. This will save you around 33 threads for a normal 3x3 region server.
But, this is totally completely untested. So it probably won't work for another patch or five.
Diffstat (limited to 'OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueThreadClass.cs')
-rw-r--r-- | OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueThreadClass.cs | 402 |
1 files changed, 209 insertions, 193 deletions
diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueThreadClass.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueThreadClass.cs index 11fd896..f36baa7 100644 --- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueThreadClass.cs +++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueThreadClass.cs | |||
@@ -40,27 +40,27 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase | |||
40 | /// <summary> | 40 | /// <summary> |
41 | /// Because every thread needs some data set for it (time started to execute current function), it will do its work within a class | 41 | /// Because every thread needs some data set for it (time started to execute current function), it will do its work within a class |
42 | /// </summary> | 42 | /// </summary> |
43 | public class EventQueueThreadClass: iScriptEngineFunctionModule | 43 | public class EventQueueThreadClass : iScriptEngineFunctionModule |
44 | { | 44 | { |
45 | /// <summary> | 45 | /// <summary> |
46 | /// How many ms to sleep if queue is empty | 46 | /// How many ms to sleep if queue is empty |
47 | /// </summary> | 47 | /// </summary> |
48 | private int nothingToDoSleepms;// = 50; | 48 | private static int nothingToDoSleepms;// = 50; |
49 | private ThreadPriority MyThreadPriority; | 49 | private static ThreadPriority MyThreadPriority; |
50 | 50 | ||
51 | public long LastExecutionStarted; | 51 | public long LastExecutionStarted; |
52 | public bool InExecution = false; | 52 | public bool InExecution = false; |
53 | public bool KillCurrentScript = false; | 53 | public bool KillCurrentScript = false; |
54 | 54 | ||
55 | private EventQueueManager eventQueueManager; | 55 | //private EventQueueManager eventQueueManager; |
56 | public Thread EventQueueThread; | 56 | public Thread EventQueueThread; |
57 | private static int ThreadCount = 0; | 57 | private static int ThreadCount = 0; |
58 | 58 | ||
59 | private string ScriptEngineName = "ScriptEngine.Common"; | 59 | private string ScriptEngineName = "ScriptEngine.Common"; |
60 | 60 | ||
61 | public EventQueueThreadClass(EventQueueManager eqm) | 61 | public EventQueueThreadClass()//EventQueueManager eqm |
62 | { | 62 | { |
63 | eventQueueManager = eqm; | 63 | //eventQueueManager = eqm; |
64 | ReadConfig(); | 64 | ReadConfig(); |
65 | Start(); | 65 | Start(); |
66 | } | 66 | } |
@@ -72,32 +72,36 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase | |||
72 | 72 | ||
73 | public void ReadConfig() | 73 | public void ReadConfig() |
74 | { | 74 | { |
75 | ScriptEngineName = eventQueueManager.m_ScriptEngine.ScriptEngineName; | 75 | foreach (ScriptEngine m_ScriptEngine in ScriptEngine.ScriptEngines) |
76 | nothingToDoSleepms = eventQueueManager.m_ScriptEngine.ScriptConfigSource.GetInt("SleepTimeIfNoScriptExecutionMs", 50); | ||
77 | |||
78 | // Later with ScriptServer we might want to ask OS for stuff too, so doing this a bit manually | ||
79 | string pri = eventQueueManager.m_ScriptEngine.ScriptConfigSource.GetString("ScriptThreadPriority", "BelowNormal"); | ||
80 | switch (pri.ToLower()) | ||
81 | { | 76 | { |
82 | case "lowest": | 77 | ScriptEngineName = m_ScriptEngine.ScriptEngineName; |
83 | MyThreadPriority = ThreadPriority.Lowest; | 78 | nothingToDoSleepms = m_ScriptEngine.ScriptConfigSource.GetInt("SleepTimeIfNoScriptExecutionMs", 50); |
84 | break; | 79 | |
85 | case "belownormal": | 80 | // Later with ScriptServer we might want to ask OS for stuff too, so doing this a bit manually |
86 | MyThreadPriority = ThreadPriority.BelowNormal; | 81 | string pri = m_ScriptEngine.ScriptConfigSource.GetString("ScriptThreadPriority", "BelowNormal"); |
87 | break; | 82 | switch (pri.ToLower()) |
88 | case "normal": | 83 | { |
89 | MyThreadPriority = ThreadPriority.Normal; | 84 | case "lowest": |
90 | break; | 85 | MyThreadPriority = ThreadPriority.Lowest; |
91 | case "abovenormal": | 86 | break; |
92 | MyThreadPriority = ThreadPriority.AboveNormal; | 87 | case "belownormal": |
93 | break; | 88 | MyThreadPriority = ThreadPriority.BelowNormal; |
94 | case "highest": | 89 | break; |
95 | MyThreadPriority = ThreadPriority.Highest; | 90 | case "normal": |
96 | break; | 91 | MyThreadPriority = ThreadPriority.Normal; |
97 | default: | 92 | break; |
98 | MyThreadPriority = ThreadPriority.BelowNormal; // Default | 93 | case "abovenormal": |
99 | eventQueueManager.m_ScriptEngine.Log.Error("[ScriptEngineBase]: Unknown priority type \"" + pri + "\" in config file. Defaulting to \"BelowNormal\"."); | 94 | MyThreadPriority = ThreadPriority.AboveNormal; |
100 | break; | 95 | break; |
96 | case "highest": | ||
97 | MyThreadPriority = ThreadPriority.Highest; | ||
98 | break; | ||
99 | default: | ||
100 | MyThreadPriority = ThreadPriority.BelowNormal; // Default | ||
101 | m_ScriptEngine.Log.Error("[ScriptEngineBase]: Unknown priority type \"" + pri + | ||
102 | "\" in config file. Defaulting to \"BelowNormal\"."); | ||
103 | break; | ||
104 | } | ||
101 | } | 105 | } |
102 | 106 | ||
103 | // Now set that priority | 107 | // Now set that priority |
@@ -113,7 +117,7 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase | |||
113 | { | 117 | { |
114 | EventQueueThread = new Thread(EventQueueThreadLoop); | 118 | EventQueueThread = new Thread(EventQueueThreadLoop); |
115 | EventQueueThread.IsBackground = true; | 119 | EventQueueThread.IsBackground = true; |
116 | 120 | ||
117 | EventQueueThread.Priority = MyThreadPriority; | 121 | EventQueueThread.Priority = MyThreadPriority; |
118 | EventQueueThread.Name = "EventQueueManagerThread_" + ThreadCount; | 122 | EventQueueThread.Name = "EventQueueManagerThread_" + ThreadCount; |
119 | EventQueueThread.Start(); | 123 | EventQueueThread.Start(); |
@@ -127,8 +131,8 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase | |||
127 | 131 | ||
128 | public void Stop() | 132 | public void Stop() |
129 | { | 133 | { |
130 | PleaseShutdown = true; // Set shutdown flag | 134 | //PleaseShutdown = true; // Set shutdown flag |
131 | Thread.Sleep(100); // Wait a bit | 135 | //Thread.Sleep(100); // Wait a bit |
132 | if (EventQueueThread != null && EventQueueThread.IsAlive == true) | 136 | if (EventQueueThread != null && EventQueueThread.IsAlive == true) |
133 | { | 137 | { |
134 | try | 138 | try |
@@ -143,6 +147,8 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase | |||
143 | } | 147 | } |
144 | } | 148 | } |
145 | 149 | ||
150 | private EventQueueManager.QueueItemStruct BlankQIS = new EventQueueManager.QueueItemStruct(); | ||
151 | private ScriptEngine lastScriptEngine; | ||
146 | /// <summary> | 152 | /// <summary> |
147 | /// Queue processing thread loop | 153 | /// Queue processing thread loop |
148 | /// </summary> | 154 | /// </summary> |
@@ -151,188 +157,198 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase | |||
151 | //myScriptEngine.Log.Info("[" + ScriptEngineName + "]: EventQueueManager Worker thread spawned"); | 157 | //myScriptEngine.Log.Info("[" + ScriptEngineName + "]: EventQueueManager Worker thread spawned"); |
152 | try | 158 | try |
153 | { | 159 | { |
154 | while (true) | 160 | while (true) |
155 | { | 161 | { |
156 | try | 162 | try |
157 | { | 163 | { |
158 | EventQueueManager.QueueItemStruct BlankQIS = new EventQueueManager.QueueItemStruct(); | ||
159 | while (true) | 164 | while (true) |
160 | { | 165 | { |
161 | // Every now and then check if we should shut down | 166 | DoProcessQueue(); |
162 | if (PleaseShutdown || eventQueueManager.ThreadsToExit > 0) | 167 | } |
163 | { | 168 | } |
164 | // Someone should shut down, lets get exclusive lock | 169 | catch (ThreadAbortException tae) |
165 | lock (eventQueueManager.ThreadsToExitLock) | 170 | { |
166 | { | 171 | if (lastScriptEngine != null) |
167 | // Lets re-check in case someone grabbed it | 172 | lastScriptEngine.Log.Info("[" + ScriptEngineName + "]: ThreadAbortException while executing function."); |
168 | if (eventQueueManager.ThreadsToExit > 0) | 173 | } |
169 | { | 174 | catch (Exception e) |
170 | // Its crowded here so we'll shut down | 175 | { |
171 | eventQueueManager.ThreadsToExit--; | 176 | if (lastScriptEngine != null) |
172 | Stop(); | 177 | lastScriptEngine.Log.Error("[" + ScriptEngineName + "]: Exception in EventQueueThreadLoop: " + e.ToString()); |
173 | return; | 178 | } |
174 | } | 179 | } |
175 | else | 180 | } |
176 | { | 181 | catch (ThreadAbortException) |
177 | // We have been asked to shut down | 182 | { |
178 | Stop(); | 183 | //myScriptEngine.Log.Info("[" + ScriptEngineName + "]: EventQueueManager Worker thread killed: " + tae.Message); |
179 | return; | 184 | } |
180 | } | 185 | } |
181 | } | 186 | |
182 | } | 187 | public void DoProcessQueue() |
188 | { | ||
189 | foreach (ScriptEngine m_ScriptEngine in ScriptEngine.ScriptEngines) | ||
190 | { | ||
191 | lastScriptEngine = m_ScriptEngine; | ||
192 | // Every now and then check if we should shut down | ||
193 | //if (PleaseShutdown || EventQueueManager.ThreadsToExit > 0) | ||
194 | //{ | ||
195 | // // Someone should shut down, lets get exclusive lock | ||
196 | // lock (EventQueueManager.ThreadsToExitLock) | ||
197 | // { | ||
198 | // // Lets re-check in case someone grabbed it | ||
199 | // if (EventQueueManager.ThreadsToExit > 0) | ||
200 | // { | ||
201 | // // Its crowded here so we'll shut down | ||
202 | // EventQueueManager.ThreadsToExit--; | ||
203 | // Stop(); | ||
204 | // return; | ||
205 | // } | ||
206 | // else | ||
207 | // { | ||
208 | // // We have been asked to shut down | ||
209 | // Stop(); | ||
210 | // return; | ||
211 | // } | ||
212 | // } | ||
213 | //} | ||
214 | |||
215 | //try | ||
216 | // { | ||
217 | EventQueueManager.QueueItemStruct QIS = BlankQIS; | ||
218 | bool GotItem = false; | ||
183 | 219 | ||
184 | //try | 220 | //if (PleaseShutdown) |
185 | // { | 221 | // return; |
186 | EventQueueManager.QueueItemStruct QIS = BlankQIS; | ||
187 | bool GotItem = false; | ||
188 | 222 | ||
189 | if (PleaseShutdown) | 223 | if (m_ScriptEngine.m_EventQueueManager.eventQueue.Count == 0) |
190 | return; | 224 | { |
225 | // Nothing to do? Sleep a bit waiting for something to do | ||
226 | Thread.Sleep(nothingToDoSleepms); | ||
227 | } | ||
228 | else | ||
229 | { | ||
230 | // Something in queue, process | ||
231 | //myScriptEngine.Log.Info("[" + ScriptEngineName + "]: Processing event for localID: " + QIS.localID + ", itemID: " + QIS.itemID + ", FunctionName: " + QIS.FunctionName); | ||
191 | 232 | ||
192 | if (eventQueueManager.eventQueue.Count == 0) | 233 | // OBJECT BASED LOCK - TWO THREADS WORKING ON SAME OBJECT IS NOT GOOD |
234 | lock (m_ScriptEngine.m_EventQueueManager.eventQueue) | ||
235 | { | ||
236 | GotItem = false; | ||
237 | for (int qc = 0; qc < m_ScriptEngine.m_EventQueueManager.eventQueue.Count; qc++) | ||
238 | { | ||
239 | // Get queue item | ||
240 | QIS = m_ScriptEngine.m_EventQueueManager.eventQueue.Dequeue(); | ||
241 | |||
242 | // Check if object is being processed by someone else | ||
243 | if (m_ScriptEngine.m_EventQueueManager.TryLock(QIS.localID) == false) | ||
193 | { | 244 | { |
194 | // Nothing to do? Sleep a bit waiting for something to do | 245 | // Object is already being processed, requeue it |
195 | Thread.Sleep(nothingToDoSleepms); | 246 | m_ScriptEngine.m_EventQueueManager.eventQueue.Enqueue(QIS); |
196 | } | 247 | } |
197 | else | 248 | else |
198 | { | 249 | { |
199 | // Something in queue, process | 250 | // We have lock on an object and can process it |
200 | //myScriptEngine.Log.Info("[" + ScriptEngineName + "]: Processing event for localID: " + QIS.localID + ", itemID: " + QIS.itemID + ", FunctionName: " + QIS.FunctionName); | 251 | GotItem = true; |
201 | 252 | break; | |
202 | // OBJECT BASED LOCK - TWO THREADS WORKING ON SAME OBJECT IS NOT GOOD | 253 | } |
203 | lock (eventQueueManager.eventQueue) | 254 | } |
204 | { | 255 | } |
205 | GotItem = false; | ||
206 | for (int qc = 0; qc < eventQueueManager.eventQueue.Count; qc++) | ||
207 | { | ||
208 | // Get queue item | ||
209 | QIS = eventQueueManager.eventQueue.Dequeue(); | ||
210 | |||
211 | // Check if object is being processed by someone else | ||
212 | if (eventQueueManager.TryLock(QIS.localID) == false) | ||
213 | { | ||
214 | // Object is already being processed, requeue it | ||
215 | eventQueueManager.eventQueue.Enqueue(QIS); | ||
216 | } | ||
217 | else | ||
218 | { | ||
219 | // We have lock on an object and can process it | ||
220 | GotItem = true; | ||
221 | break; | ||
222 | } | ||
223 | } | ||
224 | } | ||
225 | 256 | ||
226 | if (GotItem == true) | 257 | if (GotItem == true) |
227 | { | 258 | { |
228 | // Execute function | 259 | // Execute function |
229 | try | 260 | try |
230 | { | 261 | { |
231 | ///cfk 2-7-08 dont need this right now and the default Linux build has DEBUG defined | 262 | ///cfk 2-7-08 dont need this right now and the default Linux build has DEBUG defined |
232 | #if DEBUG | 263 | #if DEBUG |
233 | //eventQueueManager.m_ScriptEngine.Log.Debug("[" + ScriptEngineName + "]: " + | 264 | //eventQueueManager.m_ScriptEngine.Log.Debug("[" + ScriptEngineName + "]: " + |
234 | // "Executing event:\r\n" | 265 | // "Executing event:\r\n" |
235 | // + "QIS.localID: " + QIS.localID | 266 | // + "QIS.localID: " + QIS.localID |
236 | // + ", QIS.itemID: " + QIS.itemID | 267 | // + ", QIS.itemID: " + QIS.itemID |
237 | // + ", QIS.functionName: " + | 268 | // + ", QIS.functionName: " + |
238 | // QIS.functionName); | 269 | // QIS.functionName); |
239 | #endif | 270 | #endif |
240 | LastExecutionStarted = DateTime.Now.Ticks; | 271 | LastExecutionStarted = DateTime.Now.Ticks; |
241 | KillCurrentScript = false; | 272 | KillCurrentScript = false; |
242 | InExecution = true; | 273 | InExecution = true; |
243 | eventQueueManager.m_ScriptEngine.m_ScriptManager.ExecuteEvent(QIS.localID, | 274 | m_ScriptEngine.m_ScriptManager.ExecuteEvent(QIS.localID, |
244 | QIS.itemID, | 275 | QIS.itemID, |
245 | QIS.functionName, | 276 | QIS.functionName, |
246 | QIS.llDetectParams, | 277 | QIS.llDetectParams, |
247 | QIS.param); | 278 | QIS.param); |
248 | InExecution = false; | 279 | InExecution = false; |
249 | } | 280 | } |
250 | catch (Exception e) | 281 | catch (Exception e) |
251 | { | 282 | { |
252 | InExecution = false; | 283 | InExecution = false; |
253 | // DISPLAY ERROR INWORLD | 284 | // DISPLAY ERROR INWORLD |
254 | string text = "Error executing script function \"" + QIS.functionName + | 285 | string text = "Error executing script function \"" + QIS.functionName + |
255 | "\":\r\n"; | 286 | "\":\r\n"; |
256 | if (e.InnerException != null) | 287 | if (e.InnerException != null) |
257 | { | 288 | { |
258 | // Send inner exception | 289 | // Send inner exception |
259 | text += e.InnerException.Message.ToString(); | 290 | text += e.InnerException.Message.ToString(); |
260 | } | 291 | } |
261 | else | 292 | else |
262 | { | 293 | { |
263 | text += "\r\n"; | 294 | text += "\r\n"; |
264 | // Send normal | 295 | // Send normal |
265 | text += e.Message.ToString(); | 296 | text += e.Message.ToString(); |
266 | } | 297 | } |
267 | if (KillCurrentScript) | 298 | if (KillCurrentScript) |
268 | text += "\r\nScript will be deactivated!"; | 299 | text += "\r\nScript will be deactivated!"; |
269 | 300 | ||
270 | try | 301 | try |
271 | { | 302 | { |
272 | if (text.Length > 1500) | 303 | if (text.Length > 1500) |
273 | text = text.Substring(0, 1500); | 304 | text = text.Substring(0, 1500); |
274 | IScriptHost m_host = | 305 | IScriptHost m_host = |
275 | eventQueueManager.m_ScriptEngine.World.GetSceneObjectPart(QIS.localID); | 306 | m_ScriptEngine.World.GetSceneObjectPart(QIS.localID); |
276 | //if (m_host != null) | 307 | //if (m_host != null) |
277 | //{ | 308 | //{ |
278 | eventQueueManager.m_ScriptEngine.World.SimChat(Helpers.StringToField(text), | 309 | m_ScriptEngine.World.SimChat(Helpers.StringToField(text), |
279 | ChatTypeEnum.Say, 0, | 310 | ChatTypeEnum.Say, 0, |
280 | m_host.AbsolutePosition, | 311 | m_host.AbsolutePosition, |
281 | m_host.Name, m_host.UUID); | 312 | m_host.Name, m_host.UUID); |
282 | } | 313 | } |
283 | catch | 314 | catch |
284 | { | 315 | { |
285 | //} | 316 | //} |
286 | //else | 317 | //else |
287 | //{ | 318 | //{ |
288 | // T oconsole | 319 | // T oconsole |
289 | eventQueueManager.m_ScriptEngine.Log.Error("[" + ScriptEngineName + "]: " + | 320 | m_ScriptEngine.m_EventQueueManager.m_ScriptEngine.Log.Error("[" + ScriptEngineName + "]: " + |
290 | "Unable to send text in-world:\r\n" + | 321 | "Unable to send text in-world:\r\n" + |
291 | text); | 322 | text); |
292 | } | 323 | } |
293 | finally | 324 | finally |
294 | { | 325 | { |
295 | // So we are done sending message in-world | 326 | // So we are done sending message in-world |
296 | if (KillCurrentScript) | 327 | if (KillCurrentScript) |
297 | { | 328 | { |
298 | eventQueueManager.m_ScriptEngine.m_ScriptManager.StopScript( | 329 | m_ScriptEngine.m_EventQueueManager.m_ScriptEngine.m_ScriptManager.StopScript( |
299 | QIS.localID, QIS.itemID); | 330 | QIS.localID, QIS.itemID); |
300 | } | ||
301 | } | ||
302 | } | ||
303 | finally | ||
304 | { | ||
305 | InExecution = false; | ||
306 | eventQueueManager.ReleaseLock(QIS.localID); | ||
307 | } | ||
308 | } | 331 | } |
309 | } | 332 | } |
310 | } | 333 | } |
311 | } | 334 | finally |
312 | catch (ThreadAbortException tae) | 335 | { |
313 | { | 336 | InExecution = false; |
314 | eventQueueManager.m_ScriptEngine.Log.Info("[" + ScriptEngineName + "]: ThreadAbortException while executing function."); | 337 | m_ScriptEngine.m_EventQueueManager.ReleaseLock(QIS.localID); |
315 | } | 338 | } |
316 | catch (Exception e) | ||
317 | { | ||
318 | eventQueueManager.m_ScriptEngine.Log.Error("[" + ScriptEngineName + "]: Exception in EventQueueThreadLoop: " + e.ToString()); | ||
319 | } | 339 | } |
320 | } | 340 | } |
321 | } | 341 | } |
322 | catch (ThreadAbortException) | ||
323 | { | ||
324 | //myScriptEngine.Log.Info("[" + ScriptEngineName + "]: EventQueueManager Worker thread killed: " + tae.Message); | ||
325 | } | ||
326 | } | 342 | } |
327 | 343 | ||
328 | /// <summary> | 344 | ///// <summary> |
329 | /// If set to true then threads and stuff should try to make a graceful exit | 345 | ///// If set to true then threads and stuff should try to make a graceful exit |
330 | /// </summary> | 346 | ///// </summary> |
331 | public bool PleaseShutdown | 347 | //public bool PleaseShutdown |
332 | { | 348 | //{ |
333 | get { return _PleaseShutdown; } | 349 | // get { return _PleaseShutdown; } |
334 | set { _PleaseShutdown = value; } | 350 | // set { _PleaseShutdown = value; } |
335 | } | 351 | //} |
336 | private bool _PleaseShutdown = false; | 352 | //private bool _PleaseShutdown = false; |
337 | } | 353 | } |
338 | } | 354 | } |