/*=====================================================================*/
/*    .../prgm/project/bigloo/fthread/src2.6b/DotNet/BglThread.cs      */
/*    -------------------------------------------------------------    */
/*    Author      :  Manuel Serrano                                    */
/*    Creation    :  Wed Jan 30 10:40:23 2002                          */
/*    Last change :  Fri Jun 20 16:15:21 2003 (serrano)                */
/*    Copyright   :  2002-03 Manuel Serrano                            */
/*    -------------------------------------------------------------    */
/*    Bigloo Class for threads. Nothing fancy here.                    */
/*=====================================================================*/
using System;
using System.Threading;
using bigloo;

namespace bigloo.fairthread
{
  /*---------------------------------------------------------------------*/
  /*    BglThread                                                        */
  /*---------------------------------------------------------------------*/
  public class BglThread
  {
    // global cpu token
    private static Object token;

    // Bigloo connection
    private Object bgl_obj;
    private Object ident;
    private procedure bgl_thunk;
    private static bgldynamic current_dynamic_env;
    public bgldynamic env;

    // parent thread (i.e. the Java thread that has entered this Bgl thread)
    private Thread parent;

    // The associated thread
    private readonly Thread thread;

    // public constructor
    public BglThread()
    {
      env = new bgldynamic( bgldynamic.current_dynamic_env );
      thread= new Thread( new ThreadStart( run ) );
    }

    // public constructor
    public BglThread( procedure thunk )
    {
      env = new bgldynamic( bgldynamic.current_dynamic_env );
      bgl_thunk = thunk;
      thread= new Thread( new ThreadStart( run ) );
    }

    // public constructor
    public BglThread( procedure thunk, symbol name )
    {
      ident = name;
      env = new bgldynamic( bgldynamic.current_dynamic_env );
      bgl_thunk = thunk;
      thread= new Thread( new ThreadStart( run ) );
      thread.Name= foreign.newstring( name.pname );
    }

    // Starts the thread
    public void start()
    {
      thread.Start();
    }

    // This method is invoked by Bigloo when the Bigloo object
    // representing the thread has been allocated.
    public void setup( Object o )  
    {
      bgl_obj = o;
    }

    // This method is used for compatibility with the Posix implementation
    // Don't remove it
    public Object idSet( Object o )
    {
      ident = o;
      return o;
    }

    // Return back the pointer to the Bigloo object
    public Object getBglObj() 
    {
      return bgl_obj;
    }

    // The thread entry-point
    private void run() 
    {
      try
      {
        fairWait();
        bgl_thunk.funcall0();
      }
      catch( Exception e )
      {
        Console.Error.WriteLine( e.StackTrace );
        try
        {
          foreign.internalerror( e );
        }
        catch( Exception )
        {
          Environment.Exit( 1 );
        }
      }
    }

    // wait for a notification
    public void fairWait()
    {
      lock( this )
      {
        try
        {
          while( token != this )
            Monitor.Wait( this );
        }
        catch( ThreadInterruptedException e )
        {
          Console.Error.WriteLine( "interrupted wait exception: " + e ); 
        }
      }
    }

    // switch to another thread
    public void fairSwitch( BglThread next )
    {
      lock( next )
      {
        token = next;
        bgldynamic.current_dynamic_env = next.env;
        Monitor.Pulse( next );
      }
    }

    // enter the scheduler
    public void enter_scheduler()
    {
      parent = Thread.CurrentThread;

      current_dynamic_env = bgldynamic.current_dynamic_env;
      bgldynamic.current_dynamic_env = this.env;
      
      /* switch into the scheduler thread */
      fairSwitch( this );

      /* wait for the token to be back */
      lock( parent )
      {
        try
        {
          while( token != parent )
            Monitor.Wait( parent );
        }
        catch( ThreadInterruptedException e )
        {
          Console.Error.WriteLine( "interrupted wait exception: " + e );
        }
      }
    }

    // leave scheduler
    public void leave_scheduler()
    {
      token = null;
      bgldynamic.current_dynamic_env = current_dynamic_env;

      lock( parent )
      {
        token = parent;
        Monitor.Pulse( parent );
      }

      lock( this )
      {
        while( token != this )
          try
          {
            Monitor.Wait( this );
          }
          catch( ThreadInterruptedException e )
          {
            Console.Error.WriteLine( "interrupted wait exception: " + e );
          }
      }
    }
  }
}
