diff options
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Region/ScriptEngine/XMREngine/XMRScriptUThread.cs | 491 |
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 | ||
28 | using Mono.Tasklets; | ||
29 | using System; | 28 | using System; |
30 | using System.Collections.Generic; | ||
31 | using System.Reflection; | ||
32 | using 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 | ||
41 | namespace OpenSim.Region.ScriptEngine.XMREngine { | 35 | namespace 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 | |||
285 | namespace 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 | |||
480 | namespace 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 | |||