diff options
author | Justin Clark-Casey (justincc) | 2013-05-31 22:50:15 +0100 |
---|---|---|
committer | Justin Clark-Casey (justincc) | 2013-05-31 22:50:15 +0100 |
commit | 921ad8704e08fccc994f7907e7cb1e9372e355b9 (patch) | |
tree | 90e96e9d8b5158c247d44c4219809d2ee8981c43 | |
parent | refactor: Remove unused AsyncCommandManager.PleaseShutdown (diff) | |
download | opensim-SC_OLD-921ad8704e08fccc994f7907e7cb1e9372e355b9.zip opensim-SC_OLD-921ad8704e08fccc994f7907e7cb1e9372e355b9.tar.gz opensim-SC_OLD-921ad8704e08fccc994f7907e7cb1e9372e355b9.tar.bz2 opensim-SC_OLD-921ad8704e08fccc994f7907e7cb1e9372e355b9.tar.xz |
Lock areas of AsyncCommandManager where multiple threads could try to access/update the same static structures simultaneously.
This is possible where there is more than one scene (multiple copies of the same script engine) and/or more than one script engine being used.
These operations are not thread safe and could be leading to the exceptions/problems seen in http://opensimulator.org/mantis/view.php?id=6651
This also prevents a small race condition where more than one AsyncLSLCmdHandlerThread could be started.
-rw-r--r-- | OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs | 288 |
1 files changed, 177 insertions, 111 deletions
diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs index c71b571..72e8415 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs | |||
@@ -52,6 +52,15 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
52 | private static Thread cmdHandlerThread; | 52 | private static Thread cmdHandlerThread; |
53 | private static int cmdHandlerThreadCycleSleepms; | 53 | private static int cmdHandlerThreadCycleSleepms; |
54 | 54 | ||
55 | /// <summary> | ||
56 | /// Lock for reading/writing static components of AsyncCommandManager. | ||
57 | /// </summary> | ||
58 | /// <remarks> | ||
59 | /// This lock exists so that multiple threads from different engines and/or different copies of the same engine | ||
60 | /// are prevented from running non-thread safe code (e.g. read/write of lists) concurrently. | ||
61 | /// </remarks> | ||
62 | private static object staticLock = new object(); | ||
63 | |||
55 | private static List<IScene> m_Scenes = new List<IScene>(); | 64 | private static List<IScene> m_Scenes = new List<IScene>(); |
56 | private static List<IScriptEngine> m_ScriptEngines = | 65 | private static List<IScriptEngine> m_ScriptEngines = |
57 | new List<IScriptEngine>(); | 66 | new List<IScriptEngine>(); |
@@ -74,37 +83,65 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
74 | 83 | ||
75 | public Dataserver DataserverPlugin | 84 | public Dataserver DataserverPlugin |
76 | { | 85 | { |
77 | get { return m_Dataserver[m_ScriptEngine]; } | 86 | get |
87 | { | ||
88 | lock (staticLock) | ||
89 | return m_Dataserver[m_ScriptEngine]; | ||
90 | } | ||
78 | } | 91 | } |
79 | 92 | ||
80 | public Timer TimerPlugin | 93 | public Timer TimerPlugin |
81 | { | 94 | { |
82 | get { return m_Timer[m_ScriptEngine]; } | 95 | get |
96 | { | ||
97 | lock (staticLock) | ||
98 | return m_Timer[m_ScriptEngine]; | ||
99 | } | ||
83 | } | 100 | } |
84 | 101 | ||
85 | public HttpRequest HttpRequestPlugin | 102 | public HttpRequest HttpRequestPlugin |
86 | { | 103 | { |
87 | get { return m_HttpRequest[m_ScriptEngine]; } | 104 | get |
105 | { | ||
106 | lock (staticLock) | ||
107 | return m_HttpRequest[m_ScriptEngine]; | ||
108 | } | ||
88 | } | 109 | } |
89 | 110 | ||
90 | public Listener ListenerPlugin | 111 | public Listener ListenerPlugin |
91 | { | 112 | { |
92 | get { return m_Listener[m_ScriptEngine]; } | 113 | get |
114 | { | ||
115 | lock (staticLock) | ||
116 | return m_Listener[m_ScriptEngine]; | ||
117 | } | ||
93 | } | 118 | } |
94 | 119 | ||
95 | public SensorRepeat SensorRepeatPlugin | 120 | public SensorRepeat SensorRepeatPlugin |
96 | { | 121 | { |
97 | get { return m_SensorRepeat[m_ScriptEngine]; } | 122 | get |
123 | { | ||
124 | lock (staticLock) | ||
125 | return m_SensorRepeat[m_ScriptEngine]; | ||
126 | } | ||
98 | } | 127 | } |
99 | 128 | ||
100 | public XmlRequest XmlRequestPlugin | 129 | public XmlRequest XmlRequestPlugin |
101 | { | 130 | { |
102 | get { return m_XmlRequest[m_ScriptEngine]; } | 131 | get |
132 | { | ||
133 | lock (staticLock) | ||
134 | return m_XmlRequest[m_ScriptEngine]; | ||
135 | } | ||
103 | } | 136 | } |
104 | 137 | ||
105 | public IScriptEngine[] ScriptEngines | 138 | public IScriptEngine[] ScriptEngines |
106 | { | 139 | { |
107 | get { return m_ScriptEngines.ToArray(); } | 140 | get |
141 | { | ||
142 | lock (staticLock) | ||
143 | return m_ScriptEngines.ToArray(); | ||
144 | } | ||
108 | } | 145 | } |
109 | 146 | ||
110 | public AsyncCommandManager(IScriptEngine _ScriptEngine) | 147 | public AsyncCommandManager(IScriptEngine _ScriptEngine) |
@@ -112,29 +149,36 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
112 | m_ScriptEngine = _ScriptEngine; | 149 | m_ScriptEngine = _ScriptEngine; |
113 | m_Scene = m_ScriptEngine.World; | 150 | m_Scene = m_ScriptEngine.World; |
114 | 151 | ||
115 | if (m_Scenes.Count == 0) | 152 | // If there is more than one scene in the simulator or multiple script engines are used on the same region |
116 | ReadConfig(); | 153 | // then more than one thread could arrive at this block of code simultaneously. However, it cannot be |
117 | 154 | // executed concurrently both because concurrent list operations are not thread-safe and because of other | |
118 | if (!m_Scenes.Contains(m_Scene)) | 155 | // race conditions such as the later check of cmdHandlerThread == null. |
119 | m_Scenes.Add(m_Scene); | 156 | lock (staticLock) |
120 | if (!m_ScriptEngines.Contains(m_ScriptEngine)) | 157 | { |
121 | m_ScriptEngines.Add(m_ScriptEngine); | 158 | if (m_Scenes.Count == 0) |
122 | 159 | ReadConfig(); | |
123 | // Create instances of all plugins | 160 | |
124 | if (!m_Dataserver.ContainsKey(m_ScriptEngine)) | 161 | if (!m_Scenes.Contains(m_Scene)) |
125 | m_Dataserver[m_ScriptEngine] = new Dataserver(this); | 162 | m_Scenes.Add(m_Scene); |
126 | if (!m_Timer.ContainsKey(m_ScriptEngine)) | 163 | if (!m_ScriptEngines.Contains(m_ScriptEngine)) |
127 | m_Timer[m_ScriptEngine] = new Timer(this); | 164 | m_ScriptEngines.Add(m_ScriptEngine); |
128 | if (!m_HttpRequest.ContainsKey(m_ScriptEngine)) | 165 | |
129 | m_HttpRequest[m_ScriptEngine] = new HttpRequest(this); | 166 | // Create instances of all plugins |
130 | if (!m_Listener.ContainsKey(m_ScriptEngine)) | 167 | if (!m_Dataserver.ContainsKey(m_ScriptEngine)) |
131 | m_Listener[m_ScriptEngine] = new Listener(this); | 168 | m_Dataserver[m_ScriptEngine] = new Dataserver(this); |
132 | if (!m_SensorRepeat.ContainsKey(m_ScriptEngine)) | 169 | if (!m_Timer.ContainsKey(m_ScriptEngine)) |
133 | m_SensorRepeat[m_ScriptEngine] = new SensorRepeat(this); | 170 | m_Timer[m_ScriptEngine] = new Timer(this); |
134 | if (!m_XmlRequest.ContainsKey(m_ScriptEngine)) | 171 | if (!m_HttpRequest.ContainsKey(m_ScriptEngine)) |
135 | m_XmlRequest[m_ScriptEngine] = new XmlRequest(this); | 172 | m_HttpRequest[m_ScriptEngine] = new HttpRequest(this); |
136 | 173 | if (!m_Listener.ContainsKey(m_ScriptEngine)) | |
137 | StartThread(); | 174 | m_Listener[m_ScriptEngine] = new Listener(this); |
175 | if (!m_SensorRepeat.ContainsKey(m_ScriptEngine)) | ||
176 | m_SensorRepeat[m_ScriptEngine] = new SensorRepeat(this); | ||
177 | if (!m_XmlRequest.ContainsKey(m_ScriptEngine)) | ||
178 | m_XmlRequest[m_ScriptEngine] = new XmlRequest(this); | ||
179 | |||
180 | StartThread(); | ||
181 | } | ||
138 | } | 182 | } |
139 | 183 | ||
140 | private static void StartThread() | 184 | private static void StartThread() |
@@ -198,25 +242,28 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
198 | 242 | ||
199 | private static void DoOneCmdHandlerPass() | 243 | private static void DoOneCmdHandlerPass() |
200 | { | 244 | { |
201 | // Check HttpRequests | 245 | lock (staticLock) |
202 | m_HttpRequest[m_ScriptEngines[0]].CheckHttpRequests(); | 246 | { |
247 | // Check HttpRequests | ||
248 | m_HttpRequest[m_ScriptEngines[0]].CheckHttpRequests(); | ||
203 | 249 | ||
204 | // Check XMLRPCRequests | 250 | // Check XMLRPCRequests |
205 | m_XmlRequest[m_ScriptEngines[0]].CheckXMLRPCRequests(); | 251 | m_XmlRequest[m_ScriptEngines[0]].CheckXMLRPCRequests(); |
206 | 252 | ||
207 | foreach (IScriptEngine s in m_ScriptEngines) | 253 | foreach (IScriptEngine s in m_ScriptEngines) |
208 | { | 254 | { |
209 | // Check Listeners | 255 | // Check Listeners |
210 | m_Listener[s].CheckListeners(); | 256 | m_Listener[s].CheckListeners(); |
211 | 257 | ||
212 | // Check timers | 258 | // Check timers |
213 | m_Timer[s].CheckTimerEvents(); | 259 | m_Timer[s].CheckTimerEvents(); |
214 | 260 | ||
215 | // Check Sensors | 261 | // Check Sensors |
216 | m_SensorRepeat[s].CheckSenseRepeaterEvents(); | 262 | m_SensorRepeat[s].CheckSenseRepeaterEvents(); |
217 | 263 | ||
218 | // Check dataserver | 264 | // Check dataserver |
219 | m_Dataserver[s].ExpireRequests(); | 265 | m_Dataserver[s].ExpireRequests(); |
266 | } | ||
220 | } | 267 | } |
221 | } | 268 | } |
222 | 269 | ||
@@ -229,32 +276,33 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
229 | { | 276 | { |
230 | // m_log.DebugFormat("[ASYNC COMMAND MANAGER]: Removing facilities for script {0}", itemID); | 277 | // m_log.DebugFormat("[ASYNC COMMAND MANAGER]: Removing facilities for script {0}", itemID); |
231 | 278 | ||
232 | // Remove a specific script | 279 | lock (staticLock) |
280 | { | ||
281 | // Remove dataserver events | ||
282 | m_Dataserver[engine].RemoveEvents(localID, itemID); | ||
233 | 283 | ||
234 | // Remove dataserver events | 284 | // Remove from: Timers |
235 | m_Dataserver[engine].RemoveEvents(localID, itemID); | 285 | m_Timer[engine].UnSetTimerEvents(localID, itemID); |
236 | 286 | ||
237 | // Remove from: Timers | 287 | // Remove from: HttpRequest |
238 | m_Timer[engine].UnSetTimerEvents(localID, itemID); | 288 | IHttpRequestModule iHttpReq = engine.World.RequestModuleInterface<IHttpRequestModule>(); |
289 | if (iHttpReq != null) | ||
290 | iHttpReq.StopHttpRequestsForScript(itemID); | ||
239 | 291 | ||
240 | // Remove from: HttpRequest | 292 | IWorldComm comms = engine.World.RequestModuleInterface<IWorldComm>(); |
241 | IHttpRequestModule iHttpReq = engine.World.RequestModuleInterface<IHttpRequestModule>(); | 293 | if (comms != null) |
242 | if (iHttpReq != null) | 294 | comms.DeleteListener(itemID); |
243 | iHttpReq.StopHttpRequestsForScript(itemID); | ||
244 | 295 | ||
245 | IWorldComm comms = engine.World.RequestModuleInterface<IWorldComm>(); | 296 | IXMLRPC xmlrpc = engine.World.RequestModuleInterface<IXMLRPC>(); |
246 | if (comms != null) | 297 | if (xmlrpc != null) |
247 | comms.DeleteListener(itemID); | 298 | { |
299 | xmlrpc.DeleteChannels(itemID); | ||
300 | xmlrpc.CancelSRDRequests(itemID); | ||
301 | } | ||
248 | 302 | ||
249 | IXMLRPC xmlrpc = engine.World.RequestModuleInterface<IXMLRPC>(); | 303 | // Remove Sensors |
250 | if (xmlrpc != null) | 304 | m_SensorRepeat[engine].UnSetSenseRepeaterEvents(localID, itemID); |
251 | { | ||
252 | xmlrpc.DeleteChannels(itemID); | ||
253 | xmlrpc.CancelSRDRequests(itemID); | ||
254 | } | 305 | } |
255 | |||
256 | // Remove Sensors | ||
257 | m_SensorRepeat[engine].UnSetSenseRepeaterEvents(localID, itemID); | ||
258 | } | 306 | } |
259 | 307 | ||
260 | /// <summary> | 308 | /// <summary> |
@@ -264,10 +312,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
264 | /// <returns></returns> | 312 | /// <returns></returns> |
265 | public static SensorRepeat GetSensorRepeatPlugin(IScriptEngine engine) | 313 | public static SensorRepeat GetSensorRepeatPlugin(IScriptEngine engine) |
266 | { | 314 | { |
267 | if (m_SensorRepeat.ContainsKey(engine)) | 315 | lock (staticLock) |
268 | return m_SensorRepeat[engine]; | 316 | { |
269 | else | 317 | if (m_SensorRepeat.ContainsKey(engine)) |
270 | return null; | 318 | return m_SensorRepeat[engine]; |
319 | else | ||
320 | return null; | ||
321 | } | ||
271 | } | 322 | } |
272 | 323 | ||
273 | /// <summary> | 324 | /// <summary> |
@@ -277,10 +328,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
277 | /// <returns></returns> | 328 | /// <returns></returns> |
278 | public static Dataserver GetDataserverPlugin(IScriptEngine engine) | 329 | public static Dataserver GetDataserverPlugin(IScriptEngine engine) |
279 | { | 330 | { |
280 | if (m_Dataserver.ContainsKey(engine)) | 331 | lock (staticLock) |
281 | return m_Dataserver[engine]; | 332 | { |
282 | else | 333 | if (m_Dataserver.ContainsKey(engine)) |
283 | return null; | 334 | return m_Dataserver[engine]; |
335 | else | ||
336 | return null; | ||
337 | } | ||
284 | } | 338 | } |
285 | 339 | ||
286 | /// <summary> | 340 | /// <summary> |
@@ -290,10 +344,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
290 | /// <returns></returns> | 344 | /// <returns></returns> |
291 | public static Timer GetTimerPlugin(IScriptEngine engine) | 345 | public static Timer GetTimerPlugin(IScriptEngine engine) |
292 | { | 346 | { |
293 | if (m_Timer.ContainsKey(engine)) | 347 | lock (staticLock) |
294 | return m_Timer[engine]; | 348 | { |
295 | else | 349 | if (m_Timer.ContainsKey(engine)) |
296 | return null; | 350 | return m_Timer[engine]; |
351 | else | ||
352 | return null; | ||
353 | } | ||
297 | } | 354 | } |
298 | 355 | ||
299 | /// <summary> | 356 | /// <summary> |
@@ -303,38 +360,44 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
303 | /// <returns></returns> | 360 | /// <returns></returns> |
304 | public static Listener GetListenerPlugin(IScriptEngine engine) | 361 | public static Listener GetListenerPlugin(IScriptEngine engine) |
305 | { | 362 | { |
306 | if (m_Listener.ContainsKey(engine)) | 363 | lock (staticLock) |
307 | return m_Listener[engine]; | 364 | { |
308 | else | 365 | if (m_Listener.ContainsKey(engine)) |
309 | return null; | 366 | return m_Listener[engine]; |
367 | else | ||
368 | return null; | ||
369 | } | ||
310 | } | 370 | } |
311 | 371 | ||
312 | public static Object[] GetSerializationData(IScriptEngine engine, UUID itemID) | 372 | public static Object[] GetSerializationData(IScriptEngine engine, UUID itemID) |
313 | { | 373 | { |
314 | List<Object> data = new List<Object>(); | 374 | List<Object> data = new List<Object>(); |
315 | 375 | ||
316 | Object[] listeners = m_Listener[engine].GetSerializationData(itemID); | 376 | lock (staticLock) |
317 | if (listeners.Length > 0) | ||
318 | { | 377 | { |
319 | data.Add("listener"); | 378 | Object[] listeners = m_Listener[engine].GetSerializationData(itemID); |
320 | data.Add(listeners.Length); | 379 | if (listeners.Length > 0) |
321 | data.AddRange(listeners); | 380 | { |
322 | } | 381 | data.Add("listener"); |
382 | data.Add(listeners.Length); | ||
383 | data.AddRange(listeners); | ||
384 | } | ||
323 | 385 | ||
324 | Object[] timers=m_Timer[engine].GetSerializationData(itemID); | 386 | Object[] timers=m_Timer[engine].GetSerializationData(itemID); |
325 | if (timers.Length > 0) | 387 | if (timers.Length > 0) |
326 | { | 388 | { |
327 | data.Add("timer"); | 389 | data.Add("timer"); |
328 | data.Add(timers.Length); | 390 | data.Add(timers.Length); |
329 | data.AddRange(timers); | 391 | data.AddRange(timers); |
330 | } | 392 | } |
331 | 393 | ||
332 | Object[] sensors = m_SensorRepeat[engine].GetSerializationData(itemID); | 394 | Object[] sensors = m_SensorRepeat[engine].GetSerializationData(itemID); |
333 | if (sensors.Length > 0) | 395 | if (sensors.Length > 0) |
334 | { | 396 | { |
335 | data.Add("sensor"); | 397 | data.Add("sensor"); |
336 | data.Add(sensors.Length); | 398 | data.Add(sensors.Length); |
337 | data.AddRange(sensors); | 399 | data.AddRange(sensors); |
400 | } | ||
338 | } | 401 | } |
339 | 402 | ||
340 | return data.ToArray(); | 403 | return data.ToArray(); |
@@ -359,20 +422,23 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | |||
359 | 422 | ||
360 | idx+=len; | 423 | idx+=len; |
361 | 424 | ||
425 | lock (staticLock) | ||
426 | { | ||
362 | switch (type) | 427 | switch (type) |
363 | { | 428 | { |
364 | case "listener": | 429 | case "listener": |
365 | m_Listener[engine].CreateFromData(localID, itemID, | 430 | m_Listener[engine].CreateFromData(localID, itemID, |
366 | hostID, item); | 431 | hostID, item); |
367 | break; | 432 | break; |
368 | case "timer": | 433 | case "timer": |
369 | m_Timer[engine].CreateFromData(localID, itemID, | 434 | m_Timer[engine].CreateFromData(localID, itemID, |
370 | hostID, item); | 435 | hostID, item); |
371 | break; | 436 | break; |
372 | case "sensor": | 437 | case "sensor": |
373 | m_SensorRepeat[engine].CreateFromData(localID, | 438 | m_SensorRepeat[engine].CreateFromData(localID, |
374 | itemID, hostID, item); | 439 | itemID, hostID, item); |
375 | break; | 440 | break; |
441 | } | ||
376 | } | 442 | } |
377 | } | 443 | } |
378 | } | 444 | } |