aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ScriptEngine/DotNetEngine
diff options
context:
space:
mode:
authorMelanie Thielker2008-09-26 13:16:11 +0000
committerMelanie Thielker2008-09-26 13:16:11 +0000
commit824283ca3c2ab54868ed61fdb0a329221d69e5fa (patch)
tree9013892fa2afa579bffe8bdcd6a46e2242ed085c /OpenSim/Region/ScriptEngine/DotNetEngine
parent* Wind updates. Still random.. but in 4 directions instead of two! (diff)
downloadopensim-SC_OLD-824283ca3c2ab54868ed61fdb0a329221d69e5fa.zip
opensim-SC_OLD-824283ca3c2ab54868ed61fdb0a329221d69e5fa.tar.gz
opensim-SC_OLD-824283ca3c2ab54868ed61fdb0a329221d69e5fa.tar.bz2
opensim-SC_OLD-824283ca3c2ab54868ed61fdb0a329221d69e5fa.tar.xz
Remove all the subclassing complexity and script server interfaces from
DNE and move all of DNE into the DotNetEngine directory. Remove references that would cause the script runtime to load the entire engine + scene into each script appdomain. This might help DNE memory consumption.
Diffstat (limited to 'OpenSim/Region/ScriptEngine/DotNetEngine')
-rw-r--r--OpenSim/Region/ScriptEngine/DotNetEngine/AppDomainManager.cs254
-rw-r--r--OpenSim/Region/ScriptEngine/DotNetEngine/Common.cs56
-rw-r--r--OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/LSL/Compiler.cs4
-rw-r--r--OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs447
-rw-r--r--OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueThreadClass.cs381
-rw-r--r--OpenSim/Region/ScriptEngine/DotNetEngine/MaintenanceThread.cs243
-rw-r--r--OpenSim/Region/ScriptEngine/DotNetEngine/ScriptEngine.cs268
-rw-r--r--OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs460
8 files changed, 2095 insertions, 18 deletions
diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/AppDomainManager.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/AppDomainManager.cs
new file mode 100644
index 0000000..969a05e
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/DotNetEngine/AppDomainManager.cs
@@ -0,0 +1,254 @@
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
28using System;
29using System.Collections;
30using System.Collections.Generic;
31using System.Reflection;
32using OpenSim.Region.ScriptEngine.Common;
33
34namespace OpenSim.Region.ScriptEngine.DotNetEngine
35{
36 public class AppDomainManager : iScriptEngineFunctionModule
37 {
38 //
39 // This class does AppDomain handling and loading/unloading of scripts in it.
40 // It is instanced in "ScriptEngine" and controlled from "ScriptManager"
41 //
42 // 1. Create a new AppDomain if old one is full (or doesn't exist)
43 // 2. Load scripts into AppDomain
44 // 3. Unload scripts from AppDomain (stopping them and marking them as inactive)
45 // 4. Unload AppDomain completely when all scripts in it has stopped
46 //
47
48 private int maxScriptsPerAppDomain = 1;
49
50 /// <summary>
51 /// Internal list of all AppDomains
52 /// </summary>
53 private List<AppDomainStructure> appDomains = new List<AppDomainStructure>();
54
55 /// <summary>
56 /// Structure to keep track of data around AppDomain
57 /// </summary>
58 private class AppDomainStructure
59 {
60 /// <summary>
61 /// The AppDomain itself
62 /// </summary>
63 public AppDomain CurrentAppDomain;
64
65 /// <summary>
66 /// Number of scripts loaded into AppDomain
67 /// </summary>
68 public int ScriptsLoaded;
69
70 /// <summary>
71 /// Number of dead scripts
72 /// </summary>
73 public int ScriptsWaitingUnload;
74 }
75
76 /// <summary>
77 /// Current AppDomain
78 /// </summary>
79 private AppDomainStructure currentAD;
80
81 private object getLock = new object(); // Mutex
82 private object freeLock = new object(); // Mutex
83
84 private ScriptEngine m_scriptEngine;
85 //public AppDomainManager(ScriptEngine scriptEngine)
86 public AppDomainManager(ScriptEngine scriptEngine)
87 {
88 m_scriptEngine = scriptEngine;
89 ReadConfig();
90 }
91
92 public void ReadConfig()
93 {
94 maxScriptsPerAppDomain = m_scriptEngine.ScriptConfigSource.GetInt("ScriptsPerAppDomain", 1);
95 }
96
97 /// <summary>
98 /// Find a free AppDomain, creating one if necessary
99 /// </summary>
100 /// <returns>Free AppDomain</returns>
101 private AppDomainStructure GetFreeAppDomain()
102 {
103 // Console.WriteLine("Finding free AppDomain");
104 lock (getLock)
105 {
106 // Current full?
107 if (currentAD != null && currentAD.ScriptsLoaded >= maxScriptsPerAppDomain)
108 {
109 // Add it to AppDomains list and empty current
110 appDomains.Add(currentAD);
111 currentAD = null;
112 }
113 // No current
114 if (currentAD == null)
115 {
116 // Create a new current AppDomain
117 currentAD = new AppDomainStructure();
118 currentAD.CurrentAppDomain = PrepareNewAppDomain();
119 }
120
121 // Console.WriteLine("Scripts loaded in this Appdomain: " + currentAD.ScriptsLoaded);
122 return currentAD;
123 }
124 }
125
126 private int AppDomainNameCount;
127
128 /// <summary>
129 /// Create and prepare a new AppDomain for scripts
130 /// </summary>
131 /// <returns>The new AppDomain</returns>
132 private AppDomain PrepareNewAppDomain()
133 {
134 // Create and prepare a new AppDomain
135 AppDomainNameCount++;
136 // TODO: Currently security match current appdomain
137
138 // Construct and initialize settings for a second AppDomain.
139 AppDomainSetup ads = new AppDomainSetup();
140 ads.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
141 ads.DisallowBindingRedirects = true;
142 ads.DisallowCodeDownload = true;
143 ads.LoaderOptimization = LoaderOptimization.MultiDomainHost;
144 ads.ShadowCopyFiles = "false"; // Disable shadowing
145 ads.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
146
147
148 AppDomain AD = AppDomain.CreateDomain("ScriptAppDomain_" + AppDomainNameCount, null, ads);
149 m_scriptEngine.Log.Info("[" + m_scriptEngine.ScriptEngineName + "]: AppDomain Loading: " +
150 AssemblyName.GetAssemblyName("OpenSim.Region.ScriptEngine.Common.dll").ToString());
151 AD.Load(AssemblyName.GetAssemblyName("OpenSim.Region.ScriptEngine.Common.dll"));
152
153 // Return the new AppDomain
154 return AD;
155 }
156
157 /// <summary>
158 /// Unload appdomains that are full and have only dead scripts
159 /// </summary>
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//#if DEBUG
177 //Console.WriteLine("Found empty AppDomain, unloading");
178 //long m = GC.GetTotalMemory(true); // This force a garbage collect that freezes some windows plateforms
179//#endif
180 // Unload
181 AppDomain.Unload(ads.CurrentAppDomain);
182//#if DEBUG
183 //m_scriptEngine.Log.Info("[" + m_scriptEngine.ScriptEngineName + "]: AppDomain unload freed " + (m - GC.GetTotalMemory(true)) + " bytes of memory");
184//#endif
185 }
186 }
187 }
188 }
189 }
190
191 public IScript LoadScript(string FileName)
192 {
193 // Find next available AppDomain to put it in
194 AppDomainStructure FreeAppDomain = GetFreeAppDomain();
195
196#if DEBUG
197 m_scriptEngine.Log.Info("[" + m_scriptEngine.ScriptEngineName + "]: Loading into AppDomain: " + FileName);
198#endif
199 IScript mbrt =
200 (IScript)
201 FreeAppDomain.CurrentAppDomain.CreateInstanceFromAndUnwrap(FileName, "SecondLife.Script");
202 //Console.WriteLine("ScriptEngine AppDomainManager: is proxy={0}", RemotingServices.IsTransparentProxy(mbrt));
203 FreeAppDomain.ScriptsLoaded++;
204
205 return mbrt;
206 }
207
208
209 /// <summary>
210 /// Increase "dead script" counter for an AppDomain
211 /// </summary>
212 /// <param name="ad"></param>
213 //[Obsolete("Needs fixing, needs a real purpose in life!!!")]
214 public void StopScript(AppDomain ad)
215 {
216 lock (freeLock)
217 {
218#if DEBUG
219 m_scriptEngine.Log.Info("[" + m_scriptEngine.ScriptEngineName + "]: Stopping script in AppDomain");
220#endif
221 // Check if it is current AppDomain
222 if (currentAD.CurrentAppDomain == ad)
223 {
224 // Yes - increase
225 currentAD.ScriptsWaitingUnload++;
226 return;
227 }
228
229 // Lopp through all AppDomains
230 foreach (AppDomainStructure ads in new ArrayList(appDomains))
231 {
232 if (ads.CurrentAppDomain == ad)
233 {
234 // Found it
235 ads.ScriptsWaitingUnload++;
236 break;
237 }
238 }
239 }
240
241 UnloadAppDomains(); // Outsite lock, has its own GetLock
242 }
243
244 /// <summary>
245 /// If set to true then threads and stuff should try to make a graceful exit
246 /// </summary>
247 public bool PleaseShutdown
248 {
249 get { return _PleaseShutdown; }
250 set { _PleaseShutdown = value; }
251 }
252 private bool _PleaseShutdown = false;
253 }
254}
diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/Common.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/Common.cs
new file mode 100644
index 0000000..3d9e19b
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/DotNetEngine/Common.cs
@@ -0,0 +1,56 @@
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
28namespace OpenSim.Region.ScriptEngine.DotNetEngine
29{
30 public static class Common
31 {
32 public static bool debug = true;
33 public static ScriptEngine mySE;
34
35 // This class just contains some static log stuff used for debugging.
36
37 //public delegate void SendToDebugEventDelegate(string message);
38 //public delegate void SendToLogEventDelegate(string message);
39 //static public event SendToDebugEventDelegate SendToDebugEvent;
40 //static public event SendToLogEventDelegate SendToLogEvent;
41
42 public static void SendToDebug(string message)
43 {
44 //if (Debug == true)
45 mySE.Log.Info("[" + mySE.ScriptEngineName + "]: Debug: " + message);
46 //SendToDebugEvent("\r\n" + DateTime.Now.ToString("[HH:mm:ss] ") + message);
47 }
48
49 public static void SendToLog(string message)
50 {
51 //if (Debug == true)
52 mySE.Log.Info("[" + mySE.ScriptEngineName + "]: LOG: " + message);
53 //SendToLogEvent("\r\n" + DateTime.Now.ToString("[HH:mm:ss] ") + message);
54 }
55 }
56}
diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/LSL/Compiler.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/LSL/Compiler.cs
index b50e823..4cb74fa 100644
--- a/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/LSL/Compiler.cs
+++ b/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/LSL/Compiler.cs
@@ -83,8 +83,8 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL
83 private static int instanceID = new Random().Next(0, int.MaxValue); // Unique number to use on our compiled files 83 private static int instanceID = new Random().Next(0, int.MaxValue); // Unique number to use on our compiled files
84 private static UInt64 scriptCompileCounter = 0; // And a counter 84 private static UInt64 scriptCompileCounter = 0; // And a counter
85 85
86 public Common.ScriptEngineBase.ScriptEngine m_scriptEngine; 86 public ScriptEngine m_scriptEngine;
87 public Compiler(Common.ScriptEngineBase.ScriptEngine scriptEngine) 87 public Compiler(ScriptEngine scriptEngine)
88 { 88 {
89 m_scriptEngine = scriptEngine; 89 m_scriptEngine = scriptEngine;
90 ReadConfig(); 90 ReadConfig();
diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs
new file mode 100644
index 0000000..7805d67
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs
@@ -0,0 +1,447 @@
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
28using System;
29using System.Collections;
30using System.Collections.Generic;
31using OpenMetaverse;
32using OpenSim.Region.ScriptEngine.Shared;
33
34namespace OpenSim.Region.ScriptEngine.DotNetEngine
35{
36 /// <summary>
37 /// EventQueueManager handles event queues
38 /// Events are queued and executed in separate thread
39 /// </summary>
40 [Serializable]
41 public class EventQueueManager : iScriptEngineFunctionModule
42 {
43 //
44 // Class is instanced in "ScriptEngine" and used by "EventManager" which is also instanced in "ScriptEngine".
45 //
46 // Class purpose is to queue and execute functions that are received by "EventManager":
47 // - allowing "EventManager" to release its event thread immediately, thus not interrupting server execution.
48 // - allowing us to prioritize and control execution of script functions.
49 // Class can use multiple threads for simultaneous execution. Mutexes are used for thread safety.
50 //
51 // 1. Hold an execution queue for scripts
52 // 2. Use threads to process queue, each thread executes one script function on each pass.
53 // 3. Catch any script error and process it
54 //
55 //
56 // Notes:
57 // * Current execution load balancing is optimized for 1 thread, and can cause unfair execute balancing between scripts.
58 // Not noticeable unless server is under high load.
59 //
60
61 public ScriptEngine m_ScriptEngine;
62
63 /// <summary>
64 /// List of threads (classes) processing event queue
65 /// Note that this may or may not be a reference to a static object depending on PrivateRegionThreads config setting.
66 /// </summary>
67 internal static List<EventQueueThreadClass> eventQueueThreads = new List<EventQueueThreadClass>(); // Thread pool that we work on
68 /// <summary>
69 /// Locking access to eventQueueThreads AND staticGlobalEventQueueThreads.
70 /// </summary>
71// private object eventQueueThreadsLock = new object();
72 // Static objects for referencing the objects above if we don't have private threads:
73 //internal static List<EventQueueThreadClass> staticEventQueueThreads; // A static reference used if we don't use private threads
74// internal static object staticEventQueueThreadsLock; // Statick lock object reference for same reason
75
76 /// <summary>
77 /// Global static list of all threads (classes) processing event queue -- used by max enforcment thread
78 /// </summary>
79 //private List<EventQueueThreadClass> staticGlobalEventQueueThreads = new List<EventQueueThreadClass>();
80
81 /// <summary>
82 /// Used internally to specify how many threads should exit gracefully
83 /// </summary>
84 public static int ThreadsToExit;
85 public static object ThreadsToExitLock = new object();
86
87
88 //public object queueLock = new object(); // Mutex lock object
89
90 /// <summary>
91 /// How many threads to process queue with
92 /// </summary>
93 internal static int numberOfThreads;
94
95 internal static int EventExecutionMaxQueueSize;
96
97 /// <summary>
98 /// Maximum time one function can use for execution before we perform a thread kill.
99 /// </summary>
100 private static int maxFunctionExecutionTimems
101 {
102 get { return (int)(maxFunctionExecutionTimens / 10000); }
103 set { maxFunctionExecutionTimens = value * 10000; }
104 }
105
106 /// <summary>
107 /// Contains nanoseconds version of maxFunctionExecutionTimems so that it matches time calculations better (performance reasons).
108 /// WARNING! ONLY UPDATE maxFunctionExecutionTimems, NEVER THIS DIRECTLY.
109 /// </summary>
110 public static long maxFunctionExecutionTimens;
111 /// <summary>
112 /// Enforce max execution time
113 /// </summary>
114 public static bool EnforceMaxExecutionTime;
115 /// <summary>
116 /// Kill script (unload) when it exceeds execution time
117 /// </summary>
118 private static bool KillScriptOnMaxFunctionExecutionTime;
119
120 /// <summary>
121 /// List of localID locks for mutex processing of script events
122 /// </summary>
123 private List<uint> objectLocks = new List<uint>();
124 private object tryLockLock = new object(); // Mutex lock object
125
126 /// <summary>
127 /// Queue containing events waiting to be executed
128 /// </summary>
129 public Queue<QueueItemStruct> eventQueue = new Queue<QueueItemStruct>();
130
131 #region " Queue structures "
132 /// <summary>
133 /// Queue item structure
134 /// </summary>
135 public struct QueueItemStruct
136 {
137 public uint localID;
138 public UUID itemID;
139 public string functionName;
140 public DetectParams[] llDetectParams;
141 public object[] param;
142 }
143
144 #endregion
145
146 #region " Initialization / Startup "
147 public EventQueueManager(ScriptEngine _ScriptEngine)
148 {
149 m_ScriptEngine = _ScriptEngine;
150
151 ReadConfig();
152 AdjustNumberOfScriptThreads();
153 }
154
155 public void ReadConfig()
156 {
157 // Refresh config
158 numberOfThreads = m_ScriptEngine.ScriptConfigSource.GetInt("NumberOfScriptThreads", 2);
159 maxFunctionExecutionTimems = m_ScriptEngine.ScriptConfigSource.GetInt("MaxEventExecutionTimeMs", 5000);
160 EnforceMaxExecutionTime = m_ScriptEngine.ScriptConfigSource.GetBoolean("EnforceMaxEventExecutionTime", false);
161 KillScriptOnMaxFunctionExecutionTime = m_ScriptEngine.ScriptConfigSource.GetBoolean("DeactivateScriptOnTimeout", false);
162 EventExecutionMaxQueueSize = m_ScriptEngine.ScriptConfigSource.GetInt("EventExecutionMaxQueueSize", 300);
163
164 // Now refresh config in all threads
165 lock (eventQueueThreads)
166 {
167 foreach (EventQueueThreadClass EventQueueThread in eventQueueThreads)
168 {
169 EventQueueThread.ReadConfig();
170 }
171 }
172 }
173
174 #endregion
175
176 #region " Shutdown all threads "
177 ~EventQueueManager()
178 {
179 Stop();
180 }
181
182 private void Stop()
183 {
184 if (eventQueueThreads != null)
185 {
186 // Kill worker threads
187 lock (eventQueueThreads)
188 {
189 foreach (EventQueueThreadClass EventQueueThread in new ArrayList(eventQueueThreads))
190 {
191 AbortThreadClass(EventQueueThread);
192 }
193 //eventQueueThreads.Clear();
194 //staticGlobalEventQueueThreads.Clear();
195 }
196 }
197
198 // Remove all entries from our event queue
199 lock (eventQueue)
200 {
201 eventQueue.Clear();
202 }
203 }
204
205 #endregion
206
207 #region " Start / stop script execution threads (ThreadClasses) "
208 private void StartNewThreadClass()
209 {
210 EventQueueThreadClass eqtc = new EventQueueThreadClass();
211 eventQueueThreads.Add(eqtc);
212 //m_ScriptEngine.Log.Debug("[" + m_ScriptEngine.ScriptEngineName + "]: Started new script execution thread. Current thread count: " + eventQueueThreads.Count);
213 }
214
215 private void AbortThreadClass(EventQueueThreadClass threadClass)
216 {
217 if (eventQueueThreads.Contains(threadClass))
218 eventQueueThreads.Remove(threadClass);
219
220 try
221 {
222 threadClass.Stop();
223 }
224 catch (Exception)
225 {
226 //m_ScriptEngine.Log.Error("[" + m_ScriptEngine.ScriptEngineName + ":EventQueueManager]: If you see this, could you please report it to Tedd:");
227 //m_ScriptEngine.Log.Error("[" + m_ScriptEngine.ScriptEngineName + ":EventQueueManager]: Script thread execution timeout kill ended in exception: " + ex.ToString());
228 }
229 //m_ScriptEngine.Log.Debug("[" + m_ScriptEngine.ScriptEngineName + "]: Killed script execution thread. Remaining thread count: " + eventQueueThreads.Count);
230 }
231 #endregion
232
233 #region " Mutex locks for queue access "
234 /// <summary>
235 /// Try to get a mutex lock on localID
236 /// </summary>
237 /// <param name="localID"></param>
238 /// <returns></returns>
239 public bool TryLock(uint localID)
240 {
241 lock (tryLockLock)
242 {
243 if (objectLocks.Contains(localID) == true)
244 {
245 return false;
246 }
247 else
248 {
249 objectLocks.Add(localID);
250 return true;
251 }
252 }
253 }
254
255 /// <summary>
256 /// Release mutex lock on localID
257 /// </summary>
258 /// <param name="localID"></param>
259 public void ReleaseLock(uint localID)
260 {
261 lock (tryLockLock)
262 {
263 if (objectLocks.Contains(localID) == true)
264 {
265 objectLocks.Remove(localID);
266 }
267 }
268 }
269 #endregion
270
271 #region " Check execution queue for a specified Event"
272 /// <summary>
273 /// checks to see if a specified event type is already in the queue
274 /// </summary>
275 /// <param name="localID">Region object ID</param>
276 /// <param name="FunctionName">Name of the function, will be state + "_event_" + FunctionName</param>
277 /// <returns>true if event is found , false if not found</returns>
278 ///
279 public bool CheckEeventQueueForEvent(uint localID, string FunctionName)
280 {
281 if (eventQueue.Count > 0)
282 {
283 lock (eventQueue)
284 {
285 foreach (EventQueueManager.QueueItemStruct QIS in eventQueue)
286 {
287 if ((QIS.functionName == FunctionName) && (QIS.localID == localID))
288 return true;
289 }
290 }
291 }
292 return false;
293 }
294 #endregion
295
296 #region " Add events to execution queue "
297 /// <summary>
298 /// Add event to event execution queue
299 /// </summary>
300 /// <param name="localID">Region object ID</param>
301 /// <param name="FunctionName">Name of the function, will be state + "_event_" + FunctionName</param>
302 /// <param name="param">Array of parameters to match event mask</param>
303 public bool AddToObjectQueue(uint localID, string FunctionName, DetectParams[] qParams, params object[] param)
304 {
305 // Determine all scripts in Object and add to their queue
306 //myScriptEngine.log.Info("[" + ScriptEngineName + "]: EventQueueManager Adding localID: " + localID + ", FunctionName: " + FunctionName);
307
308 // Do we have any scripts in this object at all? If not, return
309 if (m_ScriptEngine.m_ScriptManager.Scripts.ContainsKey(localID) == false)
310 {
311 //Console.WriteLine("Event \String.Empty + FunctionName + "\" for localID: " + localID + ". No scripts found on this localID.");
312 return false;
313 }
314
315 List<UUID> scriptKeys =
316 m_ScriptEngine.m_ScriptManager.GetScriptKeys(localID);
317
318 foreach (UUID itemID in scriptKeys)
319 {
320 // Add to each script in that object
321 // TODO: Some scripts may not subscribe to this event. Should we NOT add it? Does it matter?
322 AddToScriptQueue(localID, itemID, FunctionName, qParams, param);
323 }
324 return true;
325 }
326
327 /// <summary>
328 /// Add event to event execution queue
329 /// </summary>
330 /// <param name="localID">Region object ID</param>
331 /// <param name="itemID">Region script ID</param>
332 /// <param name="FunctionName">Name of the function, will be state + "_event_" + FunctionName</param>
333 /// <param name="param">Array of parameters to match event mask</param>
334 public bool AddToScriptQueue(uint localID, UUID itemID, string FunctionName, DetectParams[] qParams, params object[] param)
335 {
336 List<UUID> keylist = m_ScriptEngine.m_ScriptManager.GetScriptKeys(localID);
337
338 if (!keylist.Contains(itemID)) // We don't manage that script
339 {
340 return false;
341 }
342
343 lock (eventQueue)
344 {
345 if (eventQueue.Count >= EventExecutionMaxQueueSize)
346 {
347 m_ScriptEngine.Log.Error("[" + m_ScriptEngine.ScriptEngineName + "]: ERROR: Event execution queue item count is at " + eventQueue.Count + ". Config variable \"EventExecutionMaxQueueSize\" is set to " + EventExecutionMaxQueueSize + ", so ignoring new event.");
348 m_ScriptEngine.Log.Error("[" + m_ScriptEngine.ScriptEngineName + "]: Event ignored: localID: " + localID + ", itemID: " + itemID + ", FunctionName: " + FunctionName);
349 return false;
350 }
351
352 // Create a structure and add data
353 QueueItemStruct QIS = new QueueItemStruct();
354 QIS.localID = localID;
355 QIS.itemID = itemID;
356 QIS.functionName = FunctionName;
357 QIS.llDetectParams = qParams;
358 QIS.param = param;
359
360 // Add it to queue
361 eventQueue.Enqueue(QIS);
362 }
363 return true;
364 }
365 #endregion
366
367 #region " Maintenance thread "
368
369 /// <summary>
370 /// Adjust number of script thread classes. It can start new, but if it needs to stop it will just set number of threads in "ThreadsToExit" and threads will have to exit themselves.
371 /// Called from MaintenanceThread
372 /// </summary>
373 public void AdjustNumberOfScriptThreads()
374 {
375 // Is there anything here for us to do?
376 if (eventQueueThreads.Count == numberOfThreads)
377 return;
378
379 lock (eventQueueThreads)
380 {
381 int diff = numberOfThreads - eventQueueThreads.Count;
382 // Positive number: Start
383 // Negative number: too many are running
384 if (diff > 0)
385 {
386 // We need to add more threads
387 for (int ThreadCount = eventQueueThreads.Count; ThreadCount < numberOfThreads; ThreadCount++)
388 {
389 StartNewThreadClass();
390 }
391 }
392 if (diff < 0)
393 {
394 // We need to kill some threads
395 lock (ThreadsToExitLock)
396 {
397 ThreadsToExit = Math.Abs(diff);
398 }
399 }
400 }
401 }
402
403 /// <summary>
404 /// Check if any thread class has been executing an event too long
405 /// </summary>
406 public void CheckScriptMaxExecTime()
407 {
408 // Iterate through all ScriptThreadClasses and check how long their current function has been executing
409 lock (eventQueueThreads)
410 {
411 foreach (EventQueueThreadClass EventQueueThread in eventQueueThreads)
412 {
413 // Is thread currently executing anything?
414 if (EventQueueThread.InExecution)
415 {
416 // Has execution time expired?
417 if (DateTime.Now.Ticks - EventQueueThread.LastExecutionStarted >
418 maxFunctionExecutionTimens)
419 {
420 // Yes! We need to kill this thread!
421
422 // Set flag if script should be removed or not
423 EventQueueThread.KillCurrentScript = KillScriptOnMaxFunctionExecutionTime;
424
425 // Abort this thread
426 AbortThreadClass(EventQueueThread);
427
428 // We do not need to start another, MaintenenceThread will do that for us
429 //StartNewThreadClass();
430 }
431 }
432 }
433 }
434 }
435 #endregion
436
437 ///// <summary>
438 ///// If set to true then threads and stuff should try to make a graceful exit
439 ///// </summary>
440 //public bool PleaseShutdown
441 //{
442 // get { return _PleaseShutdown; }
443 // set { _PleaseShutdown = value; }
444 //}
445 //private bool _PleaseShutdown = false;
446 }
447}
diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueThreadClass.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueThreadClass.cs
new file mode 100644
index 0000000..db3f89f
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueThreadClass.cs
@@ -0,0 +1,381 @@
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
28using System;
29using System.Collections;
30using System.Reflection;
31using System.Text.RegularExpressions;
32using System.Threading;
33using System.Globalization;
34using OpenMetaverse;
35using log4net;
36using OpenSim.Framework;
37using OpenSim.Region.Environment.Scenes.Scripting;
38
39namespace OpenSim.Region.ScriptEngine.DotNetEngine
40{
41 /// <summary>
42 /// Because every thread needs some data set for it (time started to execute current function), it will do its work within a class
43 /// </summary>
44 public class EventQueueThreadClass : iScriptEngineFunctionModule
45 {
46 // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
47
48 /// <summary>
49 /// How many ms to sleep if queue is empty
50 /// </summary>
51 private static int nothingToDoSleepms;// = 50;
52 private static ThreadPriority MyThreadPriority;
53
54 public long LastExecutionStarted;
55 public bool InExecution = false;
56 public bool KillCurrentScript = false;
57
58 //private EventQueueManager eventQueueManager;
59 public Thread EventQueueThread;
60 private static int ThreadCount = 0;
61
62 private string ScriptEngineName = "ScriptEngine.Common";
63
64 public EventQueueThreadClass()//EventQueueManager eqm
65 {
66 //eventQueueManager = eqm;
67 ReadConfig();
68 Start();
69 }
70
71 ~EventQueueThreadClass()
72 {
73 Stop();
74 }
75
76 public void ReadConfig()
77 {
78 lock (ScriptEngine.ScriptEngines)
79 {
80 foreach (ScriptEngine m_ScriptEngine in ScriptEngine.ScriptEngines)
81 {
82 ScriptEngineName = m_ScriptEngine.ScriptEngineName;
83 nothingToDoSleepms = m_ScriptEngine.ScriptConfigSource.GetInt("SleepTimeIfNoScriptExecutionMs", 50);
84
85 // Later with ScriptServer we might want to ask OS for stuff too, so doing this a bit manually
86 string pri = m_ScriptEngine.ScriptConfigSource.GetString("ScriptThreadPriority", "BelowNormal");
87 switch (pri.ToLower())
88 {
89 case "lowest":
90 MyThreadPriority = ThreadPriority.Lowest;
91 break;
92 case "belownormal":
93 MyThreadPriority = ThreadPriority.BelowNormal;
94 break;
95 case "normal":
96 MyThreadPriority = ThreadPriority.Normal;
97 break;
98 case "abovenormal":
99 MyThreadPriority = ThreadPriority.AboveNormal;
100 break;
101 case "highest":
102 MyThreadPriority = ThreadPriority.Highest;
103 break;
104 default:
105 MyThreadPriority = ThreadPriority.BelowNormal; // Default
106 m_ScriptEngine.Log.Error("[ScriptEngine.DotNetEngine]: Unknown priority type \"" + pri +
107 "\" in config file. Defaulting to \"BelowNormal\".");
108 break;
109 }
110 }
111 }
112 // Now set that priority
113 if (EventQueueThread != null)
114 if (EventQueueThread.IsAlive)
115 EventQueueThread.Priority = MyThreadPriority;
116 }
117
118 /// <summary>
119 /// Start thread
120 /// </summary>
121 private void Start()
122 {
123 EventQueueThread = new Thread(EventQueueThreadLoop);
124 EventQueueThread.IsBackground = true;
125
126 EventQueueThread.Priority = MyThreadPriority;
127 EventQueueThread.Name = "EventQueueManagerThread_" + ThreadCount;
128 EventQueueThread.Start();
129 ThreadTracker.Add(EventQueueThread);
130
131 // Look at this... Don't you wish everyone did that solid coding everywhere? :P
132 if (ThreadCount == int.MaxValue)
133 ThreadCount = 0;
134 ThreadCount++;
135 }
136
137 public void Stop()
138 {
139 //PleaseShutdown = true; // Set shutdown flag
140 //Thread.Sleep(100); // Wait a bit
141 if (EventQueueThread != null && EventQueueThread.IsAlive == true)
142 {
143 try
144 {
145 EventQueueThread.Abort(); // Send abort
146 //EventQueueThread.Join(); // Wait for it
147 }
148 catch (Exception)
149 {
150 //myScriptEngine.Log.Info("[" + ScriptEngineName + "]: EventQueueManager Exception killing worker thread: " + e.ToString());
151 }
152 }
153 }
154
155 private EventQueueManager.QueueItemStruct BlankQIS = new EventQueueManager.QueueItemStruct();
156 private ScriptEngine lastScriptEngine;
157 /// <summary>
158 /// Queue processing thread loop
159 /// </summary>
160 private void EventQueueThreadLoop()
161 {
162 CultureInfo USCulture = new CultureInfo("en-US");
163 Thread.CurrentThread.CurrentCulture = USCulture;
164
165 //myScriptEngine.Log.Info("[" + ScriptEngineName + "]: EventQueueManager Worker thread spawned");
166 try
167 {
168 while (true)
169 {
170 try
171 {
172 while (true)
173 {
174 DoProcessQueue();
175 }
176 }
177 catch (ThreadAbortException)
178 {
179 if (lastScriptEngine != null)
180 lastScriptEngine.Log.Info("[" + ScriptEngineName + "]: ThreadAbortException while executing function.");
181 }
182 catch (Exception e)
183 {
184 if (lastScriptEngine != null)
185 lastScriptEngine.Log.Error("[" + ScriptEngineName + "]: Exception in EventQueueThreadLoop: " + e.ToString());
186 }
187 }
188 }
189 catch (ThreadAbortException)
190 {
191 //myScriptEngine.Log.Info("[" + ScriptEngineName + "]: EventQueueManager Worker thread killed: " + tae.Message);
192 }
193 }
194
195 public void DoProcessQueue()
196 {
197 //lock (ScriptEngine.ScriptEngines)
198 //{
199 foreach (ScriptEngine m_ScriptEngine in new ArrayList(ScriptEngine.ScriptEngines))
200 {
201 lastScriptEngine = m_ScriptEngine;
202 // Every now and then check if we should shut down
203 //if (PleaseShutdown || EventQueueManager.ThreadsToExit > 0)
204 //{
205 // // Someone should shut down, lets get exclusive lock
206 // lock (EventQueueManager.ThreadsToExitLock)
207 // {
208 // // Lets re-check in case someone grabbed it
209 // if (EventQueueManager.ThreadsToExit > 0)
210 // {
211 // // Its crowded here so we'll shut down
212 // EventQueueManager.ThreadsToExit--;
213 // Stop();
214 // return;
215 // }
216 // else
217 // {
218 // // We have been asked to shut down
219 // Stop();
220 // return;
221 // }
222 // }
223 //}
224
225 //try
226 // {
227 EventQueueManager.QueueItemStruct QIS = BlankQIS;
228 bool GotItem = false;
229
230 //if (PleaseShutdown)
231 // return;
232
233 if (m_ScriptEngine.m_EventQueueManager == null || m_ScriptEngine.m_EventQueueManager.eventQueue == null)
234 continue;
235
236 if (m_ScriptEngine.m_EventQueueManager.eventQueue.Count == 0)
237 {
238 // Nothing to do? Sleep a bit waiting for something to do
239 Thread.Sleep(nothingToDoSleepms);
240 }
241 else
242 {
243 // Something in queue, process
244 //myScriptEngine.Log.Info("[" + ScriptEngineName + "]: Processing event for localID: " + QIS.localID + ", itemID: " + QIS.itemID + ", FunctionName: " + QIS.FunctionName);
245
246 // OBJECT BASED LOCK - TWO THREADS WORKING ON SAME OBJECT IS NOT GOOD
247 lock (m_ScriptEngine.m_EventQueueManager.eventQueue)
248 {
249 GotItem = false;
250 for (int qc = 0; qc < m_ScriptEngine.m_EventQueueManager.eventQueue.Count; qc++)
251 {
252 // Get queue item
253 QIS = m_ScriptEngine.m_EventQueueManager.eventQueue.Dequeue();
254
255 // Check if object is being processed by someone else
256 if (m_ScriptEngine.m_EventQueueManager.TryLock(QIS.localID) == false)
257 {
258 // Object is already being processed, requeue it
259 m_ScriptEngine.m_EventQueueManager.eventQueue.Enqueue(QIS);
260 }
261 else
262 {
263 // We have lock on an object and can process it
264 GotItem = true;
265 break;
266 }
267 }
268 }
269
270 if (GotItem == true)
271 {
272 // Execute function
273 try
274 {
275 ///cfk 2-7-08 dont need this right now and the default Linux build has DEBUG defined
276#if DEBUG
277 //eventQueueManager.m_ScriptEngine.Log.Debug("[" + ScriptEngineName + "]: " +
278 // "Executing event:\r\n"
279 // + "QIS.localID: " + QIS.localID
280 // + ", QIS.itemID: " + QIS.itemID
281 // + ", QIS.functionName: " +
282 // QIS.functionName);
283#endif
284 // Only pipe event if land supports it.
285 if (m_ScriptEngine.World.PipeEventsForScript(QIS.localID))
286 {
287 LastExecutionStarted = DateTime.Now.Ticks;
288 KillCurrentScript = false;
289 InExecution = true;
290 m_ScriptEngine.m_ScriptManager.ExecuteEvent(QIS.localID,
291 QIS.itemID,
292 QIS.functionName,
293 QIS.llDetectParams,
294 QIS.param);
295 InExecution = false;
296 }
297 }
298 catch (Exception e)
299 {
300 InExecution = false;
301 // DISPLAY ERROR INWORLD
302 string text = "Error executing script function \"" + QIS.functionName +
303 "\":\r\n";
304 if (e.InnerException != null)
305 {
306 // Send inner exception
307 string line = " (unknown line)";
308 Regex rx = new Regex(@"SecondLife\.Script\..+[\s:](?<line>\d+)\.?\r?$", RegexOptions.Compiled);
309 if (rx.Match(e.InnerException.ToString()).Success)
310 line = " (line " + rx.Match(e.InnerException.ToString()).Result("${line}") + ")";
311 text += e.InnerException.Message.ToString() + line;
312 }
313 else
314 {
315 text += "\r\n";
316 // Send normal
317 text += e.Message.ToString();
318 }
319 if (KillCurrentScript)
320 text += "\r\nScript will be deactivated!";
321
322 try
323 {
324 if (text.Length > 1500)
325 text = text.Substring(0, 1500);
326 IScriptHost m_host =
327 m_ScriptEngine.World.GetSceneObjectPart(QIS.localID);
328 //if (m_host != null)
329 //{
330 m_ScriptEngine.World.SimChat(Utils.StringToBytes(text),
331 ChatTypeEnum.DebugChannel, 2147483647,
332 m_host.AbsolutePosition,
333 m_host.Name, m_host.UUID, false);
334 }
335 catch (Exception)
336 {
337 //}
338 //else
339 //{
340 // T oconsole
341 m_ScriptEngine.m_EventQueueManager.m_ScriptEngine.Log.Error("[" + ScriptEngineName +
342 "]: " +
343 "Unable to send text in-world:\r\n" +
344 text);
345 }
346 finally
347 {
348 // So we are done sending message in-world
349 if (KillCurrentScript)
350 {
351 m_ScriptEngine.m_EventQueueManager.m_ScriptEngine.m_ScriptManager.StopScript(
352 QIS.localID, QIS.itemID);
353 }
354 }
355
356 // Pass it on so it's displayed on the console
357 // and in the logs (mikem 2008.06.02).
358 throw e.InnerException;
359 }
360 finally
361 {
362 InExecution = false;
363 m_ScriptEngine.m_EventQueueManager.ReleaseLock(QIS.localID);
364 }
365 }
366 }
367 }
368 // }
369 }
370
371 ///// <summary>
372 ///// If set to true then threads and stuff should try to make a graceful exit
373 ///// </summary>
374 //public bool PleaseShutdown
375 //{
376 // get { return _PleaseShutdown; }
377 // set { _PleaseShutdown = value; }
378 //}
379 //private bool _PleaseShutdown = false;
380 }
381}
diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/MaintenanceThread.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/MaintenanceThread.cs
new file mode 100644
index 0000000..6c1528f
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/DotNetEngine/MaintenanceThread.cs
@@ -0,0 +1,243 @@
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
28using System;
29using System.Collections;
30using System.Reflection;
31using System.Threading;
32using log4net;
33using OpenSim.Framework;
34
35namespace OpenSim.Region.ScriptEngine.DotNetEngine
36{
37 /// <summary>
38 /// This class does maintenance on script engine.
39 /// </summary>
40 public class MaintenanceThread : iScriptEngineFunctionModule
41 {
42
43 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
44
45 //public ScriptEngine m_ScriptEngine;
46 private int MaintenanceLoopms;
47 private int MaintenanceLoopTicks_ScriptLoadUnload;
48 private int MaintenanceLoopTicks_Other;
49
50
51 public MaintenanceThread()
52 {
53 //m_ScriptEngine = _ScriptEngine;
54
55 ReadConfig();
56
57 // Start maintenance thread
58 StartMaintenanceThread();
59 }
60
61 ~MaintenanceThread()
62 {
63 StopMaintenanceThread();
64 }
65
66 public void ReadConfig()
67 {
68 // Bad hack, but we need a m_ScriptEngine :)
69 lock (ScriptEngine.ScriptEngines)
70 {
71 foreach (ScriptEngine m_ScriptEngine in ScriptEngine.ScriptEngines)
72 {
73 MaintenanceLoopms = m_ScriptEngine.ScriptConfigSource.GetInt("MaintenanceLoopms", 50);
74 MaintenanceLoopTicks_ScriptLoadUnload =
75 m_ScriptEngine.ScriptConfigSource.GetInt("MaintenanceLoopTicks_ScriptLoadUnload", 1);
76 MaintenanceLoopTicks_Other =
77 m_ScriptEngine.ScriptConfigSource.GetInt("MaintenanceLoopTicks_Other", 10);
78
79 return;
80 }
81 }
82 }
83
84 #region " Maintenance thread "
85 /// <summary>
86 /// Maintenance thread. Enforcing max execution time for example.
87 /// </summary>
88 public Thread MaintenanceThreadThread;
89
90 /// <summary>
91 /// Starts maintenance thread
92 /// </summary>
93 private void StartMaintenanceThread()
94 {
95 if (MaintenanceThreadThread == null)
96 {
97 MaintenanceThreadThread = new Thread(MaintenanceLoop);
98 MaintenanceThreadThread.Name = "ScriptMaintenanceThread";
99 MaintenanceThreadThread.IsBackground = true;
100 MaintenanceThreadThread.Start();
101 ThreadTracker.Add(MaintenanceThreadThread);
102 }
103 }
104
105 /// <summary>
106 /// Stops maintenance thread
107 /// </summary>
108 private void StopMaintenanceThread()
109 {
110#if DEBUG
111 //m_ScriptEngine.Log.Debug("[" + m_ScriptEngine.ScriptEngineName + "]: StopMaintenanceThread() called");
112#endif
113 //PleaseShutdown = true;
114 Thread.Sleep(100);
115 try
116 {
117 if (MaintenanceThreadThread != null && MaintenanceThreadThread.IsAlive)
118 {
119 MaintenanceThreadThread.Abort();
120 }
121 }
122 catch (Exception)
123 {
124 //m_ScriptEngine.Log.Error("[" + m_ScriptEngine.ScriptEngineName + "]: Exception stopping maintenence thread: " + ex.ToString());
125 }
126 }
127
128 // private ScriptEngine lastScriptEngine; // Keep track of what ScriptEngine instance we are at so we can give exception
129 /// <summary>
130 /// A thread should run in this loop and check all running scripts
131 /// </summary>
132 public void MaintenanceLoop()
133 {
134 //if (m_ScriptEngine.m_EventQueueManager.maxFunctionExecutionTimens < MaintenanceLoopms)
135 // m_ScriptEngine.Log.Warn("[" + m_ScriptEngine.ScriptEngineName + "]: " +
136 // "Configuration error: MaxEventExecutionTimeMs is less than MaintenanceLoopms. The Maintenance Loop will only check scripts once per run.");
137
138 long Last_maxFunctionExecutionTimens = 0; // DateTime.Now.Ticks;
139 long Last_ReReadConfigFilens = DateTime.Now.Ticks;
140 int MaintenanceLoopTicks_ScriptLoadUnload_Count = 0;
141 int MaintenanceLoopTicks_Other_Count = 0;
142 bool MaintenanceLoopTicks_ScriptLoadUnload_ResetCount = false;
143 bool MaintenanceLoopTicks_Other_ResetCount = false;
144
145 while (true)
146 {
147 try
148 {
149 while (true)
150 {
151 Thread.Sleep(MaintenanceLoopms); // Sleep before next pass
152
153 // Reset counters?
154 if (MaintenanceLoopTicks_ScriptLoadUnload_ResetCount)
155 {
156 MaintenanceLoopTicks_ScriptLoadUnload_ResetCount = false;
157 MaintenanceLoopTicks_ScriptLoadUnload_Count = 0;
158 }
159 if (MaintenanceLoopTicks_Other_ResetCount)
160 {
161 MaintenanceLoopTicks_Other_ResetCount = false;
162 MaintenanceLoopTicks_Other_Count = 0;
163 }
164
165 // Increase our counters
166 MaintenanceLoopTicks_ScriptLoadUnload_Count++;
167 MaintenanceLoopTicks_Other_Count++;
168
169
170 //lock (ScriptEngine.ScriptEngines)
171 //{
172 foreach (ScriptEngine m_ScriptEngine in new ArrayList(ScriptEngine.ScriptEngines))
173 {
174 // lastScriptEngine = m_ScriptEngine;
175 // Re-reading config every x seconds
176 if (MaintenanceLoopTicks_Other_Count >= MaintenanceLoopTicks_Other)
177 {
178 MaintenanceLoopTicks_Other_ResetCount = true;
179 if (m_ScriptEngine.RefreshConfigFilens > 0)
180 {
181 // Check if its time to re-read config
182 if (DateTime.Now.Ticks - Last_ReReadConfigFilens >
183 m_ScriptEngine.RefreshConfigFilens)
184 {
185 //Console.WriteLine("Time passed: " + (DateTime.Now.Ticks - Last_ReReadConfigFilens) + ">" + m_ScriptEngine.RefreshConfigFilens);
186 // Its time to re-read config file
187 m_ScriptEngine.ReadConfig();
188 Last_ReReadConfigFilens = DateTime.Now.Ticks; // Reset time
189 }
190
191
192 // Adjust number of running script threads if not correct
193 if (m_ScriptEngine.m_EventQueueManager != null)
194 m_ScriptEngine.m_EventQueueManager.AdjustNumberOfScriptThreads();
195
196 // Check if any script has exceeded its max execution time
197 if (EventQueueManager.EnforceMaxExecutionTime)
198 {
199 // We are enforcing execution time
200 if (DateTime.Now.Ticks - Last_maxFunctionExecutionTimens >
201 EventQueueManager.maxFunctionExecutionTimens)
202 {
203 // Its time to check again
204 m_ScriptEngine.m_EventQueueManager.CheckScriptMaxExecTime(); // Do check
205 Last_maxFunctionExecutionTimens = DateTime.Now.Ticks; // Reset time
206 }
207 }
208 }
209 }
210 if (MaintenanceLoopTicks_ScriptLoadUnload_Count >= MaintenanceLoopTicks_ScriptLoadUnload)
211 {
212 MaintenanceLoopTicks_ScriptLoadUnload_ResetCount = true;
213 // LOAD / UNLOAD SCRIPTS
214 if (m_ScriptEngine.m_ScriptManager != null)
215 m_ScriptEngine.m_ScriptManager.DoScriptLoadUnload();
216 }
217 }
218 //}
219 }
220 }
221 catch(ThreadAbortException)
222 {
223 m_log.Error("Thread aborted in MaintenanceLoopThread. If this is during shutdown, please ignore");
224 }
225 catch (Exception ex)
226 {
227 m_log.ErrorFormat("Exception in MaintenanceLoopThread. Thread will recover after 5 sec throttle. Exception: {0}", ex.ToString());
228 }
229 }
230 }
231 #endregion
232
233 ///// <summary>
234 ///// If set to true then threads and stuff should try to make a graceful exit
235 ///// </summary>
236 //public bool PleaseShutdown
237 //{
238 // get { return _PleaseShutdown; }
239 // set { _PleaseShutdown = value; }
240 //}
241 //private bool _PleaseShutdown = false;
242 }
243}
diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptEngine.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptEngine.cs
index e785cc0..7ec71c2 100644
--- a/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptEngine.cs
+++ b/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptEngine.cs
@@ -26,29 +26,281 @@
26 */ 26 */
27 27
28using System; 28using System;
29using System.Collections.Generic;
30using System.Reflection;
31using log4net;
29using Nini.Config; 32using Nini.Config;
33using OpenSim.Region.Interfaces;
34using OpenSim.Region.Environment.Interfaces;
30using OpenSim.Region.Environment.Scenes; 35using OpenSim.Region.Environment.Scenes;
36using OpenSim.Region.ScriptEngine.Interfaces;
37using OpenMetaverse;
38using OpenSim.Region.ScriptEngine.Shared;
39using OpenSim.Region.ScriptEngine.Common;
31 40
32namespace OpenSim.Region.ScriptEngine.DotNetEngine 41namespace OpenSim.Region.ScriptEngine.DotNetEngine
33{ 42{
34 [Serializable] 43 [Serializable]
35 public class ScriptEngine : Common.ScriptEngineBase.ScriptEngine 44 public class ScriptEngine : IRegionModule, IEventReceiver, IScriptModule
36 { 45 {
37 // We need to override a few things for our DotNetEngine 46 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
38 public override void Initialise(Scene scene, IConfigSource config) 47
48 public static List<ScriptEngine> ScriptEngines = new List<ScriptEngine>();
49 private Scene m_Scene;
50 public Scene World
39 { 51 {
40 ConfigSource = config; 52 get { return m_Scene; }
41 InitializeEngine(scene, config, true, GetScriptManager());
42 } 53 }
54 public EventManager m_EventManager; // Handles and queues incoming events from OpenSim
55 public EventQueueManager m_EventQueueManager; // Executes events, handles script threads
56 public ScriptManager m_ScriptManager; // Load, unload and execute scripts
57 public AppDomainManager m_AppDomainManager; // Handles loading/unloading of scripts into AppDomains
58 public static MaintenanceThread m_MaintenanceThread; // Thread that does different kinds of maintenance, for example refreshing config and killing scripts that has been running too long
59
60 public IConfigSource ConfigSource;
61 public IConfig ScriptConfigSource;
62 private bool m_enabled = false;
43 63
44 public override Common.ScriptEngineBase.ScriptManager _GetScriptManager() 64 public IConfig Config
45 { 65 {
46 return new ScriptManager(this); 66 get { return ScriptConfigSource; }
47 } 67 }
48 68
49 public override string ScriptEngineName 69 /// <summary>
70 /// How many seconds between re-reading config-file. 0 = never. ScriptEngine will try to adjust to new config changes.
71 /// </summary>
72 public int RefreshConfigFileSeconds {
73 get { return (int)(RefreshConfigFilens / 10000000); }
74 set { RefreshConfigFilens = value * 10000000; }
75 }
76 public long RefreshConfigFilens;
77
78 public string ScriptEngineName
50 { 79 {
51 get { return "ScriptEngine.DotNetEngine"; } 80 get { return "ScriptEngine.DotNetEngine"; }
52 } 81 }
82
83 public ILog Log
84 {
85 get { return m_log; }
86 }
87
88 public ScriptEngine()
89 {
90 Common.mySE = this; // For logging, just need any instance, doesn't matter
91 lock (ScriptEngines)
92 {
93 ScriptEngines.Add(this); // Keep a list of ScriptEngines for shared threads to process all instances
94 }
95 }
96
97 public void Initialise(Scene Sceneworld, IConfigSource config)
98 {
99 m_log.Info("[" + ScriptEngineName + "]: ScriptEngine initializing");
100
101 ConfigSource = config;
102 m_Scene = Sceneworld;
103
104 // Make sure we have config
105 if (ConfigSource.Configs[ScriptEngineName] == null)
106 ConfigSource.AddConfig(ScriptEngineName);
107 ScriptConfigSource = ConfigSource.Configs[ScriptEngineName];
108
109 m_enabled = ScriptConfigSource.GetBoolean("Enabled", true);
110 if (!m_enabled)
111 return;
112
113 //m_log.Info("[" + ScriptEngineName + "]: InitializeEngine");
114
115 // Create all objects we'll be using
116 m_EventQueueManager = new EventQueueManager(this);
117 m_EventManager = new EventManager(this, true);
118 // We need to start it
119 m_ScriptManager = new ScriptManager(this);
120 m_ScriptManager.Setup();
121 m_AppDomainManager = new AppDomainManager(this);
122 if (m_MaintenanceThread == null)
123 m_MaintenanceThread = new MaintenanceThread();
124
125 m_log.Info("[" + ScriptEngineName + "]: Reading configuration from config section \"" + ScriptEngineName + "\"");
126 ReadConfig();
127
128 m_Scene.StackModuleInterface<IScriptModule>(this);
129 }
130
131 public void PostInitialise()
132 {
133 if (!m_enabled)
134 return;
135
136 m_EventManager.HookUpEvents();
137
138 m_ScriptManager.Start();
139 }
140
141 public void Shutdown()
142 {
143 // We are shutting down
144 lock (ScriptEngines)
145 {
146 ScriptEngines.Remove(this);
147 }
148 }
149
150 public void ReadConfig()
151 {
152#if DEBUG
153 //m_log.Debug("[" + ScriptEngineName + "]: Refreshing configuration for all modules");
154#endif
155 RefreshConfigFileSeconds = ScriptConfigSource.GetInt("RefreshConfig", 30);
156
157
158 // Create a new object (probably not necessary?)
159// ScriptConfigSource = ConfigSource.Configs[ScriptEngineName];
160
161 if (m_EventQueueManager != null) m_EventQueueManager.ReadConfig();
162 if (m_EventManager != null) m_EventManager.ReadConfig();
163 if (m_ScriptManager != null) m_ScriptManager.ReadConfig();
164 if (m_AppDomainManager != null) m_AppDomainManager.ReadConfig();
165 if (m_MaintenanceThread != null) m_MaintenanceThread.ReadConfig();
166 }
167
168 #region IRegionModule
169
170 public void Close()
171 {
172 }
173
174 public string Name
175 {
176 get { return "Common." + ScriptEngineName; }
177 }
178
179 public bool IsSharedModule
180 {
181 get { return false; }
182 }
183
184 public bool PostObjectEvent(uint localID, EventParams p)
185 {
186 return m_EventQueueManager.AddToObjectQueue(localID, p.EventName, p.DetectParams, p.Params);
187 }
188
189 public bool PostScriptEvent(UUID itemID, EventParams p)
190 {
191 uint localID = m_ScriptManager.GetLocalID(itemID);
192 return m_EventQueueManager.AddToScriptQueue(localID, itemID, p.EventName, p.DetectParams, p.Params);
193 }
194
195 public DetectParams GetDetectParams(UUID itemID, int number)
196 {
197 uint localID = m_ScriptManager.GetLocalID(itemID);
198 if (localID == 0)
199 return null;
200
201 IScript Script = m_ScriptManager.GetScript(localID, itemID);
202
203 if (Script == null)
204 return null;
205
206 DetectParams[] det = m_ScriptManager.GetDetectParams(Script);
207
208 if (number < 0 || number >= det.Length)
209 return null;
210
211 return det[number];
212 }
213
214 public int GetStartParameter(UUID itemID)
215 {
216 return 0;
217 }
218 #endregion
219
220 public void SetState(UUID itemID, string state)
221 {
222 uint localID = m_ScriptManager.GetLocalID(itemID);
223 if (localID == 0)
224 return;
225
226 IScript Script = m_ScriptManager.GetScript(localID, itemID);
227
228 if (Script == null)
229 return;
230
231 string currentState = Script.State;
232
233 if (currentState != state)
234 {
235 try
236 {
237 m_EventManager.state_exit(localID);
238
239 }
240 catch (AppDomainUnloadedException)
241 {
242 Console.WriteLine("[SCRIPT]: state change called when script was unloaded. Nothing to worry about, but noting the occurance");
243 }
244
245 Script.State = state;
246
247 try
248 {
249 int eventFlags = m_ScriptManager.GetStateEventFlags(localID, itemID);
250 SceneObjectPart part = m_Scene.GetSceneObjectPart(itemID);
251 if (part != null)
252 part.SetScriptEvents(itemID, eventFlags);
253 m_EventManager.state_entry(localID);
254 }
255 catch (AppDomainUnloadedException)
256 {
257 Console.WriteLine("[SCRIPT]: state change called when script was unloaded. Nothing to worry about, but noting the occurance");
258 }
259 }
260 }
261
262 public bool GetScriptState(UUID itemID)
263 {
264 uint localID = m_ScriptManager.GetLocalID(itemID);
265 if (localID == 0)
266 return false;
267
268 IScript script = m_ScriptManager.GetScript(localID, itemID);
269 if (script == null)
270 return false;
271
272 return script.Exec.Running?true:false;
273 }
274
275 public void SetScriptState(UUID itemID, bool state)
276 {
277 uint localID = m_ScriptManager.GetLocalID(itemID);
278 if (localID == 0)
279 return;
280
281 IScript script = m_ScriptManager.GetScript(localID, itemID);
282 if (script == null)
283 return;
284
285 script.Exec.Running = state;
286 }
287
288 public void ApiResetScript(UUID itemID)
289 {
290 uint localID = m_ScriptManager.GetLocalID(itemID);
291 if (localID == 0)
292 return;
293
294 m_ScriptManager.ResetScript(localID, itemID);
295 }
296
297 public void ResetScript(UUID itemID)
298 {
299 uint localID = m_ScriptManager.GetLocalID(itemID);
300 if (localID == 0)
301 return;
302
303 m_ScriptManager.ResetScript(localID, itemID);
304 }
53 } 305 }
54} 306}
diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs
index 8ff3bfd..12a8fe4 100644
--- a/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs
+++ b/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs
@@ -34,22 +34,66 @@ using OpenSim.Region.Environment.Scenes;
34using OpenSim.Region.ScriptEngine.Common; 34using OpenSim.Region.ScriptEngine.Common;
35using OpenSim.Region.ScriptEngine.Shared; 35using OpenSim.Region.ScriptEngine.Shared;
36using OpenSim.Region.ScriptEngine.Shared.Api; 36using OpenSim.Region.ScriptEngine.Shared.Api;
37using OpenSim.Region.ScriptEngine.Common.ScriptEngineBase; 37using System.Collections.Generic;
38using System.IO;
39using System.Runtime.Serialization.Formatters.Binary;
40using System.Threading;
38 41
39namespace OpenSim.Region.ScriptEngine.DotNetEngine 42namespace OpenSim.Region.ScriptEngine.DotNetEngine
40{ 43{
41 public class ScriptManager : Common.ScriptEngineBase.ScriptManager 44 public class ScriptManager
42 { 45 {
43 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 46 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
44 47
45 public ScriptManager(Common.ScriptEngineBase.ScriptEngine scriptEngine) 48 #region Declares
46 : base(scriptEngine) 49
50 private Thread scriptLoadUnloadThread;
51 private static Thread staticScriptLoadUnloadThread;
52 // private int scriptLoadUnloadThread_IdleSleepms;
53 private Queue<LUStruct> LUQueue = new Queue<LUStruct>();
54 private static bool PrivateThread;
55 private int LoadUnloadMaxQueueSize;
56 private Object scriptLock = new Object();
57 private bool m_started = false;
58 private Dictionary<IScript, DetectParams[]> detparms = new Dictionary<IScript, DetectParams[]>();
59
60 // Load/Unload structure
61 private struct LUStruct
62 {
63 public uint localID;
64 public UUID itemID;
65 public string script;
66 public LUType Action;
67 public int startParam;
68 public bool postOnRez;
69 }
70
71 private enum LUType
72 {
73 Unknown = 0,
74 Load = 1,
75 Unload = 2
76 }
77
78 // Xantor 20080525: Keep a list of compiled scripts this session for reuse
79 public Dictionary<UUID, String> scriptList = new Dictionary<UUID, string>();
80
81 // Object<string, Script<string, script>>
82 // IMPORTANT: Types and MemberInfo-derived objects require a LOT of memory.
83 // Instead use RuntimeTypeHandle, RuntimeFieldHandle and RunTimeHandle (IntPtr) instead!
84 public Dictionary<uint, Dictionary<UUID, IScript>> Scripts =
85 new Dictionary<uint, Dictionary<UUID, IScript>>();
86
87
88 public Scene World
47 { 89 {
48 base.m_scriptEngine = scriptEngine; 90 get { return m_scriptEngine.World; }
49 } 91 }
92
93 #endregion
50 private Compiler.LSL.Compiler LSLCompiler; 94 private Compiler.LSL.Compiler LSLCompiler;
51 95
52 public override void Initialize() 96 public void Initialize()
53 { 97 {
54 // Create our compiler 98 // Create our compiler
55 LSLCompiler = new Compiler.LSL.Compiler(m_scriptEngine); 99 LSLCompiler = new Compiler.LSL.Compiler(m_scriptEngine);
@@ -62,7 +106,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
62 // PROVIDE SCRIPT WITH ITS INTERFACE TO OpenSim 106 // PROVIDE SCRIPT WITH ITS INTERFACE TO OpenSim
63 107
64 108
65 public override void _StartScript(uint localID, UUID itemID, string Script, int startParam, bool postOnRez) 109 public void _StartScript(uint localID, UUID itemID, string Script, int startParam, bool postOnRez)
66 { 110 {
67 m_log.DebugFormat( 111 m_log.DebugFormat(
68 "[{0}]: ScriptManager StartScript: localID: {1}, itemID: {2}", 112 "[{0}]: ScriptManager StartScript: localID: {1}, itemID: {2}",
@@ -173,7 +217,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
173 } 217 }
174 } 218 }
175 219
176 public override void _StopScript(uint localID, UUID itemID) 220 public void _StopScript(uint localID, UUID itemID)
177 { 221 {
178 IScript LSLBC = GetScript(localID, itemID); 222 IScript LSLBC = GetScript(localID, itemID);
179 if (LSLBC == null) 223 if (LSLBC == null)
@@ -202,5 +246,405 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
202 ": " + e.ToString()); 246 ": " + e.ToString());
203 } 247 }
204 } 248 }
249
250 public void ReadConfig()
251 {
252 // scriptLoadUnloadThread_IdleSleepms = m_scriptEngine.ScriptConfigSource.GetInt("ScriptLoadUnloadLoopms", 30);
253 // TODO: Requires sharing of all ScriptManagers to single thread
254 PrivateThread = true; // m_scriptEngine.ScriptConfigSource.GetBoolean("PrivateScriptLoadUnloadThread", false);
255 LoadUnloadMaxQueueSize = m_scriptEngine.ScriptConfigSource.GetInt("LoadUnloadMaxQueueSize", 100);
256 }
257
258 #region Object init/shutdown
259
260 public ScriptEngine m_scriptEngine;
261
262 public ScriptManager(ScriptEngine scriptEngine)
263 {
264 m_scriptEngine = scriptEngine;
265 }
266 public void Setup()
267 {
268 ReadConfig();
269 Initialize();
270 }
271 public void Start()
272 {
273 m_started = true;
274
275
276 AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
277
278 //
279 // CREATE THREAD
280 // Private or shared
281 //
282 if (PrivateThread)
283 {
284 // Assign one thread per region
285 //scriptLoadUnloadThread = StartScriptLoadUnloadThread();
286 }
287 else
288 {
289 // Shared thread - make sure one exist, then assign it to the private
290 if (staticScriptLoadUnloadThread == null)
291 {
292 //staticScriptLoadUnloadThread = StartScriptLoadUnloadThread();
293 }
294 scriptLoadUnloadThread = staticScriptLoadUnloadThread;
295 }
296 }
297
298// TODO: unused
299// private static int privateThreadCount = 0;
300// private Thread StartScriptLoadUnloadThread()
301// {
302// Thread t = new Thread(ScriptLoadUnloadThreadLoop);
303// string name = "ScriptLoadUnloadThread:";
304// if (PrivateThread)
305// {
306// name += "Private:" + privateThreadCount;
307// privateThreadCount++;
308// }
309// else
310// {
311// name += "Shared";
312// }
313// t.Name = name;
314// t.IsBackground = true;
315// t.Priority = ThreadPriority.Normal;
316// t.Start();
317// OpenSim.Framework.ThreadTracker.Add(t);
318// return t;
319// }
320
321 ~ScriptManager()
322 {
323 // Abort load/unload thread
324 try
325 {
326 //PleaseShutdown = true;
327 //Thread.Sleep(100);
328 if (scriptLoadUnloadThread != null && scriptLoadUnloadThread.IsAlive == true)
329 {
330 scriptLoadUnloadThread.Abort();
331 //scriptLoadUnloadThread.Join();
332 }
333 }
334 catch
335 {
336 }
337 }
338
339 #endregion
340
341 #region Load / Unload scripts (Thread loop)
342
343// TODO: unused
344// private void ScriptLoadUnloadThreadLoop()
345// {
346// try
347// {
348// while (true)
349// {
350// if (LUQueue.Count == 0)
351// Thread.Sleep(scriptLoadUnloadThread_IdleSleepms);
352// //if (PleaseShutdown)
353// // return;
354// DoScriptLoadUnload();
355// }
356// }
357// catch (ThreadAbortException tae)
358// {
359// string a = tae.ToString();
360// a = String.Empty;
361// // Expected
362// }
363// }
364
365 public void DoScriptLoadUnload()
366 {
367 if (!m_started)
368 return;
369
370 lock (LUQueue)
371 {
372 if (LUQueue.Count > 0)
373 {
374m_scriptEngine.Log.InfoFormat("[{0}]: Loading script", m_scriptEngine.ScriptEngineName);
375 LUStruct item = LUQueue.Dequeue();
376
377 if (item.Action == LUType.Unload)
378 {
379 _StopScript(item.localID, item.itemID);
380 RemoveScript(item.localID, item.itemID);
381 }
382 else if (item.Action == LUType.Load)
383 {
384 _StartScript(item.localID, item.itemID, item.script, item.startParam, item.postOnRez);
385 }
386 }
387 }
388 }
389
390 #endregion
391
392 #region Helper functions
393
394 private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
395 {
396 //Console.WriteLine("ScriptManager.CurrentDomain_AssemblyResolve: " + args.Name);
397 return Assembly.GetExecutingAssembly().FullName == args.Name ? Assembly.GetExecutingAssembly() : null;
398 }
399
400 #endregion
401
402
403
404 #region Start/Stop/Reset script
405
406 // private readonly Object startStopLock = new Object();
407
408 /// <summary>
409 /// Fetches, loads and hooks up a script to an objects events
410 /// </summary>
411 /// <param name="itemID"></param>
412 /// <param name="localID"></param>
413 public void StartScript(uint localID, UUID itemID, string Script, int startParam, bool postOnRez)
414 {
415 lock (LUQueue)
416 {
417 if ((LUQueue.Count >= LoadUnloadMaxQueueSize) && m_started)
418 {
419 m_scriptEngine.Log.Error("[" + m_scriptEngine.ScriptEngineName + "]: ERROR: Load/unload queue item count is at " + LUQueue.Count + ". Config variable \"LoadUnloadMaxQueueSize\" is set to " + LoadUnloadMaxQueueSize + ", so ignoring new script.");
420 return;
421 }
422
423 LUStruct ls = new LUStruct();
424 ls.localID = localID;
425 ls.itemID = itemID;
426 ls.script = Script;
427 ls.Action = LUType.Load;
428 ls.startParam = startParam;
429 ls.postOnRez = postOnRez;
430 LUQueue.Enqueue(ls);
431m_scriptEngine.Log.InfoFormat("[{0}]: Queued script for load", m_scriptEngine.ScriptEngineName);
432 }
433 }
434
435 /// <summary>
436 /// Disables and unloads a script
437 /// </summary>
438 /// <param name="localID"></param>
439 /// <param name="itemID"></param>
440 public void StopScript(uint localID, UUID itemID)
441 {
442 LUStruct ls = new LUStruct();
443 ls.localID = localID;
444 ls.itemID = itemID;
445 ls.Action = LUType.Unload;
446 ls.startParam = 0;
447 ls.postOnRez = false;
448 lock (LUQueue)
449 {
450 LUQueue.Enqueue(ls);
451 }
452 }
453
454 // Create a new instance of the compiler (reuse)
455 //private Compiler.LSL.Compiler LSLCompiler = new Compiler.LSL.Compiler();
456
457
458 #endregion
459
460 #region Perform event execution in script
461
462 /// <summary>
463 /// Execute a LL-event-function in Script
464 /// </summary>
465 /// <param name="localID">Object the script is located in</param>
466 /// <param name="itemID">Script ID</param>
467 /// <param name="FunctionName">Name of function</param>
468 /// <param name="args">Arguments to pass to function</param>
469 internal void ExecuteEvent(uint localID, UUID itemID, string FunctionName, DetectParams[] qParams, object[] args)
470 {
471 //cfk 2-7-08 dont need this right now and the default Linux build has DEBUG defined
472 ///#if DEBUG
473 /// Console.WriteLine("ScriptEngine: Inside ExecuteEvent for event " + FunctionName);
474 ///#endif
475 // Execute a function in the script
476 //m_scriptEngine.Log.Info("[" + ScriptEngineName + "]: Executing Function localID: " + localID + ", itemID: " + itemID + ", FunctionName: " + FunctionName);
477 //ScriptBaseInterface Script = (ScriptBaseInterface)GetScript(localID, itemID);
478 IScript Script = GetScript(localID, itemID);
479 if (Script == null)
480 {
481 return;
482 }
483 //cfk 2-7-08 dont need this right now and the default Linux build has DEBUG defined
484 ///#if DEBUG
485 /// Console.WriteLine("ScriptEngine: Executing event: " + FunctionName);
486 ///#endif
487 // Must be done in correct AppDomain, so leaving it up to the script itself
488 detparms[Script] = qParams;
489 Script.Exec.ExecuteEvent(FunctionName, args);
490 detparms.Remove(Script);
491 }
492
493 public uint GetLocalID(UUID itemID)
494 {
495 foreach (KeyValuePair<uint, Dictionary<UUID, IScript> > k in Scripts)
496 {
497 if (k.Value.ContainsKey(itemID))
498 return k.Key;
499 }
500 return 0;
501 }
502
503 public int GetStateEventFlags(uint localID, UUID itemID)
504 {
505 // Console.WriteLine("GetStateEventFlags for <" + localID + "," + itemID + ">");
506 try
507 {
508 IScript Script = GetScript(localID, itemID);
509 if (Script == null)
510 {
511 return 0;
512 }
513 ExecutorBase.scriptEvents evflags = Script.Exec.GetStateEventFlags();
514 return (int)evflags;
515 }
516 catch (Exception)
517 {
518 }
519
520 return 0;
521 }
522
523
524 #endregion
525
526 #region Internal functions to keep track of script
527
528 public List<UUID> GetScriptKeys(uint localID)
529 {
530 if (Scripts.ContainsKey(localID) == false)
531 return new List<UUID>();
532
533 Dictionary<UUID, IScript> Obj;
534 Scripts.TryGetValue(localID, out Obj);
535
536 return new List<UUID>(Obj.Keys);
537 }
538
539 public IScript GetScript(uint localID, UUID itemID)
540 {
541 lock (scriptLock)
542 {
543 IScript Script = null;
544
545 if (Scripts.ContainsKey(localID) == false)
546 return null;
547
548 Dictionary<UUID, IScript> Obj;
549 Scripts.TryGetValue(localID, out Obj);
550 if (Obj.ContainsKey(itemID) == false)
551 return null;
552
553 // Get script
554 Obj.TryGetValue(itemID, out Script);
555 return Script;
556 }
557 }
558
559 public void SetScript(uint localID, UUID itemID, IScript Script)
560 {
561 lock (scriptLock)
562 {
563 // Create object if it doesn't exist
564 if (Scripts.ContainsKey(localID) == false)
565 {
566 Scripts.Add(localID, new Dictionary<UUID, IScript>());
567 }
568
569 // Delete script if it exists
570 Dictionary<UUID, IScript> Obj;
571 Scripts.TryGetValue(localID, out Obj);
572 if (Obj.ContainsKey(itemID) == true)
573 Obj.Remove(itemID);
574
575 // Add to object
576 Obj.Add(itemID, Script);
577 }
578 }
579
580 public void RemoveScript(uint localID, UUID itemID)
581 {
582 if (localID == 0)
583 localID = GetLocalID(itemID);
584
585 // Don't have that object?
586 if (Scripts.ContainsKey(localID) == false)
587 return;
588
589 // Delete script if it exists
590 Dictionary<UUID, IScript> Obj;
591 Scripts.TryGetValue(localID, out Obj);
592 if (Obj.ContainsKey(itemID) == true)
593 Obj.Remove(itemID);
594 }
595
596 #endregion
597
598
599 public void ResetScript(uint localID, UUID itemID)
600 {
601 IScript s = GetScript(localID, itemID);
602 string script = s.Source;
603 StopScript(localID, itemID);
604 SceneObjectPart part = World.GetSceneObjectPart(localID);
605 part.GetInventoryItem(itemID).PermsMask = 0;
606 part.GetInventoryItem(itemID).PermsGranter = UUID.Zero;
607 StartScript(localID, itemID, script, s.StartParam, false);
608 }
609
610
611 #region Script serialization/deserialization
612
613 public void GetSerializedScript(uint localID, UUID itemID)
614 {
615 // Serialize the script and return it
616 // Should not be a problem
617 FileStream fs = File.Create("SERIALIZED_SCRIPT_" + itemID);
618 BinaryFormatter b = new BinaryFormatter();
619 b.Serialize(fs, GetScript(localID, itemID));
620 fs.Close();
621 }
622
623 public void PutSerializedScript(uint localID, UUID itemID)
624 {
625 // Deserialize the script and inject it into an AppDomain
626
627 // How to inject into an AppDomain?
628 }
629
630 #endregion
631
632 ///// <summary>
633 ///// If set to true then threads and stuff should try to make a graceful exit
634 ///// </summary>
635 //public bool PleaseShutdown
636 //{
637 // get { return _PleaseShutdown; }
638 // set { _PleaseShutdown = value; }
639 //}
640 //private bool _PleaseShutdown = false;
641
642 public DetectParams[] GetDetectParams(IScript script)
643 {
644 if (detparms.ContainsKey(script))
645 return detparms[script];
646
647 return null;
648 }
205 } 649 }
206} 650}