diff options
author | Justin Clark-Casey (justincc) | 2015-01-16 23:55:00 +0000 |
---|---|---|
committer | Justin Clark-Casey (justincc) | 2015-01-16 23:55:11 +0000 |
commit | faaf47a86f34f24dec0d7d51eda638d0df3db305 (patch) | |
tree | 999a9351ae53e4ecc0b465e8e1bea744cb335837 | |
parent | For scripts in attachments, don't save .state files apart from the initial on... (diff) | |
download | opensim-SC-faaf47a86f34f24dec0d7d51eda638d0df3db305.zip opensim-SC-faaf47a86f34f24dec0d7d51eda638d0df3db305.tar.gz opensim-SC-faaf47a86f34f24dec0d7d51eda638d0df3db305.tar.bz2 opensim-SC-faaf47a86f34f24dec0d7d51eda638d0df3db305.tar.xz |
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
3 files changed, 44 insertions, 35 deletions
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 | |||
178 | /// <param name="timeout"></param> | 178 | /// <param name="timeout"></param> |
179 | /// How many milliseconds we will wait for an existing script event to finish before | 179 | /// How many milliseconds we will wait for an existing script event to finish before |
180 | /// forcibly aborting that event. | 180 | /// forcibly aborting that event. |
181 | /// <param name="clearEventQueue">If true then the event queue is also cleared</param> | ||
181 | /// <returns>true if the script was successfully stopped, false otherwise</returns> | 182 | /// <returns>true if the script was successfully stopped, false otherwise</returns> |
182 | bool Stop(int timeout); | 183 | bool Stop(int timeout, bool clearEventQueue = false); |
183 | 184 | ||
184 | void SetState(string state); | 185 | void SetState(string state); |
185 | 186 | ||
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 | |||
564 | } | 564 | } |
565 | } | 565 | } |
566 | 566 | ||
567 | public bool Stop(int timeout) | 567 | public bool Stop(int timeout, bool clearEventQueue = false) |
568 | { | 568 | { |
569 | if (DebugLevel >= 1) | 569 | if (DebugLevel >= 1) |
570 | m_log.DebugFormat( | 570 | m_log.DebugFormat( |
@@ -575,6 +575,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance | |||
575 | 575 | ||
576 | lock (EventQueue) | 576 | lock (EventQueue) |
577 | { | 577 | { |
578 | if (clearEventQueue) | ||
579 | ClearQueue(); | ||
580 | |||
578 | if (!Running) | 581 | if (!Running) |
579 | return true; | 582 | return true; |
580 | 583 | ||
@@ -1065,45 +1068,52 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance | |||
1065 | 1068 | ||
1066 | public void SaveState() | 1069 | public void SaveState() |
1067 | { | 1070 | { |
1068 | // If we're currently in an event, just tell it to save upon return | 1071 | // We need to lock here to avoid any race with a thread that is removing this script. |
1069 | // | 1072 | lock (EventQueue) |
1070 | if (m_InEvent) | ||
1071 | { | 1073 | { |
1072 | m_SaveState = true; | 1074 | if (!Running) |
1073 | return; | 1075 | return; |
1074 | } | ||
1075 | 1076 | ||
1076 | // m_log.DebugFormat( | 1077 | // If we're currently in an event, just tell it to save upon return |
1077 | // "[SCRIPT INSTANCE]: Saving state for script {0} (id {1}) in part {2} (id {3}) in object {4} in {5}", | 1078 | // |
1078 | // ScriptTask.Name, ScriptTask.ItemID, Part.Name, Part.UUID, Part.ParentGroup.Name, Engine.World.Name); | 1079 | if (m_InEvent) |
1080 | { | ||
1081 | m_SaveState = true; | ||
1082 | return; | ||
1083 | } | ||
1079 | 1084 | ||
1080 | PluginData = AsyncCommandManager.GetSerializationData(Engine, ItemID); | 1085 | // m_log.DebugFormat( |
1086 | // "[SCRIPT INSTANCE]: Saving state for script {0} (id {1}) in part {2} (id {3}) in object {4} in {5}", | ||
1087 | // ScriptTask.Name, ScriptTask.ItemID, Part.Name, Part.UUID, Part.ParentGroup.Name, Engine.World.Name); | ||
1081 | 1088 | ||
1082 | string xml = ScriptSerializer.Serialize(this); | 1089 | PluginData = AsyncCommandManager.GetSerializationData(Engine, ItemID); |
1083 | 1090 | ||
1084 | // Compare hash of the state we just just created with the state last written to disk | 1091 | string xml = ScriptSerializer.Serialize(this); |
1085 | // If the state is different, update the disk file. | ||
1086 | UUID hash = UUID.Parse(Utils.MD5String(xml)); | ||
1087 | 1092 | ||
1088 | if (hash != m_CurrentStateHash) | 1093 | // Compare hash of the state we just just created with the state last written to disk |
1089 | { | 1094 | // If the state is different, update the disk file. |
1090 | try | 1095 | UUID hash = UUID.Parse(Utils.MD5String(xml)); |
1096 | |||
1097 | if (hash != m_CurrentStateHash) | ||
1091 | { | 1098 | { |
1092 | using (FileStream fs = File.Create(Path.Combine(m_dataPath, ItemID.ToString() + ".state"))) | 1099 | try |
1100 | { | ||
1101 | using (FileStream fs = File.Create(Path.Combine(m_dataPath, ItemID.ToString() + ".state"))) | ||
1102 | { | ||
1103 | Byte[] buf = Util.UTF8NoBomEncoding.GetBytes(xml); | ||
1104 | fs.Write(buf, 0, buf.Length); | ||
1105 | } | ||
1106 | } | ||
1107 | catch(Exception) | ||
1093 | { | 1108 | { |
1094 | Byte[] buf = Util.UTF8NoBomEncoding.GetBytes(xml); | 1109 | // m_log.Error("Unable to save xml\n"+e.ToString()); |
1095 | fs.Write(buf, 0, buf.Length); | ||
1096 | } | 1110 | } |
1111 | //if (!File.Exists(Path.Combine(Path.GetDirectoryName(assembly), ItemID.ToString() + ".state"))) | ||
1112 | //{ | ||
1113 | // throw new Exception("Completed persistence save, but no file was created"); | ||
1114 | //} | ||
1115 | m_CurrentStateHash = hash; | ||
1097 | } | 1116 | } |
1098 | catch(Exception) | ||
1099 | { | ||
1100 | // m_log.Error("Unable to save xml\n"+e.ToString()); | ||
1101 | } | ||
1102 | //if (!File.Exists(Path.Combine(Path.GetDirectoryName(assembly), ItemID.ToString() + ".state"))) | ||
1103 | //{ | ||
1104 | // throw new Exception("Completed persistence save, but no file was created"); | ||
1105 | //} | ||
1106 | m_CurrentStateHash = hash; | ||
1107 | } | 1117 | } |
1108 | } | 1118 | } |
1109 | 1119 | ||
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 | |||
732 | 732 | ||
733 | // Clear the event queue and abort the instance thread | 733 | // Clear the event queue and abort the instance thread |
734 | // | 734 | // |
735 | instance.ClearQueue(); | 735 | instance.Stop(0, true); |
736 | instance.Stop(0); | ||
737 | 736 | ||
738 | // Release events, timer, etc | 737 | // Release events, timer, etc |
739 | // | 738 | // |
@@ -859,8 +858,6 @@ namespace OpenSim.Region.ScriptEngine.XEngine | |||
859 | } | 858 | } |
860 | } | 859 | } |
861 | 860 | ||
862 | instances.Clear(); | ||
863 | |||
864 | if (saveTime > 0) | 861 | if (saveTime > 0) |
865 | m_ThreadPool.QueueWorkItem(new WorkItemCallback(this.DoBackup), | 862 | m_ThreadPool.QueueWorkItem(new WorkItemCallback(this.DoBackup), |
866 | new Object[] { saveTime }); | 863 | new Object[] { saveTime }); |
@@ -1443,6 +1440,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine | |||
1443 | m_Scripts.Remove(itemID); | 1440 | m_Scripts.Remove(itemID); |
1444 | } | 1441 | } |
1445 | 1442 | ||
1443 | |||
1446 | instance.ClearQueue(); | 1444 | instance.ClearQueue(); |
1447 | 1445 | ||
1448 | instance.Stop(m_WaitForEventCompletionOnScriptStop); | 1446 | instance.Stop(m_WaitForEventCompletionOnScriptStop); |