MMRUThread microthread implementation

MMRUThread's are a way to provide co-routines or stack switching to provide
efficient thread switching in a co-operative environment.

To createt a MMRUThread, define a class that is a derivative of the base
MMRUThread class, and define a method, Main() that is the main function of the
thread.

When you are ready to run the uthread, call the object's Start() method.
Start() will call the Main() method on its own new stack.  If Main() returns,
then it switches back to the original stack and returns where Start() was
called.

If Main() calls Suspend(), perhaps through many call levels, the new stack is
saved and the original stack is restored then it returns to where Start() was
called.  When the original stack's code is ready to resume the UThread object,
it calls its Resume() method.  At which point the stacks are swapped again
until either the new stack routines call Suspend() or return out the Main()
routine.

In summary:

   Start() is like pthread_create()
   Suspend() is like pthread_cond_wait()
   Resume() is like pthread_cond_signal()
   Exit() is like pthread_exit()

Valid transitions:

     constructor()     ->  [it is now inactive]

  [inactive, Active() returns 0]
     ->  Start()       ->  [it is now running]
     ->  Dispose()     ->  memory released
     ->  Resume()      ->  exception thrown
     ->  Suspend()     ->  exception thrown
     ->  Exit()        ->  exception thrown

  [running, Active() returns 1]
     ->  Main() returns           ->  [it is now inactive]
     ->  Main() throws exception  ->  [it is now inactive]
     ->  Main() calls Exit()      ->  [it is now inactive]
     ->  Main() calls Suspend()   ->  [it is now suspended]
     ->  Start()       ->  exception thrown
     ->  Dispose()     ->  exception thrown

  [suspended, Active() returns -1]
     ->  Resume()      ->  [it is now running, resumed from where it suspended]
     ->  Start()       ->  [it is now running, but from beginning of Main()]
     ->  Dispose()     ->  memory released
     ->  Suspend()     ->  exception thrown
     ->  Exit()        ->  exception thrown

Application provided Method
---------------------------

public override void Main()

   Called on the uthread stack when non-uthread code calls Start().  UThread
   object is in the running state when the Main() method is running and remains
   in the running state until Main() either returns, throws an exception, or
   calls Suspend() or Exit().

   If Main() returns or throws an exception, the uthread is transitioned to
   inactive.  If Main() calls Suspend(), either directly or via any number of
   call levels, the uthread is transitioned to suspend.

System provided Methods
-----------------------

public void Start()

   Called by non-uthread code to switch to uthread code at the beginning of the
   uthread's Main() method.

   The uthread must be inactive or suspended and is transitioned to the running
   state.

public static void Suspend()

   Called by uthread code to switch back to the non-uthread code that most
   recently transitioned the uthread to the running state.  The uthread must be
   in the running state and is transitioned to the suspended state.  The
   uthread may subsequently be Resume()'d or Start()'d.

public static void Exit()

   Called by uthread code to switch back to the non-uthread code that most
   recently transitioned the uthread to the running state.  The uthread must be
   in the running state and is transitioned to the inactive state.  The uthread
   may subsequently be Start()'d.

public void Resume()

   Called by non-uthread code to switch to uthread code at the point where the
   uthread last called Suspend().  The uthread must be in the suspended state
   and is transitioned to the running state.

   This method may also be called from a uthread to switch to a target uthread
   at the point where the target uthread last called Suspend().  The calling
   uthread is put in the suspended state by this call and the target uthread is
   transitioned to the running state.  The target uthread must be in the
   suspended state when this method is called.  The behavior is just as if the
   calling uthread called Suspend() then the orignal thread immediately called
   Resume() for the target uthread.

public int Active()

   Query the state of a uthread, returns:

                    valid calls while in that state
                    -------------------------------
      0: inactive   Start(), Dispose()
     -1: suspended  Start(), Resume(), Dispose()
      1: running    Suspend(), Exit()

public static MMRUThread Current()

   Determine which uthread is calling Current().  Returns null if called by an
   original thread.

public void Dispose()

   Immediately releases stack memory used by the uthread object.
   Call this when you will no longer use the uthread object.
   The uthread must be either inactive or suspended.
   The uthread object must not be used again.
   This call is optional as the stack memory will be released when
      the uthread object is garbage collected if Dispose() was not
      called.  However since stack memory can be very large, Dispose()
      should be called when it is known the uthread object will no
      longer be used.

Example program
---------------

using Mono.Tasklets;
using System;
using System.IO;

public class UThreadTest {

    public static int Main (string[] args) {

        /*
         * Print some silly message to start.
         */
        Console.WriteLine ("Hello World!");

        /*
         * Create a couple MMRUThread derived objects.
         * Use different objects so we switch RIPs and such.
         */
        ThreadOne thread1 = new ThreadOne ();
        ThreadTwo thread2 = new ThreadTwo ();

        Console.WriteLine ("Main: start thread 1");
        thread1.Start ();
        Console.WriteLine ("Main: start thread 2");
        thread2.Start ();

        /*
         * This is our scheduler loop.  Run threads that say they
         * are suspended.
         */
        while ((thread1.Active () < 0) || (thread2.Active () < 0)) {
            if (thread1.Active () < 0) {
                Console.WriteLine ("Main: resume thread 1");
                thread1.Resume ();
            }
            if (thread2.Active () < 0) {
                Console.WriteLine ("Main: resume thread 2");
                thread2.Resume ();
            }
        }

        if (thread1.Active () != 0) {
            throw new Exception ("Main: thread 1 is still active");
        }
        if (thread2.Active () != 0) {
            throw new Exception ("Main: thread 2 is still active");
        }

        Console.WriteLine ("Main: all done!");

        return 0;
    }

    public class ThreadOne : MMRUThread {
        public override void Main ()
        {
            Console.WriteLine ("ThreadOne: started");
            Suspend ();
            Console.WriteLine ("ThreadOne: step 1");
            Suspend ();
            Console.WriteLine ("ThreadOne: finished");
        }
    }

    public class ThreadTwo : MMRUThread {
        public override void Main ()
        {
            Console.WriteLine ("ThreadTwo: started");
            Suspend ();
            Console.WriteLine ("ThreadTwo: step 1");
            Suspend ();
            Console.WriteLine ("ThreadTwo: step 2");
            Suspend ();
            Console.WriteLine ("ThreadTwo: finished");
            Exit ();
            throw new Exception ("ThreadTwo: Exit() returned");
        }
    }
}