ScriptLoader.cs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSim Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. using System;
  28. using System.Collections;
  29. using System.Collections.Generic;
  30. using System.Reflection;
  31. using System.Text;
  32. using log4net;
  33. using OpenSim.ScriptEngine.Shared;
  34. using IScript=OpenSim.Region.ScriptEngine.Shared.ScriptBase.IScript;
  35. namespace OpenSim.ScriptEngine.Components.DotNetEngine.Scheduler
  36. {
  37. public class ScriptLoader : IScriptLoader
  38. {
  39. //
  40. // This class does AppDomain handling and loading/unloading of
  41. // scripts in it. It is instanced in "ScriptEngine" and controlled
  42. // from "ScriptManager"
  43. //
  44. // 1. Create a new AppDomain if old one is full (or doesn't exist)
  45. // 2. Load scripts into AppDomain
  46. // 3. Unload scripts from AppDomain (stopping them and marking
  47. // them as inactive)
  48. // 4. Unload AppDomain completely when all scripts in it has stopped
  49. //
  50. public string Name { get { return "SECS.DotNetEngine.Scheduler.ScriptLoader"; } }
  51. private int maxScriptsPerAppDomain = 10;
  52. // Internal list of all AppDomains
  53. private List<AppDomainStructure> appDomains =
  54. new List<AppDomainStructure>();
  55. private Dictionary<string, AppDomainStructure> AppDomainFiles = new Dictionary<string, AppDomainStructure>();
  56. public readonly string[] AssembliesInAppDomain = new string[] { "OpenSim.ScriptEngine.Shared.Script.dll", "OpenSim.Region.ScriptEngine.Shared.dll" };
  57. internal static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  58. // Structure to keep track of data around AppDomain
  59. private class AppDomainStructure
  60. {
  61. public AppDomain CurrentAppDomain; // The AppDomain itself
  62. public int ScriptsLoaded; // Number of scripts loaded into AppDomain
  63. public int ScriptsWaitingUnload; // Number of dead scripts
  64. }
  65. // Current AppDomain
  66. private AppDomainStructure currentAD;
  67. private object getLock = new object(); // Mutex
  68. private object freeLock = new object(); // Mutex
  69. // Find a free AppDomain, creating one if necessary
  70. private AppDomainStructure GetFreeAppDomain()
  71. {
  72. lock (getLock)
  73. {
  74. // Current full?
  75. if (currentAD != null &&
  76. currentAD.ScriptsLoaded >= maxScriptsPerAppDomain)
  77. {
  78. // Add it to AppDomains list and empty current
  79. appDomains.Add(currentAD);
  80. currentAD = null;
  81. }
  82. // No current
  83. if (currentAD == null)
  84. {
  85. // Create a new current AppDomain
  86. currentAD = new AppDomainStructure();
  87. currentAD.CurrentAppDomain = PrepareNewAppDomain();
  88. }
  89. return currentAD;
  90. }
  91. }
  92. private int AppDomainNameCount;
  93. public ScriptAssemblies.IScript LoadScript(ScriptStructure script)
  94. {
  95. // Find next available AppDomain to put it in
  96. AppDomainStructure FreeAppDomain;
  97. // If we already have loaded file, then reuse that AppDomains
  98. if (AppDomainFiles.ContainsKey(script.AssemblyFileName))
  99. FreeAppDomain = AppDomainFiles[script.AssemblyFileName];
  100. else
  101. FreeAppDomain = GetFreeAppDomain();
  102. // Set script object AppDomain
  103. script.AppDomain = FreeAppDomain.CurrentAppDomain;
  104. // Create instance of script
  105. ScriptAssemblies.IScript mbrt = (ScriptAssemblies.IScript)
  106. FreeAppDomain.CurrentAppDomain.CreateInstanceFromAndUnwrap(
  107. script.AssemblyFileName, "ScriptAssemblies.Script");
  108. //, true, BindingFlags.CreateInstance, null);
  109. FreeAppDomain.ScriptsLoaded++;
  110. return mbrt;
  111. }
  112. // Create and prepare a new AppDomain for scripts
  113. private AppDomain PrepareNewAppDomain()
  114. {
  115. // Create and prepare a new AppDomain
  116. AppDomainNameCount++;
  117. // TODO: Currently security match current appdomain
  118. // Construct and initialize settings for a second AppDomain.
  119. AppDomainSetup ads = new AppDomainSetup();
  120. ads.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
  121. ads.DisallowBindingRedirects = true;
  122. ads.DisallowCodeDownload = true;
  123. ads.LoaderOptimization = LoaderOptimization.MultiDomainHost;
  124. ads.ShadowCopyFiles = "false"; // Disable shadowing
  125. ads.ConfigurationFile =
  126. AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
  127. AppDomain AD = AppDomain.CreateDomain("ScriptAppDomain_" +
  128. AppDomainNameCount, null, ads);
  129. foreach (string file in AssembliesInAppDomain)
  130. {
  131. m_log.InfoFormat("[{0}] AppDomain Loading: \"{1}\"->\"{2}\".", Name, file,
  132. AssemblyName.GetAssemblyName(file).ToString());
  133. AD.Load(AssemblyName.GetAssemblyName(file));
  134. }
  135. // Return the new AppDomain
  136. return AD;
  137. }
  138. // Unload appdomains that are full and have only dead scripts
  139. private void UnloadAppDomains()
  140. {
  141. lock (freeLock)
  142. {
  143. // Go through all
  144. foreach (AppDomainStructure ads in new ArrayList(appDomains))
  145. {
  146. // Don't process current AppDomain
  147. if (ads.CurrentAppDomain != currentAD.CurrentAppDomain)
  148. {
  149. // Not current AppDomain
  150. // Is number of unloaded bigger or equal to number of loaded?
  151. if (ads.ScriptsLoaded <= ads.ScriptsWaitingUnload)
  152. {
  153. // Remove from internal list
  154. appDomains.Remove(ads);
  155. // Unload
  156. AppDomain.Unload(ads.CurrentAppDomain);
  157. }
  158. }
  159. }
  160. }
  161. }
  162. // Increase "dead script" counter for an AppDomain
  163. public void StopScript(AppDomain ad)
  164. {
  165. lock (freeLock)
  166. {
  167. // Check if it is current AppDomain
  168. if (currentAD.CurrentAppDomain == ad)
  169. {
  170. // Yes - increase
  171. currentAD.ScriptsWaitingUnload++;
  172. return;
  173. }
  174. // Lopp through all AppDomains
  175. foreach (AppDomainStructure ads in new ArrayList(appDomains))
  176. {
  177. if (ads.CurrentAppDomain == ad)
  178. {
  179. // Found it
  180. ads.ScriptsWaitingUnload++;
  181. break;
  182. }
  183. }
  184. }
  185. UnloadAppDomains(); // Outsite lock, has its own GetLock
  186. }
  187. }
  188. }