aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/ScriptEngine/Components/DotNetEngine/Scheduler/ScriptLoader.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/ScriptEngine/Components/DotNetEngine/Scheduler/ScriptLoader.cs')
-rw-r--r--OpenSim/ScriptEngine/Components/DotNetEngine/Scheduler/ScriptLoader.cs216
1 files changed, 216 insertions, 0 deletions
diff --git a/OpenSim/ScriptEngine/Components/DotNetEngine/Scheduler/ScriptLoader.cs b/OpenSim/ScriptEngine/Components/DotNetEngine/Scheduler/ScriptLoader.cs
new file mode 100644
index 0000000..cb4a870
--- /dev/null
+++ b/OpenSim/ScriptEngine/Components/DotNetEngine/Scheduler/ScriptLoader.cs
@@ -0,0 +1,216 @@
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 */
27using System;
28using System.Collections;
29using System.Collections.Generic;
30using System.Reflection;
31using System.Text;
32using log4net;
33using OpenSim.ScriptEngine.Shared;
34using IScript=OpenSim.Region.ScriptEngine.Shared.ScriptBase.IScript;
35
36namespace OpenSim.ScriptEngine.Components.DotNetEngine.Scheduler
37{
38 public class ScriptLoader : IScriptLoader
39 {
40 //
41 // This class does AppDomain handling and loading/unloading of
42 // scripts in it. It is instanced in "ScriptEngine" and controlled
43 // from "ScriptManager"
44 //
45 // 1. Create a new AppDomain if old one is full (or doesn't exist)
46 // 2. Load scripts into AppDomain
47 // 3. Unload scripts from AppDomain (stopping them and marking
48 // them as inactive)
49 // 4. Unload AppDomain completely when all scripts in it has stopped
50 //
51
52 public string Name { get { return "SECS.DotNetEngine.Scheduler.ScriptLoader"; } }
53 private int maxScriptsPerAppDomain = 10;
54
55 // Internal list of all AppDomains
56 private List<AppDomainStructure> appDomains =
57 new List<AppDomainStructure>();
58 private Dictionary<string, AppDomainStructure> AppDomainFiles = new Dictionary<string, AppDomainStructure>();
59 public readonly string[] AssembliesInAppDomain = new string[] { "OpenSim.ScriptEngine.Shared.Script.dll", "OpenSim.Region.ScriptEngine.Shared.dll" };
60
61 internal static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
62
63 // Structure to keep track of data around AppDomain
64 private class AppDomainStructure
65 {
66 public AppDomain CurrentAppDomain; // The AppDomain itself
67 public int ScriptsLoaded; // Number of scripts loaded into AppDomain
68 public int ScriptsWaitingUnload; // Number of dead scripts
69 }
70
71 // Current AppDomain
72 private AppDomainStructure currentAD;
73
74 private object getLock = new object(); // Mutex
75 private object freeLock = new object(); // Mutex
76
77 // Find a free AppDomain, creating one if necessary
78 private AppDomainStructure GetFreeAppDomain()
79 {
80 lock (getLock)
81 {
82 // Current full?
83 if (currentAD != null &&
84 currentAD.ScriptsLoaded >= maxScriptsPerAppDomain)
85 {
86 // Add it to AppDomains list and empty current
87 appDomains.Add(currentAD);
88 currentAD = null;
89 }
90 // No current
91 if (currentAD == null)
92 {
93 // Create a new current AppDomain
94 currentAD = new AppDomainStructure();
95 currentAD.CurrentAppDomain = PrepareNewAppDomain();
96 }
97
98 return currentAD;
99 }
100 }
101
102 private int AppDomainNameCount;
103 public ScriptAssemblies.IScript LoadScript(ScriptStructure script)
104 {
105 // Find next available AppDomain to put it in
106 AppDomainStructure FreeAppDomain;
107
108 // If we already have loaded file, then reuse that AppDomains
109 if (AppDomainFiles.ContainsKey(script.AssemblyFileName))
110 FreeAppDomain = AppDomainFiles[script.AssemblyFileName];
111 else
112 FreeAppDomain = GetFreeAppDomain();
113
114 // Set script object AppDomain
115 script.AppDomain = FreeAppDomain.CurrentAppDomain;
116
117 // Create instance of script
118 ScriptAssemblies.IScript mbrt = (ScriptAssemblies.IScript)
119 FreeAppDomain.CurrentAppDomain.CreateInstanceFromAndUnwrap(
120 script.AssemblyFileName, "ScriptAssemblies.Script");
121 //, true, BindingFlags.CreateInstance, null);
122 FreeAppDomain.ScriptsLoaded++;
123
124 return mbrt;
125 }
126
127 // Create and prepare a new AppDomain for scripts
128 private AppDomain PrepareNewAppDomain()
129 {
130 // Create and prepare a new AppDomain
131 AppDomainNameCount++;
132
133 // TODO: Currently security match current appdomain
134
135 // Construct and initialize settings for a second AppDomain.
136 AppDomainSetup ads = new AppDomainSetup();
137 ads.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
138 ads.DisallowBindingRedirects = true;
139 ads.DisallowCodeDownload = true;
140 ads.LoaderOptimization = LoaderOptimization.MultiDomainHost;
141 ads.ShadowCopyFiles = "false"; // Disable shadowing
142 ads.ConfigurationFile =
143 AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
144
145 AppDomain AD = AppDomain.CreateDomain("ScriptAppDomain_" +
146 AppDomainNameCount, null, ads);
147
148 foreach (string file in AssembliesInAppDomain)
149 {
150 m_log.InfoFormat("[{0}] AppDomain Loading: \"{1}\"->\"{2}\".", Name, file,
151 AssemblyName.GetAssemblyName(file).ToString());
152 AD.Load(AssemblyName.GetAssemblyName(file));
153 }
154
155 // Return the new AppDomain
156 return AD;
157 }
158
159 // Unload appdomains that are full and have only dead scripts
160 private void UnloadAppDomains()
161 {
162 lock (freeLock)
163 {
164 // Go through all
165 foreach (AppDomainStructure ads in new ArrayList(appDomains))
166 {
167 // Don't process current AppDomain
168 if (ads.CurrentAppDomain != currentAD.CurrentAppDomain)
169 {
170 // Not current AppDomain
171 // Is number of unloaded bigger or equal to number of loaded?
172 if (ads.ScriptsLoaded <= ads.ScriptsWaitingUnload)
173 {
174 // Remove from internal list
175 appDomains.Remove(ads);
176
177 // Unload
178 AppDomain.Unload(ads.CurrentAppDomain);
179 }
180 }
181 }
182 }
183 }
184
185 // Increase "dead script" counter for an AppDomain
186 public void StopScript(AppDomain ad)
187 {
188 lock (freeLock)
189 {
190 // Check if it is current AppDomain
191 if (currentAD.CurrentAppDomain == ad)
192 {
193 // Yes - increase
194 currentAD.ScriptsWaitingUnload++;
195 return;
196 }
197
198 // Lopp through all AppDomains
199 foreach (AppDomainStructure ads in new ArrayList(appDomains))
200 {
201 if (ads.CurrentAppDomain == ad)
202 {
203 // Found it
204 ads.ScriptsWaitingUnload++;
205 break;
206 }
207 }
208 }
209
210 UnloadAppDomains(); // Outsite lock, has its own GetLock
211 }
212
213
214
215 }
216} \ No newline at end of file