From faaf47a86f34f24dec0d7d51eda638d0df3db305 Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Fri, 16 Jan 2015 23:55:00 +0000 Subject: Prevent a race condition between the script engine backup thread and script removal by locking on the script's EventQueue and only proceeding if it's flagged as still running. Relates to http://opensimulator.org/mantis/view.php?id=7407 --- .../ScriptEngine/Interfaces/IScriptInstance.cs | 3 +- .../ScriptEngine/Shared/Instance/ScriptInstance.cs | 70 ++++++++++++---------- OpenSim/Region/ScriptEngine/XEngine/XEngine.cs | 6 +- 3 files changed, 44 insertions(+), 35 deletions(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs b/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs index 35e5f18..fa2ceef 100644 --- a/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs +++ b/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs @@ -178,8 +178,9 @@ namespace OpenSim.Region.ScriptEngine.Interfaces /// /// How many milliseconds we will wait for an existing script event to finish before /// forcibly aborting that event. + /// If true then the event queue is also cleared /// true if the script was successfully stopped, false otherwise - bool Stop(int timeout); + bool Stop(int timeout, bool clearEventQueue = false); void SetState(string state); diff --git a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs index 9498aa8..e202a24 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs @@ -564,7 +564,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance } } - public bool Stop(int timeout) + public bool Stop(int timeout, bool clearEventQueue = false) { if (DebugLevel >= 1) m_log.DebugFormat( @@ -575,6 +575,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance lock (EventQueue) { + if (clearEventQueue) + ClearQueue(); + if (!Running) return true; @@ -1065,45 +1068,52 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance public void SaveState() { - // If we're currently in an event, just tell it to save upon return - // - if (m_InEvent) + // We need to lock here to avoid any race with a thread that is removing this script. + lock (EventQueue) { - m_SaveState = true; - return; - } + if (!Running) + return; -// m_log.DebugFormat( -// "[SCRIPT INSTANCE]: Saving state for script {0} (id {1}) in part {2} (id {3}) in object {4} in {5}", -// ScriptTask.Name, ScriptTask.ItemID, Part.Name, Part.UUID, Part.ParentGroup.Name, Engine.World.Name); + // If we're currently in an event, just tell it to save upon return + // + if (m_InEvent) + { + m_SaveState = true; + return; + } - PluginData = AsyncCommandManager.GetSerializationData(Engine, ItemID); + // m_log.DebugFormat( + // "[SCRIPT INSTANCE]: Saving state for script {0} (id {1}) in part {2} (id {3}) in object {4} in {5}", + // ScriptTask.Name, ScriptTask.ItemID, Part.Name, Part.UUID, Part.ParentGroup.Name, Engine.World.Name); - string xml = ScriptSerializer.Serialize(this); + PluginData = AsyncCommandManager.GetSerializationData(Engine, ItemID); - // Compare hash of the state we just just created with the state last written to disk - // If the state is different, update the disk file. - UUID hash = UUID.Parse(Utils.MD5String(xml)); + string xml = ScriptSerializer.Serialize(this); - if (hash != m_CurrentStateHash) - { - try + // Compare hash of the state we just just created with the state last written to disk + // If the state is different, update the disk file. + UUID hash = UUID.Parse(Utils.MD5String(xml)); + + if (hash != m_CurrentStateHash) { - using (FileStream fs = File.Create(Path.Combine(m_dataPath, ItemID.ToString() + ".state"))) + try + { + using (FileStream fs = File.Create(Path.Combine(m_dataPath, ItemID.ToString() + ".state"))) + { + Byte[] buf = Util.UTF8NoBomEncoding.GetBytes(xml); + fs.Write(buf, 0, buf.Length); + } + } + catch(Exception) { - Byte[] buf = Util.UTF8NoBomEncoding.GetBytes(xml); - fs.Write(buf, 0, buf.Length); + // m_log.Error("Unable to save xml\n"+e.ToString()); } + //if (!File.Exists(Path.Combine(Path.GetDirectoryName(assembly), ItemID.ToString() + ".state"))) + //{ + // throw new Exception("Completed persistence save, but no file was created"); + //} + m_CurrentStateHash = hash; } - catch(Exception) - { - // m_log.Error("Unable to save xml\n"+e.ToString()); - } - //if (!File.Exists(Path.Combine(Path.GetDirectoryName(assembly), ItemID.ToString() + ".state"))) - //{ - // throw new Exception("Completed persistence save, but no file was created"); - //} - m_CurrentStateHash = hash; } } diff --git a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs index 03fafed..1a64595 100644 --- a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs +++ b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs @@ -732,8 +732,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine // Clear the event queue and abort the instance thread // - instance.ClearQueue(); - instance.Stop(0); + instance.Stop(0, true); // Release events, timer, etc // @@ -859,8 +858,6 @@ namespace OpenSim.Region.ScriptEngine.XEngine } } - instances.Clear(); - if (saveTime > 0) m_ThreadPool.QueueWorkItem(new WorkItemCallback(this.DoBackup), new Object[] { saveTime }); @@ -1443,6 +1440,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine m_Scripts.Remove(itemID); } + instance.ClearQueue(); instance.Stop(m_WaitForEventCompletionOnScriptStop); -- cgit v1.1