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
0.8.1-post-fixes
Justin Clark-Casey (justincc) 2015-01-16 23:55:00 +00:00
parent 8d724e90de
commit faaf47a86f
3 changed files with 48 additions and 39 deletions

View File

@ -178,8 +178,9 @@ namespace OpenSim.Region.ScriptEngine.Interfaces
/// <param name="timeout"></param> /// <param name="timeout"></param>
/// How many milliseconds we will wait for an existing script event to finish before /// How many milliseconds we will wait for an existing script event to finish before
/// forcibly aborting that event. /// forcibly aborting that event.
/// <param name="clearEventQueue">If true then the event queue is also cleared</param>
/// <returns>true if the script was successfully stopped, false otherwise</returns> /// <returns>true if the script was successfully stopped, false otherwise</returns>
bool Stop(int timeout); bool Stop(int timeout, bool clearEventQueue = false);
void SetState(string state); void SetState(string state);

View File

@ -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) if (DebugLevel >= 1)
m_log.DebugFormat( m_log.DebugFormat(
@ -575,6 +575,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
lock (EventQueue) lock (EventQueue)
{ {
if (clearEventQueue)
ClearQueue();
if (!Running) if (!Running)
return true; return true;
@ -1065,45 +1068,52 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
public void SaveState() public void SaveState()
{ {
// If we're currently in an event, just tell it to save upon return // We need to lock here to avoid any race with a thread that is removing this script.
// lock (EventQueue)
if (m_InEvent)
{ {
m_SaveState = true; if (!Running)
return; return;
}
// m_log.DebugFormat( // If we're currently in an event, just tell it to save upon return
// "[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 (m_InEvent)
PluginData = AsyncCommandManager.GetSerializationData(Engine, ItemID);
string xml = ScriptSerializer.Serialize(this);
// 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)
{
try
{ {
using (FileStream fs = File.Create(Path.Combine(m_dataPath, ItemID.ToString() + ".state"))) m_SaveState = true;
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);
PluginData = AsyncCommandManager.GetSerializationData(Engine, ItemID);
string xml = ScriptSerializer.Serialize(this);
// 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)
{
try
{ {
Byte[] buf = Util.UTF8NoBomEncoding.GetBytes(xml); using (FileStream fs = File.Create(Path.Combine(m_dataPath, ItemID.ToString() + ".state")))
fs.Write(buf, 0, buf.Length); {
Byte[] buf = Util.UTF8NoBomEncoding.GetBytes(xml);
fs.Write(buf, 0, buf.Length);
}
} }
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;
} }
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;
} }
} }

View File

@ -732,8 +732,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
// Clear the event queue and abort the instance thread // Clear the event queue and abort the instance thread
// //
instance.ClearQueue(); instance.Stop(0, true);
instance.Stop(0);
// Release events, timer, etc // Release events, timer, etc
// //
@ -859,8 +858,6 @@ namespace OpenSim.Region.ScriptEngine.XEngine
} }
} }
instances.Clear();
if (saveTime > 0) if (saveTime > 0)
m_ThreadPool.QueueWorkItem(new WorkItemCallback(this.DoBackup), m_ThreadPool.QueueWorkItem(new WorkItemCallback(this.DoBackup),
new Object[] { saveTime }); new Object[] { saveTime });
@ -1443,6 +1440,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
m_Scripts.Remove(itemID); m_Scripts.Remove(itemID);
} }
instance.ClearQueue(); instance.ClearQueue();
instance.Stop(m_WaitForEventCompletionOnScriptStop); instance.Stop(m_WaitForEventCompletionOnScriptStop);