aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ScriptEngine/XMREngine/XMRScriptUThread.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ScriptEngine/XMREngine/XMRScriptUThread.cs')
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/XMRScriptUThread.cs557
1 files changed, 557 insertions, 0 deletions
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRScriptUThread.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMRScriptUThread.cs
new file mode 100644
index 0000000..2e290dd
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/XMRScriptUThread.cs
@@ -0,0 +1,557 @@
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 OpenSimulator 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 Mono.Tasklets;
29using System;
30using System.Collections.Generic;
31using System.Reflection;
32using System.Threading;
33
34
35
36/***************************\
37 * Use standard C# code *
38 * - uses system threads *
39\***************************/
40
41namespace OpenSim.Region.ScriptEngine.XMREngine {
42
43 public class ScriptUThread_Sys : IScriptUThread, IDisposable
44 {
45 private Exception except;
46 private int active; // -1: hibernating
47 // 0: exited
48 // 1: running
49 private object activeLock = new object ();
50 private XMRInstance instance;
51
52 public ScriptUThread_Sys (XMRInstance instance)
53 {
54 this.instance = instance;
55 }
56
57 /**
58 * @brief Start script event handler from the beginning.
59 * Return when either the script event handler completes
60 * or the script calls Hiber().
61 * @returns null: script did not throw any exception so far
62 * else: script threw an exception
63 */
64 public Exception StartEx ()
65 {
66 lock (activeLock) {
67
68 /*
69 * We should only be called when script is inactive.
70 */
71 if (active != 0) throw new Exception ("active=" + active);
72
73 /*
74 * Tell CallSEHThread() to run script event handler in a thread.
75 */
76 active = 1;
77 TredPoo.RunSomething (CallSEHThread);
78
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 }
86 }
87
88 /*
89 * Return whether or not script threw an exception.
90 */
91 return except;
92 }
93
94 /**
95 * @brief We now want to run some more script code from where it last hibernated
96 * until it either finishes the script event handler or until the script
97 * calls Hiber() again.
98 */
99 public Exception ResumeEx ()
100 {
101 lock (activeLock) {
102
103 /*
104 * We should only be called when script is hibernating.
105 */
106 if (active >= 0) throw new Exception ("active=" + active);
107
108 /*
109 * Tell Hiber() to return back to script.
110 */
111 active = 1;
112 Monitor.PulseAll (activeLock);
113
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 }
121 }
122
123 /*
124 * Return whether or not script threw an exception.
125 */
126 return except;
127 }
128
129 /**
130 * @brief Script is being closed out.
131 * Terminate thread asap.
132 */
133 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 { }
370
371 /**
372 * @brief Determine if script is active.
373 * Returns: 0: nothing started or has returned
374 * Resume() must not be called
375 * Start() may be called
376 * Hiber() must not be called
377 * -1: thread has called Hiber()
378 * Resume() may be called
379 * Start() may be called
380 * Hiber() must not be called
381 * 1: thread is running
382 * Resume() must not be called
383 * Start() must not be called
384 * Hiber() may be called
385 */
386 public int Active ()
387 {
388 return active;
389 }
390
391 /**
392 * @brief Called by the script wherever 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 */
396 public void Hiber ()
397 {
398 /*
399 * Save where we are in the script's code in 'instance.scrstack'
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 }
423 }
424
425 /**
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
439 /*
440 * This is original call to Resume() from the engine,
441 * jump to where we left off within Hiber().
442 */
443 case SAVEENGINESTACK: {
444 active = 1;
445 instance.scrstack.Restore (LOADSCRIPTSTACK);
446 throw new Exception ("returned from Restore()");
447 }
448
449 /*
450 * Script has called Hiber() again, so return back to
451 * script engine code.
452 */
453 case LOADENGINESTACK: {
454 break;
455 }
456
457 default: throw new Exception ("bad engstack code");
458 }
459
460 return except;
461 }
462
463 /**
464 * @brief Number of remaining stack bytes.
465 */
466 public int StackLeft ()
467 {
468 return 0x7FFFFFFF;
469 }
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
515 private static object[] resumex_args = new object[] { null };
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 }
557}