If [XEngine] ScriptStopStrategy is changed between abort and co-op, for the existing session use the previous strategy for that script rather than not starting the script at all.
We have to do this since we can't unload existing DLLs if they're all in the same AppDomain. But we can still update the underlying DLL which will be used in the next simulator session.bullet-2.82
parent
6d3b409af2
commit
d7b9260496
|
@ -34,7 +34,36 @@ namespace OpenSim.Region.ScriptEngine.Interfaces
|
||||||
{
|
{
|
||||||
public interface ICompiler
|
public interface ICompiler
|
||||||
{
|
{
|
||||||
void PerformScriptCompile(string source, string asset, UUID ownerID, out string assembly, out Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> linemap);
|
/// <summary>
|
||||||
|
/// Performs the script compile.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Script"></param>
|
||||||
|
/// <param name="asset"></param>
|
||||||
|
/// <param name="ownerUUID"></param>
|
||||||
|
/// <param name="alwaysRecompile">
|
||||||
|
/// If set to true then always recompile the script, even if we have a DLL already cached.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="assembly"></param>
|
||||||
|
/// <param name="linemap"></param>
|
||||||
|
void PerformScriptCompile(
|
||||||
|
string source, string asset, UUID ownerID,
|
||||||
|
out string assembly, out Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> linemap);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs the script compile.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Script"></param>
|
||||||
|
/// <param name="asset"></param>
|
||||||
|
/// <param name="ownerUUID"></param>
|
||||||
|
/// <param name="alwaysRecompile">
|
||||||
|
/// If set to true then always recompile the script, even if we have a DLL already cached.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="assembly"></param>
|
||||||
|
/// <param name="linemap"></param>
|
||||||
|
void PerformScriptCompile(
|
||||||
|
string source, string asset, UUID ownerID, bool alwaysRecompile,
|
||||||
|
out string assembly, out Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> linemap);
|
||||||
|
|
||||||
string[] GetWarnings();
|
string[] GetWarnings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -284,12 +284,15 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
|
||||||
return GetCompilerOutput(assetID.ToString());
|
return GetCompilerOutput(assetID.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public void PerformScriptCompile(
|
||||||
/// Converts script from LSL to CS and calls CompileFromCSText
|
string source, string asset, UUID ownerUUID,
|
||||||
/// </summary>
|
out string assembly, out Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> linemap)
|
||||||
/// <param name="Script">LSL script</param>
|
{
|
||||||
/// <returns>Filename to .dll assembly</returns>
|
PerformScriptCompile(source, asset, ownerUUID, false, out assembly, out linemap);
|
||||||
public void PerformScriptCompile(string Script, string asset, UUID ownerUUID,
|
}
|
||||||
|
|
||||||
|
public void PerformScriptCompile(
|
||||||
|
string source, string asset, UUID ownerUUID, bool alwaysRecompile,
|
||||||
out string assembly, out Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> linemap)
|
out string assembly, out Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> linemap)
|
||||||
{
|
{
|
||||||
// m_log.DebugFormat("[Compiler]: Compiling script\n{0}", Script);
|
// m_log.DebugFormat("[Compiler]: Compiling script\n{0}", Script);
|
||||||
|
@ -303,9 +306,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
|
||||||
|
|
||||||
CheckOrCreateScriptsDirectory();
|
CheckOrCreateScriptsDirectory();
|
||||||
|
|
||||||
// Don't recompile if we already have it
|
// Don't recompile if we're not forced to and we already have it
|
||||||
// Performing 3 file exists tests for every script can still be slow
|
// Performing 3 file exists tests for every script can still be slow
|
||||||
if (File.Exists(assembly) && File.Exists(assembly + ".text") && File.Exists(assembly + ".map"))
|
if (!alwaysRecompile && File.Exists(assembly) && File.Exists(assembly + ".text") && File.Exists(assembly + ".map"))
|
||||||
{
|
{
|
||||||
// If we have already read this linemap file, then it will be in our dictionary.
|
// If we have already read this linemap file, then it will be in our dictionary.
|
||||||
// Don't build another copy of the dictionary (saves memory) and certainly
|
// Don't build another copy of the dictionary (saves memory) and certainly
|
||||||
|
@ -316,29 +319,27 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Script == String.Empty)
|
if (source == String.Empty)
|
||||||
{
|
|
||||||
throw new Exception("Cannot find script assembly and no script text present");
|
throw new Exception("Cannot find script assembly and no script text present");
|
||||||
}
|
|
||||||
|
|
||||||
enumCompileType language = DefaultCompileLanguage;
|
enumCompileType language = DefaultCompileLanguage;
|
||||||
|
|
||||||
if (Script.StartsWith("//c#", true, CultureInfo.InvariantCulture))
|
if (source.StartsWith("//c#", true, CultureInfo.InvariantCulture))
|
||||||
language = enumCompileType.cs;
|
language = enumCompileType.cs;
|
||||||
if (Script.StartsWith("//vb", true, CultureInfo.InvariantCulture))
|
if (source.StartsWith("//vb", true, CultureInfo.InvariantCulture))
|
||||||
{
|
{
|
||||||
language = enumCompileType.vb;
|
language = enumCompileType.vb;
|
||||||
// We need to remove //vb, it won't compile with that
|
// We need to remove //vb, it won't compile with that
|
||||||
|
|
||||||
Script = Script.Substring(4, Script.Length - 4);
|
source = source.Substring(4, source.Length - 4);
|
||||||
}
|
}
|
||||||
if (Script.StartsWith("//lsl", true, CultureInfo.InvariantCulture))
|
if (source.StartsWith("//lsl", true, CultureInfo.InvariantCulture))
|
||||||
language = enumCompileType.lsl;
|
language = enumCompileType.lsl;
|
||||||
|
|
||||||
if (Script.StartsWith("//js", true, CultureInfo.InvariantCulture))
|
if (source.StartsWith("//js", true, CultureInfo.InvariantCulture))
|
||||||
language = enumCompileType.js;
|
language = enumCompileType.js;
|
||||||
|
|
||||||
if (Script.StartsWith("//yp", true, CultureInfo.InvariantCulture))
|
if (source.StartsWith("//yp", true, CultureInfo.InvariantCulture))
|
||||||
language = enumCompileType.yp;
|
language = enumCompileType.yp;
|
||||||
|
|
||||||
// m_log.DebugFormat("[Compiler]: Compile language is {0}", language);
|
// m_log.DebugFormat("[Compiler]: Compile language is {0}", language);
|
||||||
|
@ -359,13 +360,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools
|
||||||
throw new Exception(errtext);
|
throw new Exception(errtext);
|
||||||
}
|
}
|
||||||
|
|
||||||
string compileScript = Script;
|
string compileScript = source;
|
||||||
|
|
||||||
if (language == enumCompileType.lsl)
|
if (language == enumCompileType.lsl)
|
||||||
{
|
{
|
||||||
// Its LSL, convert it to C#
|
// Its LSL, convert it to C#
|
||||||
LSL_Converter = (ICodeConverter)new CSCodeGenerator(comms, m_insertCoopTerminationCalls);
|
LSL_Converter = (ICodeConverter)new CSCodeGenerator(comms, m_insertCoopTerminationCalls);
|
||||||
compileScript = LSL_Converter.Convert(Script);
|
compileScript = LSL_Converter.Convert(source);
|
||||||
|
|
||||||
// copy converter warnings into our warnings.
|
// copy converter warnings into our warnings.
|
||||||
foreach (string warning in LSL_Converter.GetWarnings())
|
foreach (string warning in LSL_Converter.GetWarnings())
|
||||||
|
|
|
@ -67,8 +67,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools.Tests
|
||||||
}
|
}
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void SetUp()
|
public override void SetUp()
|
||||||
{
|
{
|
||||||
|
base.SetUp();
|
||||||
|
|
||||||
// Create a CSCodeProvider and CompilerParameters.
|
// Create a CSCodeProvider and CompilerParameters.
|
||||||
m_CSCodeProvider = new CSharpCodeProvider();
|
m_CSCodeProvider = new CSharpCodeProvider();
|
||||||
m_compilerParameters = new CompilerParameters();
|
m_compilerParameters = new CompilerParameters();
|
||||||
|
|
|
@ -237,12 +237,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
|
||||||
m_postOnRez = postOnRez;
|
m_postOnRez = postOnRez;
|
||||||
m_AttachedAvatar = Part.ParentGroup.AttachedAvatar;
|
m_AttachedAvatar = Part.ParentGroup.AttachedAvatar;
|
||||||
m_RegionID = Part.ParentGroup.Scene.RegionInfo.RegionID;
|
m_RegionID = Part.ParentGroup.Scene.RegionInfo.RegionID;
|
||||||
|
|
||||||
if (Engine.Config.GetString("ScriptStopStrategy", "abort") == "co-op")
|
|
||||||
{
|
|
||||||
m_coopTermination = true;
|
|
||||||
m_coopSleepHandle = new XEngineEventWaitHandle(false, EventResetMode.AutoReset);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -252,54 +246,38 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
|
||||||
/// <param name='assembly'></param>
|
/// <param name='assembly'></param>
|
||||||
/// <param name='stateSource'></param>
|
/// <param name='stateSource'></param>
|
||||||
/// <returns>false if load failed, true if suceeded</returns>
|
/// <returns>false if load failed, true if suceeded</returns>
|
||||||
public bool Load(AppDomain dom, string assembly, StateSource stateSource)
|
public bool Load(AppDomain dom, Assembly scriptAssembly, StateSource stateSource)
|
||||||
{
|
{
|
||||||
m_Assembly = assembly;
|
//m_Assembly = scriptAssembly.CodeBase;
|
||||||
|
m_Assembly = scriptAssembly.Location;
|
||||||
m_stateSource = stateSource;
|
m_stateSource = stateSource;
|
||||||
|
|
||||||
ApiManager am = new ApiManager();
|
|
||||||
|
|
||||||
foreach (string api in am.GetApis())
|
|
||||||
{
|
|
||||||
m_Apis[api] = am.CreateApi(api);
|
|
||||||
m_Apis[api].Initialize(Engine, Part, ScriptTask, m_coopSleepHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
object[] constructorParams;
|
object[] constructorParams;
|
||||||
|
|
||||||
Assembly scriptAssembly = dom.Load(Path.GetFileNameWithoutExtension(assembly));
|
|
||||||
Type scriptType = scriptAssembly.GetType("SecondLife.XEngineScript");
|
Type scriptType = scriptAssembly.GetType("SecondLife.XEngineScript");
|
||||||
|
|
||||||
if (scriptType != null)
|
if (scriptType != null)
|
||||||
{
|
{
|
||||||
|
m_coopTermination = true;
|
||||||
|
m_coopSleepHandle = new XEngineEventWaitHandle(false, EventResetMode.AutoReset);
|
||||||
constructorParams = new object[] { m_coopSleepHandle };
|
constructorParams = new object[] { m_coopSleepHandle };
|
||||||
}
|
}
|
||||||
else if (!m_coopTermination)
|
|
||||||
{
|
|
||||||
scriptType = scriptAssembly.GetType("SecondLife.Script");
|
|
||||||
constructorParams = null;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_log.ErrorFormat(
|
m_coopTermination = false;
|
||||||
"[SCRIPT INSTANCE]: Not starting script {0} (id {1}) in part {2} (id {3}) in object {4} in {5}. You must remove all existing {6}* script DLL files before using enabling co-op termination"
|
scriptType = scriptAssembly.GetType("SecondLife.Script");
|
||||||
+ ", either by setting DeleteScriptsOnStartup = true in [XEngine] for one run"
|
constructorParams = null;
|
||||||
+ " or by deleting these files manually.",
|
|
||||||
ScriptTask.Name, ScriptTask.ItemID, Part.Name, Part.UUID, Part.ParentGroup.Name, Engine.World.Name, assembly);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// m_log.DebugFormat(
|
// m_log.DebugFormat(
|
||||||
// "[SCRIPT INSTANCE]: Looking to load {0} from assembly {1} in {2}",
|
// "[SCRIPT INSTANCE]: Looking to load {0} from assembly {1} in {2}",
|
||||||
// scriptType.FullName, Path.GetFileNameWithoutExtension(assembly), Engine.World.Name);
|
// scriptType.FullName, m_Assembly, Engine.World.Name);
|
||||||
|
|
||||||
if (dom != System.AppDomain.CurrentDomain)
|
if (dom != System.AppDomain.CurrentDomain)
|
||||||
m_Script
|
m_Script
|
||||||
= (IScript)dom.CreateInstanceAndUnwrap(
|
= (IScript)dom.CreateInstanceAndUnwrap(
|
||||||
Path.GetFileNameWithoutExtension(assembly),
|
Path.GetFileNameWithoutExtension(m_Assembly),
|
||||||
scriptType.FullName,
|
scriptType.FullName,
|
||||||
false,
|
false,
|
||||||
BindingFlags.Default,
|
BindingFlags.Default,
|
||||||
|
@ -327,11 +305,19 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
|
||||||
{
|
{
|
||||||
m_log.ErrorFormat(
|
m_log.ErrorFormat(
|
||||||
"[SCRIPT INSTANCE]: Not starting script {0} (id {1}) in part {2} (id {3}) in object {4} in {5}. Error loading assembly {6}. Exception {7}{8}",
|
"[SCRIPT INSTANCE]: Not starting script {0} (id {1}) in part {2} (id {3}) in object {4} in {5}. Error loading assembly {6}. Exception {7}{8}",
|
||||||
ScriptTask.Name, ScriptTask.ItemID, Part.Name, Part.UUID, Part.ParentGroup.Name, Engine.World.Name, assembly, e.Message, e.StackTrace);
|
ScriptTask.Name, ScriptTask.ItemID, Part.Name, Part.UUID, Part.ParentGroup.Name, Engine.World.Name, m_Assembly, e.Message, e.StackTrace);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ApiManager am = new ApiManager();
|
||||||
|
|
||||||
|
foreach (string api in am.GetApis())
|
||||||
|
{
|
||||||
|
m_Apis[api] = am.CreateApi(api);
|
||||||
|
m_Apis[api].Initialize(Engine, Part, ScriptTask, m_coopSleepHandle);
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
foreach (KeyValuePair<string,IScriptApi> kv in m_Apis)
|
foreach (KeyValuePair<string,IScriptApi> kv in m_Apis)
|
||||||
|
@ -341,8 +327,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
|
||||||
|
|
||||||
// // m_log.Debug("[Script] Script instance created");
|
// // m_log.Debug("[Script] Script instance created");
|
||||||
|
|
||||||
Part.SetScriptEvents(ItemID,
|
Part.SetScriptEvents(ItemID, (int)m_Script.GetStateEventFlags(State));
|
||||||
(int)m_Script.GetStateEventFlags(State));
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -355,8 +340,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
|
||||||
|
|
||||||
m_SaveState = true;
|
m_SaveState = true;
|
||||||
|
|
||||||
string savedState = Path.Combine(Path.GetDirectoryName(assembly),
|
string savedState = Path.Combine(Path.GetDirectoryName(m_Assembly), ItemID.ToString() + ".state");
|
||||||
ItemID.ToString() + ".state");
|
|
||||||
if (File.Exists(savedState))
|
if (File.Exists(savedState))
|
||||||
{
|
{
|
||||||
string xml = String.Empty;
|
string xml = String.Empty;
|
||||||
|
|
|
@ -87,7 +87,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine.Tests
|
||||||
public void TestCompileAndStartScript()
|
public void TestCompileAndStartScript()
|
||||||
{
|
{
|
||||||
TestHelpers.InMethod();
|
TestHelpers.InMethod();
|
||||||
// log4net.Config.XmlConfigurator.Configure();
|
TestHelpers.EnableLogging();
|
||||||
|
|
||||||
UUID userId = TestHelpers.ParseTail(0x1);
|
UUID userId = TestHelpers.ParseTail(0x1);
|
||||||
// UUID objectId = TestHelpers.ParseTail(0x100);
|
// UUID objectId = TestHelpers.ParseTail(0x100);
|
||||||
|
|
|
@ -86,6 +86,8 @@ namespace OpenSim.Region.ScriptEngine.XEngine
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private int m_StartDelay;
|
private int m_StartDelay;
|
||||||
|
|
||||||
|
private bool m_coopTermination;
|
||||||
|
|
||||||
private int m_IdleTimeout;
|
private int m_IdleTimeout;
|
||||||
private int m_StackSize;
|
private int m_StackSize;
|
||||||
private int m_SleepTime;
|
private int m_SleepTime;
|
||||||
|
@ -246,6 +248,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
|
||||||
|
|
||||||
if (rawScriptStopStrategy == "co-op")
|
if (rawScriptStopStrategy == "co-op")
|
||||||
{
|
{
|
||||||
|
m_coopTermination = true;
|
||||||
ScriptClassName = "XEngineScript";
|
ScriptClassName = "XEngineScript";
|
||||||
ScriptBaseClassName = typeof(XEngineScriptBase).FullName;
|
ScriptBaseClassName = typeof(XEngineScriptBase).FullName;
|
||||||
ScriptBaseClassParameters = typeof(XEngineScriptBase).GetConstructor(new Type[] { typeof(WaitHandle) }).GetParameters();
|
ScriptBaseClassParameters = typeof(XEngineScriptBase).GetConstructor(new Type[] { typeof(WaitHandle) }).GetParameters();
|
||||||
|
@ -1139,7 +1142,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
|
||||||
|
|
||||||
ScenePresence presence = m_Scene.GetScenePresence(item.OwnerID);
|
ScenePresence presence = m_Scene.GetScenePresence(item.OwnerID);
|
||||||
|
|
||||||
string assembly = "";
|
string assemblyPath = "";
|
||||||
|
|
||||||
Culture.SetCurrentCulture();
|
Culture.SetCurrentCulture();
|
||||||
|
|
||||||
|
@ -1151,12 +1154,12 @@ namespace OpenSim.Region.ScriptEngine.XEngine
|
||||||
{
|
{
|
||||||
lock (m_AddingAssemblies)
|
lock (m_AddingAssemblies)
|
||||||
{
|
{
|
||||||
m_Compiler.PerformScriptCompile(script, assetID.ToString(), item.OwnerID, out assembly, out linemap);
|
m_Compiler.PerformScriptCompile(script, assetID.ToString(), item.OwnerID, out assemblyPath, out linemap);
|
||||||
|
|
||||||
if (!m_AddingAssemblies.ContainsKey(assembly)) {
|
if (!m_AddingAssemblies.ContainsKey(assemblyPath)) {
|
||||||
m_AddingAssemblies[assembly] = 1;
|
m_AddingAssemblies[assemblyPath] = 1;
|
||||||
} else {
|
} else {
|
||||||
m_AddingAssemblies[assembly]++;
|
m_AddingAssemblies[assemblyPath]++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1301,19 +1304,43 @@ namespace OpenSim.Region.ScriptEngine.XEngine
|
||||||
m_ScriptFailCount++;
|
m_ScriptFailCount++;
|
||||||
lock (m_AddingAssemblies)
|
lock (m_AddingAssemblies)
|
||||||
{
|
{
|
||||||
m_AddingAssemblies[assembly]--;
|
m_AddingAssemblies[assemblyPath]--;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_DomainScripts[appDomain].Add(itemID);
|
m_DomainScripts[appDomain].Add(itemID);
|
||||||
|
|
||||||
|
Assembly scriptAssembly = m_AppDomains[appDomain].Load(Path.GetFileNameWithoutExtension(assemblyPath));
|
||||||
|
bool recompile = false;
|
||||||
|
|
||||||
|
if (m_coopTermination)
|
||||||
|
{
|
||||||
|
Type scriptType = scriptAssembly.GetType("SecondLife.XEngineScript");
|
||||||
|
|
||||||
|
if (scriptType == null)
|
||||||
|
recompile = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Type scriptType = scriptAssembly.GetType("SecondLife.Script");
|
||||||
|
|
||||||
|
if (scriptType == null)
|
||||||
|
recompile = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are loading all scripts into the same AppDomain, then we can't reload the DLL in this
|
||||||
|
// simulator session if the script halt strategy has been changed. Instead, we'll continue with
|
||||||
|
// the existing DLL and the new one will be used in the next simulator session.
|
||||||
|
if (recompile)
|
||||||
|
m_Compiler.PerformScriptCompile(script, assetID.ToString(), item.OwnerID, true, out assemblyPath, out linemap);
|
||||||
|
|
||||||
instance = new ScriptInstance(this, part,
|
instance = new ScriptInstance(this, part,
|
||||||
item,
|
item,
|
||||||
startParam, postOnRez,
|
startParam, postOnRez,
|
||||||
m_MaxScriptQueue);
|
m_MaxScriptQueue);
|
||||||
|
|
||||||
if (!instance.Load(m_AppDomains[appDomain], assembly, stateSource))
|
if (!instance.Load(m_AppDomains[appDomain], scriptAssembly, stateSource))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// if (DebugLevel >= 1)
|
// if (DebugLevel >= 1)
|
||||||
|
@ -1345,11 +1372,11 @@ namespace OpenSim.Region.ScriptEngine.XEngine
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_Assemblies.ContainsKey(assetID))
|
if (!m_Assemblies.ContainsKey(assetID))
|
||||||
m_Assemblies[assetID] = assembly;
|
m_Assemblies[assetID] = assemblyPath;
|
||||||
|
|
||||||
lock (m_AddingAssemblies)
|
lock (m_AddingAssemblies)
|
||||||
{
|
{
|
||||||
m_AddingAssemblies[assembly]--;
|
m_AddingAssemblies[assemblyPath]--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instance != null)
|
if (instance != null)
|
||||||
|
|
Loading…
Reference in New Issue