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.cpu-performance
							parent
							
								
									00c1586ff8
								
							
						
					
					
						commit
						921ad8704e
					
				|  | @ -52,6 +52,15 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | ||||||
|         private static Thread cmdHandlerThread; |         private static Thread cmdHandlerThread; | ||||||
|         private static int cmdHandlerThreadCycleSleepms; |         private static int cmdHandlerThreadCycleSleepms; | ||||||
| 
 | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// Lock for reading/writing static components of AsyncCommandManager. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <remarks> | ||||||
|  |         /// This lock exists so that multiple threads from different engines and/or different copies of the same engine | ||||||
|  |         /// are prevented from running non-thread safe code (e.g. read/write of lists) concurrently. | ||||||
|  |         /// </remarks> | ||||||
|  |         private static object staticLock = new object(); | ||||||
|  | 
 | ||||||
|         private static List<IScene> m_Scenes = new List<IScene>(); |         private static List<IScene> m_Scenes = new List<IScene>(); | ||||||
|         private static List<IScriptEngine> m_ScriptEngines = |         private static List<IScriptEngine> m_ScriptEngines = | ||||||
|                 new List<IScriptEngine>(); |                 new List<IScriptEngine>(); | ||||||
|  | @ -74,37 +83,65 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | ||||||
| 
 | 
 | ||||||
|         public Dataserver DataserverPlugin |         public Dataserver DataserverPlugin | ||||||
|         { |         { | ||||||
|             get { return m_Dataserver[m_ScriptEngine]; } |             get  | ||||||
|  |             {  | ||||||
|  |                 lock (staticLock) | ||||||
|  |                     return m_Dataserver[m_ScriptEngine];  | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         public Timer TimerPlugin |         public Timer TimerPlugin | ||||||
|         { |         { | ||||||
|             get { return m_Timer[m_ScriptEngine]; } |             get  | ||||||
|  |             {  | ||||||
|  |                 lock (staticLock) | ||||||
|  |                     return m_Timer[m_ScriptEngine];  | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         public HttpRequest HttpRequestPlugin |         public HttpRequest HttpRequestPlugin | ||||||
|         { |         { | ||||||
|             get { return m_HttpRequest[m_ScriptEngine]; } |             get  | ||||||
|  |             {  | ||||||
|  |                 lock (staticLock) | ||||||
|  |                     return m_HttpRequest[m_ScriptEngine];  | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         public Listener ListenerPlugin |         public Listener ListenerPlugin | ||||||
|         { |         { | ||||||
|             get { return m_Listener[m_ScriptEngine]; } |             get  | ||||||
|  |             {  | ||||||
|  |                 lock (staticLock) | ||||||
|  |                     return m_Listener[m_ScriptEngine];  | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         public SensorRepeat SensorRepeatPlugin |         public SensorRepeat SensorRepeatPlugin | ||||||
|         { |         { | ||||||
|             get { return m_SensorRepeat[m_ScriptEngine]; } |             get  | ||||||
|  |             {  | ||||||
|  |                 lock (staticLock) | ||||||
|  |                     return m_SensorRepeat[m_ScriptEngine];  | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         public XmlRequest XmlRequestPlugin |         public XmlRequest XmlRequestPlugin | ||||||
|         { |         { | ||||||
|             get { return m_XmlRequest[m_ScriptEngine]; } |             get  | ||||||
|  |             {  | ||||||
|  |                 lock (staticLock) | ||||||
|  |                     return m_XmlRequest[m_ScriptEngine];  | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         public IScriptEngine[] ScriptEngines |         public IScriptEngine[] ScriptEngines | ||||||
|         { |         { | ||||||
|             get { return m_ScriptEngines.ToArray(); } |             get  | ||||||
|  |             {  | ||||||
|  |                 lock (staticLock) | ||||||
|  |                     return m_ScriptEngines.ToArray();  | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         public AsyncCommandManager(IScriptEngine _ScriptEngine) |         public AsyncCommandManager(IScriptEngine _ScriptEngine) | ||||||
|  | @ -112,29 +149,36 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | ||||||
|             m_ScriptEngine = _ScriptEngine; |             m_ScriptEngine = _ScriptEngine; | ||||||
|             m_Scene = m_ScriptEngine.World; |             m_Scene = m_ScriptEngine.World; | ||||||
| 
 | 
 | ||||||
|             if (m_Scenes.Count == 0) |             // If there is more than one scene in the simulator or multiple script engines are used on the same region | ||||||
|                 ReadConfig(); |             // then more than one thread could arrive at this block of code simultaneously.  However, it cannot be | ||||||
|  |             // executed concurrently both because concurrent list operations are not thread-safe and because of other | ||||||
|  |             // race conditions such as the later check of cmdHandlerThread == null. | ||||||
|  |             lock (staticLock) | ||||||
|  |             { | ||||||
|  |                 if (m_Scenes.Count == 0) | ||||||
|  |                     ReadConfig(); | ||||||
| 
 | 
 | ||||||
|             if (!m_Scenes.Contains(m_Scene)) |                 if (!m_Scenes.Contains(m_Scene)) | ||||||
|                 m_Scenes.Add(m_Scene); |                     m_Scenes.Add(m_Scene); | ||||||
|             if (!m_ScriptEngines.Contains(m_ScriptEngine)) |                 if (!m_ScriptEngines.Contains(m_ScriptEngine)) | ||||||
|                 m_ScriptEngines.Add(m_ScriptEngine); |                     m_ScriptEngines.Add(m_ScriptEngine); | ||||||
| 
 | 
 | ||||||
|             // Create instances of all plugins |                 // Create instances of all plugins | ||||||
|             if (!m_Dataserver.ContainsKey(m_ScriptEngine)) |                 if (!m_Dataserver.ContainsKey(m_ScriptEngine)) | ||||||
|                 m_Dataserver[m_ScriptEngine] = new Dataserver(this); |                     m_Dataserver[m_ScriptEngine] = new Dataserver(this); | ||||||
|             if (!m_Timer.ContainsKey(m_ScriptEngine)) |                 if (!m_Timer.ContainsKey(m_ScriptEngine)) | ||||||
|                 m_Timer[m_ScriptEngine] = new Timer(this); |                     m_Timer[m_ScriptEngine] = new Timer(this); | ||||||
|             if (!m_HttpRequest.ContainsKey(m_ScriptEngine)) |                 if (!m_HttpRequest.ContainsKey(m_ScriptEngine)) | ||||||
|                 m_HttpRequest[m_ScriptEngine] = new HttpRequest(this); |                     m_HttpRequest[m_ScriptEngine] = new HttpRequest(this); | ||||||
|             if (!m_Listener.ContainsKey(m_ScriptEngine)) |                 if (!m_Listener.ContainsKey(m_ScriptEngine)) | ||||||
|                 m_Listener[m_ScriptEngine] = new Listener(this); |                     m_Listener[m_ScriptEngine] = new Listener(this); | ||||||
|             if (!m_SensorRepeat.ContainsKey(m_ScriptEngine)) |                 if (!m_SensorRepeat.ContainsKey(m_ScriptEngine)) | ||||||
|                 m_SensorRepeat[m_ScriptEngine] = new SensorRepeat(this); |                     m_SensorRepeat[m_ScriptEngine] = new SensorRepeat(this); | ||||||
|             if (!m_XmlRequest.ContainsKey(m_ScriptEngine)) |                 if (!m_XmlRequest.ContainsKey(m_ScriptEngine)) | ||||||
|                 m_XmlRequest[m_ScriptEngine] = new XmlRequest(this); |                     m_XmlRequest[m_ScriptEngine] = new XmlRequest(this); | ||||||
| 
 | 
 | ||||||
|             StartThread(); |                 StartThread(); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         private static void StartThread() |         private static void StartThread() | ||||||
|  | @ -198,25 +242,28 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | ||||||
| 
 | 
 | ||||||
|         private static void DoOneCmdHandlerPass() |         private static void DoOneCmdHandlerPass() | ||||||
|         { |         { | ||||||
|             // Check HttpRequests |             lock (staticLock) | ||||||
|             m_HttpRequest[m_ScriptEngines[0]].CheckHttpRequests(); |  | ||||||
| 
 |  | ||||||
|             // Check XMLRPCRequests |  | ||||||
|             m_XmlRequest[m_ScriptEngines[0]].CheckXMLRPCRequests(); |  | ||||||
| 
 |  | ||||||
|             foreach (IScriptEngine s in m_ScriptEngines) |  | ||||||
|             { |             { | ||||||
|                 // Check Listeners |                 // Check HttpRequests | ||||||
|                 m_Listener[s].CheckListeners(); |                 m_HttpRequest[m_ScriptEngines[0]].CheckHttpRequests(); | ||||||
| 
 | 
 | ||||||
|                 // Check timers |                 // Check XMLRPCRequests | ||||||
|                 m_Timer[s].CheckTimerEvents(); |                 m_XmlRequest[m_ScriptEngines[0]].CheckXMLRPCRequests(); | ||||||
| 
 | 
 | ||||||
|                 // Check Sensors |                 foreach (IScriptEngine s in m_ScriptEngines) | ||||||
|                 m_SensorRepeat[s].CheckSenseRepeaterEvents(); |                 { | ||||||
|  |                     // Check Listeners | ||||||
|  |                     m_Listener[s].CheckListeners(); | ||||||
| 
 | 
 | ||||||
|                 // Check dataserver |                     // Check timers | ||||||
|                 m_Dataserver[s].ExpireRequests(); |                     m_Timer[s].CheckTimerEvents(); | ||||||
|  | 
 | ||||||
|  |                     // Check Sensors | ||||||
|  |                     m_SensorRepeat[s].CheckSenseRepeaterEvents(); | ||||||
|  | 
 | ||||||
|  |                     // Check dataserver | ||||||
|  |                     m_Dataserver[s].ExpireRequests(); | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -229,32 +276,33 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | ||||||
|         { |         { | ||||||
| //            m_log.DebugFormat("[ASYNC COMMAND MANAGER]: Removing facilities for script {0}", itemID); | //            m_log.DebugFormat("[ASYNC COMMAND MANAGER]: Removing facilities for script {0}", itemID); | ||||||
| 
 | 
 | ||||||
|             // Remove a specific script |             lock (staticLock) | ||||||
| 
 |  | ||||||
|             // Remove dataserver events |  | ||||||
|             m_Dataserver[engine].RemoveEvents(localID, itemID); |  | ||||||
| 
 |  | ||||||
|             // Remove from: Timers |  | ||||||
|             m_Timer[engine].UnSetTimerEvents(localID, itemID); |  | ||||||
| 
 |  | ||||||
|             // Remove from: HttpRequest |  | ||||||
|             IHttpRequestModule iHttpReq = engine.World.RequestModuleInterface<IHttpRequestModule>(); |  | ||||||
|             if (iHttpReq != null) |  | ||||||
|                 iHttpReq.StopHttpRequestsForScript(itemID); |  | ||||||
| 
 |  | ||||||
|             IWorldComm comms = engine.World.RequestModuleInterface<IWorldComm>(); |  | ||||||
|             if (comms != null) |  | ||||||
|                 comms.DeleteListener(itemID); |  | ||||||
| 
 |  | ||||||
|             IXMLRPC xmlrpc = engine.World.RequestModuleInterface<IXMLRPC>(); |  | ||||||
|             if (xmlrpc != null) |  | ||||||
|             { |             { | ||||||
|                 xmlrpc.DeleteChannels(itemID); |                 // Remove dataserver events | ||||||
|                 xmlrpc.CancelSRDRequests(itemID); |                 m_Dataserver[engine].RemoveEvents(localID, itemID); | ||||||
|             } |  | ||||||
| 
 | 
 | ||||||
|             // Remove Sensors |                 // Remove from: Timers | ||||||
|             m_SensorRepeat[engine].UnSetSenseRepeaterEvents(localID, itemID); |                 m_Timer[engine].UnSetTimerEvents(localID, itemID); | ||||||
|  | 
 | ||||||
|  |                 // Remove from: HttpRequest | ||||||
|  |                 IHttpRequestModule iHttpReq = engine.World.RequestModuleInterface<IHttpRequestModule>(); | ||||||
|  |                 if (iHttpReq != null) | ||||||
|  |                     iHttpReq.StopHttpRequestsForScript(itemID); | ||||||
|  | 
 | ||||||
|  |                 IWorldComm comms = engine.World.RequestModuleInterface<IWorldComm>(); | ||||||
|  |                 if (comms != null) | ||||||
|  |                     comms.DeleteListener(itemID); | ||||||
|  | 
 | ||||||
|  |                 IXMLRPC xmlrpc = engine.World.RequestModuleInterface<IXMLRPC>(); | ||||||
|  |                 if (xmlrpc != null) | ||||||
|  |                 { | ||||||
|  |                     xmlrpc.DeleteChannels(itemID); | ||||||
|  |                     xmlrpc.CancelSRDRequests(itemID); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 // Remove Sensors | ||||||
|  |                 m_SensorRepeat[engine].UnSetSenseRepeaterEvents(localID, itemID); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
|  | @ -264,10 +312,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | ||||||
|         /// <returns></returns> |         /// <returns></returns> | ||||||
|         public static SensorRepeat GetSensorRepeatPlugin(IScriptEngine engine) |         public static SensorRepeat GetSensorRepeatPlugin(IScriptEngine engine) | ||||||
|         { |         { | ||||||
|             if (m_SensorRepeat.ContainsKey(engine)) |             lock (staticLock) | ||||||
|                 return m_SensorRepeat[engine]; |             { | ||||||
|             else |                 if (m_SensorRepeat.ContainsKey(engine)) | ||||||
|                 return null; |                     return m_SensorRepeat[engine]; | ||||||
|  |                 else | ||||||
|  |                     return null; | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
|  | @ -277,10 +328,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | ||||||
|         /// <returns></returns> |         /// <returns></returns> | ||||||
|         public static Dataserver GetDataserverPlugin(IScriptEngine engine) |         public static Dataserver GetDataserverPlugin(IScriptEngine engine) | ||||||
|         { |         { | ||||||
|             if (m_Dataserver.ContainsKey(engine)) |             lock (staticLock) | ||||||
|                 return m_Dataserver[engine]; |             { | ||||||
|             else |                 if (m_Dataserver.ContainsKey(engine)) | ||||||
|                 return null; |                     return m_Dataserver[engine]; | ||||||
|  |                 else | ||||||
|  |                     return null; | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
|  | @ -290,10 +344,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | ||||||
|         /// <returns></returns> |         /// <returns></returns> | ||||||
|         public static Timer GetTimerPlugin(IScriptEngine engine) |         public static Timer GetTimerPlugin(IScriptEngine engine) | ||||||
|         { |         { | ||||||
|             if (m_Timer.ContainsKey(engine)) |             lock (staticLock) | ||||||
|                 return m_Timer[engine]; |             { | ||||||
|             else |                 if (m_Timer.ContainsKey(engine)) | ||||||
|                 return null; |                     return m_Timer[engine]; | ||||||
|  |                 else | ||||||
|  |                     return null; | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
|  | @ -303,38 +360,44 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | ||||||
|         /// <returns></returns> |         /// <returns></returns> | ||||||
|         public static Listener GetListenerPlugin(IScriptEngine engine) |         public static Listener GetListenerPlugin(IScriptEngine engine) | ||||||
|         { |         { | ||||||
|             if (m_Listener.ContainsKey(engine)) |             lock (staticLock) | ||||||
|                 return m_Listener[engine]; |             { | ||||||
|             else |                 if (m_Listener.ContainsKey(engine)) | ||||||
|                 return null; |                     return m_Listener[engine]; | ||||||
|  |                 else | ||||||
|  |                     return null; | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         public static Object[] GetSerializationData(IScriptEngine engine, UUID itemID) |         public static Object[] GetSerializationData(IScriptEngine engine, UUID itemID) | ||||||
|         { |         { | ||||||
|             List<Object> data = new List<Object>(); |             List<Object> data = new List<Object>(); | ||||||
| 
 | 
 | ||||||
|             Object[] listeners = m_Listener[engine].GetSerializationData(itemID); |             lock (staticLock) | ||||||
|             if (listeners.Length > 0) |  | ||||||
|             { |             { | ||||||
|                 data.Add("listener"); |                 Object[] listeners = m_Listener[engine].GetSerializationData(itemID); | ||||||
|                 data.Add(listeners.Length); |                 if (listeners.Length > 0) | ||||||
|                 data.AddRange(listeners); |                 { | ||||||
|             } |                     data.Add("listener"); | ||||||
|  |                     data.Add(listeners.Length); | ||||||
|  |                     data.AddRange(listeners); | ||||||
|  |                 } | ||||||
| 
 | 
 | ||||||
|             Object[] timers=m_Timer[engine].GetSerializationData(itemID); |                 Object[] timers=m_Timer[engine].GetSerializationData(itemID); | ||||||
|             if (timers.Length > 0) |                 if (timers.Length > 0) | ||||||
|             { |                 { | ||||||
|                 data.Add("timer"); |                     data.Add("timer"); | ||||||
|                 data.Add(timers.Length); |                     data.Add(timers.Length); | ||||||
|                 data.AddRange(timers); |                     data.AddRange(timers); | ||||||
|             } |                 } | ||||||
| 
 | 
 | ||||||
|             Object[] sensors = m_SensorRepeat[engine].GetSerializationData(itemID); |                 Object[] sensors = m_SensorRepeat[engine].GetSerializationData(itemID); | ||||||
|             if (sensors.Length > 0) |                 if (sensors.Length > 0) | ||||||
|             { |                 { | ||||||
|                 data.Add("sensor"); |                     data.Add("sensor"); | ||||||
|                 data.Add(sensors.Length); |                     data.Add(sensors.Length); | ||||||
|                 data.AddRange(sensors); |                     data.AddRange(sensors); | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             return data.ToArray(); |             return data.ToArray(); | ||||||
|  | @ -359,20 +422,23 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api | ||||||
| 
 | 
 | ||||||
|                     idx+=len; |                     idx+=len; | ||||||
| 
 | 
 | ||||||
|  |                     lock (staticLock) | ||||||
|  |                     { | ||||||
|                     switch (type) |                     switch (type) | ||||||
|                     { |                     { | ||||||
|                     case "listener": |                         case "listener": | ||||||
|                         m_Listener[engine].CreateFromData(localID, itemID, |                             m_Listener[engine].CreateFromData(localID, itemID, | ||||||
|                                                     hostID, item); |                                                         hostID, item); | ||||||
|                         break; |                             break; | ||||||
|                     case "timer": |                         case "timer": | ||||||
|                         m_Timer[engine].CreateFromData(localID, itemID, |                             m_Timer[engine].CreateFromData(localID, itemID, | ||||||
|                                                     hostID, item); |                                                         hostID, item); | ||||||
|                         break; |                             break; | ||||||
|                     case "sensor": |                         case "sensor": | ||||||
|                         m_SensorRepeat[engine].CreateFromData(localID, |                             m_SensorRepeat[engine].CreateFromData(localID, | ||||||
|                                                     itemID, hostID, item); |                                                         itemID, hostID, item); | ||||||
|                         break; |                             break; | ||||||
|  |                         } | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Justin Clark-Casey (justincc)
						Justin Clark-Casey (justincc)