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