aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ScriptEngine/XMREngine/XMRScriptUThread.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/XMRScriptUThread.cs491
1 files changed, 58 insertions, 433 deletions
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRScriptUThread.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMRScriptUThread.cs
index 2e290dd..74bba4f 100644
--- a/OpenSim/Region/ScriptEngine/XMREngine/XMRScriptUThread.cs
+++ b/OpenSim/Region/ScriptEngine/XMREngine/XMRScriptUThread.cs
@@ -25,31 +25,24 @@
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27 27
28using Mono.Tasklets;
29using System; 28using System;
30using System.Collections.Generic;
31using System.Reflection;
32using System.Threading;
33
34
35 29
36/***************************\ 30/***************************\
37 * Use standard C# code * 31 * Use standard C# code *
38 * - uses system threads * 32 * - uses stack smashing *
39\***************************/ 33\***************************/
40 34
41namespace OpenSim.Region.ScriptEngine.XMREngine { 35namespace OpenSim.Region.ScriptEngine.XMREngine
36{
42 37
43 public class ScriptUThread_Sys : IScriptUThread, IDisposable 38 public class ScriptUThread_Nul : IScriptUThread, IDisposable
44 { 39 {
45 private Exception except;
46 private int active; // -1: hibernating 40 private int active; // -1: hibernating
47 // 0: exited 41 // 0: exited
48 // 1: running 42 // 1: running
49 private object activeLock = new object ();
50 private XMRInstance instance; 43 private XMRInstance instance;
51 44
52 public ScriptUThread_Sys (XMRInstance instance) 45 public ScriptUThread_Nul (XMRInstance instance)
53 { 46 {
54 this.instance = instance; 47 this.instance = instance;
55 } 48 }
@@ -63,31 +56,27 @@ namespace OpenSim.Region.ScriptEngine.XMREngine {
63 */ 56 */
64 public Exception StartEx () 57 public Exception StartEx ()
65 { 58 {
66 lock (activeLock) { 59 // We should only be called when no event handler running.
67 60 if (active != 0) throw new Exception ("active=" + active);
68 /* 61
69 * We should only be called when script is inactive. 62 // Start script event handler from very beginning.
70 */ 63 active = 1;
71 if (active != 0) throw new Exception ("active=" + active); 64 Exception except = null;
72 65 instance.callMode = XMRInstance.CallMode_NORMAL;
73 /* 66 try {
74 * Tell CallSEHThread() to run script event handler in a thread. 67 instance.CallSEH (); // run script event handler
75 */ 68 active = 0;
76 active = 1; 69 } catch (StackHibernateException) {
77 TredPoo.RunSomething (CallSEHThread); 70 if (instance.callMode != XMRInstance.CallMode_SAVE) {
78 71 throw new Exception ("callMode=" + instance.callMode);
79 /*
80 * Wait for script to call Hiber() or for script to
81 * return back out to CallSEHThread().
82 */
83 while (active > 0) {
84 Monitor.Wait (activeLock);
85 } 72 }
73 active = -1; // it is hibernating, can be resumed
74 } catch (Exception e) {
75 active = 0;
76 except = e; // threw exception, save for Start()/Resume()
86 } 77 }
87 78
88 /* 79 // Return whether or not script threw an exception.
89 * Return whether or not script threw an exception.
90 */
91 return except; 80 return except;
92 } 81 }
93 82
@@ -98,31 +87,27 @@ namespace OpenSim.Region.ScriptEngine.XMREngine {
98 */ 87 */
99 public Exception ResumeEx () 88 public Exception ResumeEx ()
100 { 89 {
101 lock (activeLock) { 90 // We should only be called when script is hibernating.
102 91 if (active >= 0) throw new Exception ("active=" + active);
103 /* 92
104 * We should only be called when script is hibernating. 93 // Resume script from captured stack.
105 */ 94 instance.callMode = XMRInstance.CallMode_RESTORE;
106 if (active >= 0) throw new Exception ("active=" + active); 95 instance.suspendOnCheckRunTemp = true;
107 96 Exception except = null;
108 /* 97 try {
109 * Tell Hiber() to return back to script. 98 instance.CallSEH (); // run script event handler
110 */ 99 active = 0;
111 active = 1; 100 } catch (StackHibernateException) {
112 Monitor.PulseAll (activeLock); 101 if (instance.callMode != XMRInstance.CallMode_SAVE) {
113 102 throw new Exception ("callMode=" + instance.callMode);
114 /*
115 * Wait for script to call Hiber() again or for script to
116 * return back out to CallSEHThread().
117 */
118 while (active > 0) {
119 Monitor.Wait (activeLock);
120 } 103 }
104 active = -1;
105 } catch (Exception e) {
106 active = 0;
107 except = e; // threw exception, save for Start()/Resume()
121 } 108 }
122 109
123 /* 110 // Return whether or not script threw an exception.
124 * Return whether or not script threw an exception.
125 */
126 return except; 111 return except;
127 } 112 }
128 113
@@ -131,241 +116,6 @@ namespace OpenSim.Region.ScriptEngine.XMREngine {
131 * Terminate thread asap. 116 * Terminate thread asap.
132 */ 117 */
133 public void Dispose () 118 public void Dispose ()
134 {
135 lock (activeLock) {
136 instance = null;
137 Monitor.PulseAll (activeLock);
138 }
139 }
140
141 /**
142 * @brief Determine if script is active.
143 * Returns: 0: nothing started or has returned
144 * Resume() must not be called
145 * Start() may be called
146 * Hiber() must not be called
147 * -1: thread has called Hiber()
148 * Resume() may be called
149 * Start() may be called
150 * Hiber() must not be called
151 * 1: thread is running
152 * Resume() must not be called
153 * Start() must not be called
154 * Hiber() may be called
155 */
156 public int Active ()
157 {
158 return active;
159 }
160
161 /**
162 * @brief This thread executes the script event handler code.
163 */
164 private void CallSEHThread ()
165 {
166 lock (activeLock) {
167 if (active <= 0) throw new Exception ("active=" + active);
168
169 except = null; // assume completion without exception
170 try {
171 instance.CallSEH (); // run script event handler
172 } catch (Exception e) {
173 except = e; // threw exception, save for Start()/Resume()
174 }
175
176 active = 0; // tell Start() or Resume() we're done
177 Monitor.PulseAll (activeLock);
178 }
179 }
180
181 /**
182 * @brief Called by the script event handler whenever it wants to hibernate.
183 */
184 public void Hiber ()
185 {
186 if (active <= 0) throw new Exception ("active=" + active);
187
188 // tell Start() or Resume() we are hibernating
189 active = -1;
190 Monitor.PulseAll (activeLock);
191
192 // wait for Resume() or Dispose() to be called
193 while ((active < 0) && (instance != null)) {
194 Monitor.Wait (activeLock);
195 }
196
197 // don't execute any more script code, just exit
198 if (instance == null) {
199 throw new AbortedByDisposeException ();
200 }
201 }
202
203 /**
204 * @brief Number of remaining stack bytes.
205 */
206 public int StackLeft ()
207 {
208 return 0x7FFFFFFF;
209 }
210
211 public class AbortedByDisposeException : Exception, IXMRUncatchable { }
212
213 /**
214 * @brief Pool of threads that run script event handlers.
215 */
216 private class TredPoo {
217 private static readonly TimeSpan idleTimeSpan = new TimeSpan (0, 0, 1, 0, 0); // 1 minute
218
219 private static int tredPooAvail = 0;
220 private static object tredPooLock = new object ();
221 private static Queue<ThreadStart> tredPooQueue = new Queue<ThreadStart> ();
222
223 /**
224 * @brief Queue a function for execution in a system thread.
225 */
226 public static void RunSomething (ThreadStart entry)
227 {
228 lock (tredPooLock) {
229 tredPooQueue.Enqueue (entry);
230 Monitor.Pulse (tredPooLock);
231 if (tredPooAvail < tredPooQueue.Count) {
232 new TredPoo ();
233 }
234 }
235 }
236
237 /**
238 * @brief Start a new system thread.
239 * It will shortly attempt to dequeue work or if none,
240 * add itself to the available thread list.
241 */
242 private TredPoo ()
243 {
244 Thread thread = new Thread (Main);
245 thread.Name = "XMRUThread_sys";
246 thread.IsBackground = true;
247 thread.Start ();
248 tredPooAvail ++;
249 }
250
251 /**
252 * @brief Executes items from the queue or waits a little while
253 * if nothing. If idle for a while, it exits.
254 */
255 private void Main ()
256 {
257 int first = 1;
258 ThreadStart entry;
259 while (true) {
260 lock (tredPooLock) {
261 tredPooAvail -= first;
262 first = 0;
263 while (tredPooQueue.Count <= 0) {
264 tredPooAvail ++;
265 bool keepgoing = Monitor.Wait (tredPooLock, idleTimeSpan);
266 -- tredPooAvail;
267 if (!keepgoing) return;
268 }
269 entry = tredPooQueue.Dequeue ();
270 }
271 entry ();
272 }
273 }
274 }
275 }
276}
277
278
279
280/*************************************\
281 * Use Mono.Tasklets.Continuations *
282 * - memcpy's stack *
283\*************************************/
284
285namespace OpenSim.Region.ScriptEngine.XMREngine {
286
287 public partial class XMRInstance {
288 public Mono.Tasklets.Continuation engstack;
289 public Mono.Tasklets.Continuation scrstack;
290 }
291
292 public class ScriptUThread_Con : IScriptUThread, IDisposable
293 {
294 private XMRInstance instance;
295
296 public ScriptUThread_Con (XMRInstance instance)
297 {
298 this.instance = instance;
299 }
300
301 private const int SAVEENGINESTACK = 0;
302 private const int LOADENGINESTACK = 1;
303 private const int SAVESCRIPTSTACK = 2;
304 private const int LOADSCRIPTSTACK = 3;
305
306 private Exception except;
307 private int active;
308
309 /**
310 * @brief Start script event handler from the beginning.
311 * Return when either the script event handler completes
312 * or the script calls Hiber().
313 * @returns null: script did not throw any exception so far
314 * else: script threw an exception
315 */
316 public Exception StartEx ()
317 {
318 /*
319 * Save engine stack so we know how to jump back to engine in case
320 * the script calls Hiber().
321 */
322 switch (instance.engstack.Store (SAVEENGINESTACK)) {
323
324 /*
325 * Engine stack has been saved, start running the event handler.
326 */
327 case SAVEENGINESTACK: {
328
329 /*
330 * Run event handler according to stackFrames.
331 * In either case it is assumed that stateCode and eventCode
332 * indicate which event handler is to be called and that ehArgs
333 * points to the event handler argument list.
334 */
335 active = 1;
336 except = null;
337 try {
338 instance.CallSEH ();
339 } catch (Exception e) {
340 except = e;
341 }
342
343 /*
344 * We now want to return to the script engine.
345 * Setting active = 0 means the microthread has exited.
346 * We need to call engstack.Restore() in case the script called Hiber()
347 * anywhere, we want to return out the corresponding Restore() and not the
348 * Start().
349 */
350 active = 0;
351 instance.engstack.Restore (LOADENGINESTACK);
352 throw new Exception ("returned from Restore()");
353 }
354
355 /*
356 * Script called Hiber() somewhere so just return back out.
357 */
358 case LOADENGINESTACK: {
359 break;
360 }
361
362 default: throw new Exception ("bad engstack code");
363 }
364
365 return except;
366 }
367
368 public void Dispose ()
369 { } 119 { }
370 120
371 /** 121 /**
@@ -389,75 +139,33 @@ namespace OpenSim.Region.ScriptEngine.XMREngine {
389 } 139 }
390 140
391 /** 141 /**
392 * @brief Called by the script wherever it wants to hibernate. 142 * @brief Called by the script event handler whenever it wants to hibernate.
393 * So this means to save the scripts stack in 'instance.scrstack' then
394 * restore the engstack to cause us to return back to the engine.
395 */ 143 */
396 public void Hiber () 144 public void Hiber ()
397 { 145 {
398 /* 146 if (instance.callMode != XMRInstance.CallMode_NORMAL) {
399 * Save where we are in the script's code in 'instance.scrstack' 147 throw new Exception ("callMode=" + instance.callMode);
400 * so we can wake the script when Resume() is called.
401 */
402 switch (instance.scrstack.Store (SAVESCRIPTSTACK)) {
403
404 /*
405 * Script's stack is now saved in 'instance.scrstack'.
406 * Reload the engine's stack from 'instance.engstack' and jump to it.
407 */
408 case SAVESCRIPTSTACK: {
409 active = -1;
410 instance.engstack.Restore (LOADENGINESTACK);
411 throw new Exception ("returned from Restore()");
412 }
413
414 /*
415 * Resume() was just called and we want to resume executing script code.
416 */
417 case LOADSCRIPTSTACK: {
418 break;
419 }
420
421 default: throw new Exception ("bad scrstack code");
422 } 148 }
423 }
424 149
425 /** 150 switch (active) {
426 * @brief We now want to run some more script code from where it last hibernated
427 * until it either finishes the script event handler or until the script
428 * calls Hiber() again.
429 */
430 public Exception ResumeEx ()
431 {
432 /*
433 * Save where we are in the engine's code in 'instance.engstack'
434 * so if the script calls Hiber() again or exits, we know how to get
435 * back to the engine.
436 */
437 switch (instance.engstack.Store (SAVEENGINESTACK)) {
438 151
439 /* 152 // the stack has been restored as a result of calling ResumeEx()
440 * This is original call to Resume() from the engine, 153 // say the microthread is now active and resume processing
441 * jump to where we left off within Hiber(). 154 case -1: {
442 */
443 case SAVEENGINESTACK: {
444 active = 1; 155 active = 1;
445 instance.scrstack.Restore (LOADSCRIPTSTACK); 156 return;
446 throw new Exception ("returned from Restore()");
447 } 157 }
448 158
449 /* 159 // the script event handler wants to hibernate
450 * Script has called Hiber() again, so return back to 160 // capture stack frames and unwind to Start() or Resume()
451 * script engine code. 161 case 1: {
452 */ 162 instance.callMode = XMRInstance.CallMode_SAVE;
453 case LOADENGINESTACK: { 163 instance.stackFrames = null;
454 break; 164 throw new StackHibernateException ();
455 } 165 }
456 166
457 default: throw new Exception ("bad engstack code"); 167 default: throw new Exception ("active=" + active);
458 } 168 }
459
460 return except;
461 } 169 }
462 170
463 /** 171 /**
@@ -467,91 +175,8 @@ namespace OpenSim.Region.ScriptEngine.XMREngine {
467 { 175 {
468 return 0x7FFFFFFF; 176 return 0x7FFFFFFF;
469 } 177 }
470 }
471}
472
473
474
475/***********************************\
476 * Use Mono.Tasklets.MMRUThreads *
477 * - switches stack pointer *
478\***********************************/
479
480namespace OpenSim.Region.ScriptEngine.XMREngine {
481
482 public class ScriptUThread_MMR : IScriptUThread, IDisposable
483 {
484 private static Exception uthread_looked;
485 private static Type uttype;
486 private static Type uthread_entry;
487 private static MethodInfo uthread_dispose;
488 private static MethodInfo uthread_startex;
489 private static MethodInfo uthread_resumex;
490 private static MethodInfo uthread_suspend;
491 private static MethodInfo uthread_active;
492 private static MethodInfo uthread_stackleft;
493
494 public static Exception LoadMono ()
495 {
496 if ((uthread_looked == null) && (uthread_stackleft == null)) {
497 try {
498 Assembly mt = Assembly.Load ("Mono.Tasklets");
499 uttype = mt.GetType ("Mono.Tasklets.MMRUThread", true);
500 uthread_entry = mt.GetType ("Mono.Tasklets.MMRUThread+Entry", true);
501
502 uthread_dispose = uttype.GetMethod ("Dispose"); // no parameters, no return value
503 uthread_startex = uttype.GetMethod ("StartEx"); // takes uthread_entry delegate as parameter, returns exception
504 uthread_resumex = uttype.GetMethod ("ResumeEx"); // takes exception as parameter, returns exception
505 uthread_suspend = uttype.GetMethod ("Suspend", new Type[] { }); // no return value
506 uthread_active = uttype.GetMethod ("Active"); // no parameters, returns int
507 uthread_stackleft = uttype.GetMethod ("StackLeft"); // no parameters, returns IntPtr
508 } catch (Exception e) {
509 uthread_looked = new NotSupportedException ("'mmr' thread model requires patched mono", e);
510 }
511 }
512 return uthread_looked;
513 }
514 178
515 private static object[] resumex_args = new object[] { null }; 179 public class StackHibernateException : Exception, IXMRUncatchable { }
516
517 private object uthread; // type MMRUThread
518 private object[] startex_args = new object[1];
519
520 public ScriptUThread_MMR (XMRInstance instance)
521 {
522 this.uthread = Activator.CreateInstance (uttype, new object[] { (IntPtr) instance.m_StackSize, instance.m_DescName });
523 startex_args[0] = Delegate.CreateDelegate (uthread_entry, instance, "CallSEH");
524 }
525
526 public void Dispose ()
527 {
528 uthread_dispose.Invoke (uthread, null);
529 uthread = null;
530 }
531
532 public Exception StartEx ()
533 {
534 return (Exception) uthread_startex.Invoke (uthread, startex_args);
535 }
536
537 public Exception ResumeEx ()
538 {
539 return (Exception) uthread_resumex.Invoke (uthread, resumex_args);
540 }
541
542 public void Hiber ()
543 {
544 uthread_suspend.Invoke (null, null);
545 }
546
547 public int Active ()
548 {
549 return (int) uthread_active.Invoke (uthread, null);
550 }
551
552 public int StackLeft ()
553 {
554 return (int) (IntPtr) uthread_stackleft.Invoke (null, null);
555 }
556 } 180 }
557} 181}
182