From 889194db63016ad4b9ecb0c6ae82d3d9c7632c95 Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Wed, 2 Jul 2014 23:48:44 +0100 Subject: Actually call Close() for shared region modules when the simulator is being shutdown. Adds regression test for this case. --- .../RegionModulesControllerPlugin.cs | 87 ++++--- OpenSim/Framework/Servers/BaseOpenSimServer.cs | 8 +- OpenSim/Framework/Util.cs | 7 +- OpenSim/Region/Application/OpenSimBase.cs | 44 +++- .../Region/CoreModules/Asset/CenomeAssetCache.cs | 9 +- OpenSim/Region/Framework/Scenes/Scene.cs | 1 + .../Scenes/Tests/SharedRegionModuleTests.cs | 249 +++++++++++++++++++++ OpenSim/Tests/Common/OpenSimTestCase.cs | 1 + prebuild.xml | 4 + 9 files changed, 361 insertions(+), 49 deletions(-) create mode 100644 OpenSim/Region/Framework/Scenes/Tests/SharedRegionModuleTests.cs diff --git a/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs b/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs index 510be37..e03483a 100644 --- a/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs +++ b/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs @@ -32,6 +32,7 @@ using log4net; using Mono.Addins; using Nini.Config; using OpenSim; +using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; @@ -45,6 +46,12 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController LogManager.GetLogger( MethodBase.GetCurrentMethod().DeclaringType); + /// + /// Controls whether we load modules from Mono.Addins. + /// + /// For debug purposes. Defaults to true. + public bool LoadModulesFromAddins { get; set; } + // Config access private OpenSimBase m_openSim; @@ -61,6 +68,11 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController private List m_sharedInstances = new List(); + public RegionModulesControllerPlugin() + { + LoadModulesFromAddins = true; + } + #region IApplicationPlugin implementation public void Initialise (OpenSimBase openSim) @@ -69,6 +81,9 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController m_openSim.ApplicationRegistry.RegisterInterface(this); m_log.DebugFormat("[REGIONMODULES]: Initializing..."); + if (!LoadModulesFromAddins) + return; + // Who we are string id = AddinManager.CurrentAddin.Id; @@ -88,40 +103,8 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController Dictionary> loadedModules = new Dictionary>(); // Scan modules and load all that aren't disabled - foreach (TypeExtensionNode node in - AddinManager.GetExtensionNodes("/OpenSim/RegionModules")) - { - IList loadedModuleData; - - if (!loadedModules.ContainsKey(node.Addin)) - loadedModules.Add(node.Addin, new List { 0, 0, 0 }); - - loadedModuleData = loadedModules[node.Addin]; - - if (node.Type.GetInterface(typeof(ISharedRegionModule).ToString()) != null) - { - if (CheckModuleEnabled(node, modulesConfig)) - { - m_log.DebugFormat("[REGIONMODULES]: Found shared region module {0}, class {1}", node.Id, node.Type); - m_sharedModules.Add(node); - loadedModuleData[0]++; - } - } - else if (node.Type.GetInterface(typeof(INonSharedRegionModule).ToString()) != null) - { - if (CheckModuleEnabled(node, modulesConfig)) - { - m_log.DebugFormat("[REGIONMODULES]: Found non-shared region module {0}, class {1}", node.Id, node.Type); - m_nonSharedModules.Add(node); - loadedModuleData[1]++; - } - } - else - { - m_log.WarnFormat("[REGIONMODULES]: Found unknown type of module {0}, class {1}", node.Id, node.Type); - loadedModuleData[2]++; - } - } + foreach (TypeExtensionNode node in AddinManager.GetExtensionNodes("/OpenSim/RegionModules")) + AddNode(node, modulesConfig, loadedModules); foreach (KeyValuePair> loadedModuleData in loadedModules) { @@ -194,6 +177,41 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController #region IPlugin implementation + private void AddNode( + TypeExtensionNode node, IConfig modulesConfig, Dictionary> loadedModules) + { + IList loadedModuleData; + + if (!loadedModules.ContainsKey(node.Addin)) + loadedModules.Add(node.Addin, new List { 0, 0, 0 }); + + loadedModuleData = loadedModules[node.Addin]; + + if (node.Type.GetInterface(typeof(ISharedRegionModule).ToString()) != null) + { + if (CheckModuleEnabled(node, modulesConfig)) + { + m_log.DebugFormat("[REGIONMODULES]: Found shared region module {0}, class {1}", node.Id, node.Type); + m_sharedModules.Add(node); + loadedModuleData[0]++; + } + } + else if (node.Type.GetInterface(typeof(INonSharedRegionModule).ToString()) != null) + { + if (CheckModuleEnabled(node, modulesConfig)) + { + m_log.DebugFormat("[REGIONMODULES]: Found non-shared region module {0}, class {1}", node.Id, node.Type); + m_nonSharedModules.Add(node); + loadedModuleData[1]++; + } + } + else + { + m_log.WarnFormat("[REGIONMODULES]: Found unknown type of module {0}, class {1}", node.Id, node.Type); + loadedModuleData[2]++; + } + } + // We don't do that here // public void Initialise () @@ -215,6 +233,7 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController m_sharedInstances[0].Close(); m_sharedInstances.RemoveAt(0); } + m_sharedModules.Clear(); m_nonSharedModules.Clear(); } diff --git a/OpenSim/Framework/Servers/BaseOpenSimServer.cs b/OpenSim/Framework/Servers/BaseOpenSimServer.cs index 54e6061..828a852 100644 --- a/OpenSim/Framework/Servers/BaseOpenSimServer.cs +++ b/OpenSim/Framework/Servers/BaseOpenSimServer.cs @@ -57,6 +57,11 @@ namespace OpenSim.Framework.Servers private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); /// + /// Used by tests to suppress Environment.Exit(0) so that post-run operations are possible. + /// + public bool SuppressExit { get; set; } + + /// /// This will control a periodic log printout of the current 'show stats' (if they are active) for this /// server. /// @@ -109,7 +114,8 @@ namespace OpenSim.Framework.Servers base.ShutdownSpecific(); - Environment.Exit(0); + if (!SuppressExit) + Environment.Exit(0); } /// diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index 740e55a..729281c 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -1964,10 +1964,15 @@ namespace OpenSim.Framework { if (maxThreads < 2) throw new ArgumentOutOfRangeException("maxThreads", "maxThreads must be greater than 2"); + if (minThreads > maxThreads || minThreads < 2) throw new ArgumentOutOfRangeException("minThreads", "minThreads must be greater than 2 and less than or equal to maxThreads"); + if (m_ThreadPool != null) - throw new InvalidOperationException("SmartThreadPool is already initialized"); + { + m_log.Warn("SmartThreadPool is already initialized. Ignoring request."); + return; + } STPStartInfo startInfo = new STPStartInfo(); startInfo.ThreadPoolName = "Util"; diff --git a/OpenSim/Region/Application/OpenSimBase.cs b/OpenSim/Region/Application/OpenSimBase.cs index 4c1914a..d2dce24 100644 --- a/OpenSim/Region/Application/OpenSimBase.cs +++ b/OpenSim/Region/Application/OpenSimBase.cs @@ -71,6 +71,20 @@ namespace OpenSim // OpenSim.ini Section name for ESTATES Settings public const string ESTATE_SECTION_NAME = "Estates"; + /// + /// Allow all plugin loading to be disabled for tests/debug. + /// + /// + /// true by default + /// + public bool EnableInitialPluginLoad { get; set; } + + /// + /// Control whether we attempt to load an estate data service. + /// + /// For tests/debugging + public bool LoadEstateDataService { get; set; } + protected string proxyUrl; protected int proxyOffset = 0; @@ -96,7 +110,7 @@ namespace OpenSim public ConsoleCommand CreateAccount = null; - protected List m_plugins = new List(); + public List m_plugins = new List(); /// /// The config information passed into the OpenSimulator region server. @@ -135,6 +149,8 @@ namespace OpenSim /// public OpenSimBase(IConfigSource configSource) : base() { + EnableInitialPluginLoad = true; + LoadEstateDataService = true; LoadConfigSettings(configSource); } @@ -236,20 +252,25 @@ namespace OpenSim if (String.IsNullOrEmpty(module)) throw new Exception("Configuration file is missing the LocalServiceModule parameter in the [EstateDataStore] or [EstateService] section"); - m_estateDataService = ServerUtils.LoadPlugin(module, new object[] { Config }); - if (m_estateDataService == null) - throw new Exception( - string.Format( - "Could not load an IEstateDataService implementation from {0}, as configured in the LocalServiceModule parameter of the [EstateDataStore] config section.", - module)); + if (LoadEstateDataService) + { + m_estateDataService = ServerUtils.LoadPlugin(module, new object[] { Config }); + if (m_estateDataService == null) + throw new Exception( + string.Format( + "Could not load an IEstateDataService implementation from {0}, as configured in the LocalServiceModule parameter of the [EstateDataStore] config section.", + module)); + } base.StartupSpecific(); - LoadPlugins(); + if (EnableInitialPluginLoad) + LoadPlugins(); + + // We still want to post initalize any plugins even if loading has been disabled since a test may have + // inserted them manually. foreach (IApplicationPlugin plugin in m_plugins) - { plugin.PostInitialise(); - } if (m_console != null) AddPluginCommands(m_console); @@ -874,6 +895,9 @@ namespace OpenSim try { SceneManager.Close(); + + foreach (IApplicationPlugin plugin in m_plugins) + plugin.Dispose(); } catch (Exception e) { diff --git a/OpenSim/Region/CoreModules/Asset/CenomeAssetCache.cs b/OpenSim/Region/CoreModules/Asset/CenomeAssetCache.cs index 9b0e1f4..ebec9d2 100644 --- a/OpenSim/Region/CoreModules/Asset/CenomeAssetCache.cs +++ b/OpenSim/Region/CoreModules/Asset/CenomeAssetCache.cs @@ -316,9 +316,12 @@ namespace OpenSim.Region.CoreModules.Asset /// public void Close() { - m_enabled = false; - m_cache.Clear(); - m_cache = null; + if (m_enabled) + { + m_enabled = false; + m_cache.Clear(); + m_cache = null; + } } /// diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index ca42d5b..3957ba6 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -1894,6 +1894,7 @@ namespace OpenSim.Region.Framework.Scenes RegionInfo.RegionID, RegionInfo.RegionLocX, RegionInfo.RegionLocY, RegionInfo.RegionSizeX, RegionInfo.RegionSizeY); + if (error != String.Empty) throw new Exception(error); } diff --git a/OpenSim/Region/Framework/Scenes/Tests/SharedRegionModuleTests.cs b/OpenSim/Region/Framework/Scenes/Tests/SharedRegionModuleTests.cs new file mode 100644 index 0000000..d499f87 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/Tests/SharedRegionModuleTests.cs @@ -0,0 +1,249 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Net; +using Mono.Addins; +using Nini.Config; +using NUnit.Framework; +using OpenMetaverse; +using OpenSim; +using OpenSim.ApplicationPlugins.RegionModulesController; +using OpenSim.Framework; +using OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Tests.Common; + +namespace OpenSim.Region.Framework.Scenes.Tests +{ + public class SharedRegionModuleTests : OpenSimTestCase + { + [Test] + public void TestLifecycle() + { + TestHelpers.InMethod(); +// TestHelpers.EnableLogging(); + + UUID estateOwnerId = TestHelpers.ParseTail(0x1); + UUID regionId = TestHelpers.ParseTail(0x10); + + IConfigSource configSource = new IniConfigSource(); + configSource.AddConfig("Startup"); + configSource.AddConfig("Modules"); + +// // We use this to skip estate questions + // Turns out not to be needed is estate owner id is pre-set in region information. +// IConfig estateConfig = configSource.AddConfig(OpenSimBase.ESTATE_SECTION_NAME); +// estateConfig.Set("DefaultEstateOwnerName", "Zaphod Beeblebrox"); +// estateConfig.Set("DefaultEstateOwnerUUID", estateOwnerId); +// estateConfig.Set("DefaultEstateOwnerEMail", "zaphod@galaxy.com"); +// estateConfig.Set("DefaultEstateOwnerPassword", "two heads"); + + // For grid servic + configSource.AddConfig("GridService"); + configSource.Configs["Modules"].Set("GridServices", "LocalGridServicesConnector"); + configSource.Configs["GridService"].Set("StorageProvider", "OpenSim.Data.Null.dll:NullRegionData"); + configSource.Configs["GridService"].Set("LocalServiceModule", "OpenSim.Services.GridService.dll:GridService"); + configSource.Configs["GridService"].Set("ConnectionString", "!static"); + + LocalGridServicesConnector gridService = new LocalGridServicesConnector(); +// + OpenSim sim = new OpenSim(configSource); + + sim.SuppressExit = true; + sim.EnableInitialPluginLoad = false; + sim.LoadEstateDataService = false; + sim.NetServersInfo.HttpListenerPort = 0; + + IRegistryCore reg = sim.ApplicationRegistry; + + RegionInfo ri = new RegionInfo(); + ri.RegionID = regionId; + ri.EstateSettings.EstateOwner = estateOwnerId; + ri.InternalEndPoint = new IPEndPoint(0, 0); + + MockRegionModulesControllerPlugin rmcp = new MockRegionModulesControllerPlugin(); + sim.m_plugins = new List() { rmcp }; + reg.RegisterInterface(rmcp); + + // XXX: Have to initialize directly for now + rmcp.Initialise(sim); + + rmcp.AddNode(gridService); + + TestSharedRegion tsr = new TestSharedRegion(); + rmcp.AddNode(tsr); + + // FIXME: Want to use the real one eventually but this is currently directly tied into Mono.Addins + // which has been written in such a way that makes it impossible to use for regression tests. +// RegionModulesControllerPlugin rmcp = new RegionModulesControllerPlugin(); +// rmcp.LoadModulesFromAddins = false; +//// reg.RegisterInterface(rmcp); +// rmcp.Initialise(sim); +// rmcp.PostInitialise(); +// TypeExtensionNode node = new TypeExtensionNode(); +// node. +// rmcp.AddNode(node, configSource.Configs["Modules"], new Dictionary>()); + + sim.Startup(); + IScene scene; + sim.CreateRegion(ri, out scene); + + sim.Shutdown(); + + List co = tsr.CallOrder; + int expectedEventCount = 6; + + Assert.AreEqual( + expectedEventCount, + co.Count, + "Expected {0} events but only got {1} ({2})", + expectedEventCount, co.Count, string.Join(",", co)); + Assert.AreEqual("Initialise", co[0]); + Assert.AreEqual("PostInitialise", co[1]); + Assert.AreEqual("AddRegion", co[2]); + Assert.AreEqual("RegionLoaded", co[3]); + Assert.AreEqual("RemoveRegion", co[4]); + Assert.AreEqual("Close", co[5]); + } + } + + class TestSharedRegion : ISharedRegionModule + { + // FIXME: Should really use MethodInfo + public List CallOrder = new List(); + + public string Name { get { return "TestSharedRegion"; } } + + public Type ReplaceableInterface { get { return null; } } + + public void PostInitialise() + { + CallOrder.Add("PostInitialise"); + } + + public void Initialise(IConfigSource source) + { + CallOrder.Add("Initialise"); + } + + public void Close() + { + CallOrder.Add("Close"); + } + + public void AddRegion(Scene scene) + { + CallOrder.Add("AddRegion"); + } + + public void RemoveRegion(Scene scene) + { + CallOrder.Add("RemoveRegion"); + } + + public void RegionLoaded(Scene scene) + { + CallOrder.Add("RegionLoaded"); + } + } + + class MockRegionModulesControllerPlugin : IRegionModulesController, IApplicationPlugin + { + // List of shared module instances, for adding to Scenes + private List m_sharedInstances = new List(); + + // Config access + private OpenSimBase m_openSim; + + public string Version { get { return "0"; } } + public string Name { get { return "MockRegionModulesControllerPlugin"; } } + + public void Initialise() {} + + public void Initialise(OpenSimBase sim) + { + m_openSim = sim; + } + + /// + /// Called when the application loading is completed + /// + public void PostInitialise() + { + foreach (ISharedRegionModule module in m_sharedInstances) + module.PostInitialise(); + } + + public void AddRegionToModules(Scene scene) + { + List sharedlist = new List(); + + foreach (ISharedRegionModule module in m_sharedInstances) + { + module.AddRegion(scene); + scene.AddRegionModule(module.Name, module); + + sharedlist.Add(module); + } + + foreach (ISharedRegionModule module in sharedlist) + { + module.RegionLoaded(scene); + } + } + + public void RemoveRegionFromModules(Scene scene) + { + foreach (IRegionModuleBase module in scene.RegionModules.Values) + { +// m_log.DebugFormat("[REGIONMODULE]: Removing scene {0} from module {1}", +// scene.RegionInfo.RegionName, module.Name); + module.RemoveRegion(scene); + } + + scene.RegionModules.Clear(); + } + + public void AddNode(ISharedRegionModule module) + { + m_sharedInstances.Add(module); + module.Initialise(m_openSim.ConfigSource.Source); + } + + public void Dispose() + { + // We expect that all regions have been removed already + while (m_sharedInstances.Count > 0) + { + m_sharedInstances[0].Close(); + m_sharedInstances.RemoveAt(0); + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Tests/Common/OpenSimTestCase.cs b/OpenSim/Tests/Common/OpenSimTestCase.cs index 3c47faa..c1415af 100644 --- a/OpenSim/Tests/Common/OpenSimTestCase.cs +++ b/OpenSim/Tests/Common/OpenSimTestCase.cs @@ -27,6 +27,7 @@ using System; using NUnit.Framework; +using OpenSim.Framework; using OpenSim.Framework.Servers; namespace OpenSim.Tests.Common diff --git a/prebuild.xml b/prebuild.xml index 0143e26..e2423ae 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -3185,8 +3185,11 @@ + + + @@ -3195,6 +3198,7 @@ + -- cgit v1.1