#line 29 "/home/ubuntu/felix/src/packages/fibres.fdoc"
  
  Low level management of Felix fthreads (fibres).
  open class Fibres
  {
    private gen _start[t]: (t->0)*t->cont = "$1->clone()->call(0,$2)";
  
    Function to start a continution with argument type t.
    gen start[t] (p:t->0) (x:t) = { return _start (p,x); }
  
    private fun _start0: (1->0)->cont = "$1->clone()->call(0)";
  
    Function to start a contiuation without an argument.
    gen start (p:1->0) = { return _start0 (p); }
  
    Function to make a fibre out of a continuation.
    gen mk_thread: cont->fthread = "new(*ptf-> gcp,::flx::rtl::_fthread_ptr_map,false) ::flx::rtl::fthread_t($1)";
  
    // Spawn a fibre on this fibres scheduler.
    // uses a supervisor call so can't be used in a function
    proc spawn_fthread(p:1->0)
    {
        var con = start p;              // get continuation of p
        var fthr = mk_thread con;
        svc$ svc_spawn_fthread fthr;
    }
  
    proc schedule_fthread(p:1->0)
    {
        var con = start p;              // get continuation of p
        var fthr = mk_thread con;
        svc$ svc_schedule_fthread fthr;
    }
  
    proc suicide: 1 = "throw (con_t*)NULL;";
  
    proc chain : cont = "return $1;" requires property "heap_closure";
  
    // *********************************************************
    // NESTED SYNC SCHEDULER
    // NOTE: deprecated in favour of async scheduler below
    // *********************************************************
    The type of a fibre scheduler.
    type fibre_scheduler = "::flx::run::sync_sched*" requires header '#include "flx_sync.hpp"';
  
    Construct a fibre scheduler.
     NOTE: NOW GARBAGE COLLECTED!
    ctor fibre_scheduler: bool = """
      new(*ptf-> gcp,::flx::run::sync_sched_ptr_map,false)
        ::flx::run::sync_sched
        (
          $1,
          ptf-> gcp,
          new(*ptf-> gcp, ::flx::run::fthread_list_ptr_map, false) ::flx::run::fthread_list(ptf-> gcp)
        )
      """
    ;
    ctor fibre_scheduler () =>
      fibre_scheduler (Env::getenv "FLX_DEBUG_DRIVER" != "")
    ;
  
  
    Spawn a fibre on a given scheduler with a given continuation.
    Note: does NOT run it!
    FIXME: no mutex guard!!
    proc spawn_fibre: fibre_scheduler * fthread = """
      $1->active->push_back($2);
    """;
  
    proc frun: (1->0) = "::flx::rtl::executil::frun (ptf-> gcp, $1);"
      requires header '#include "flx_executil.hpp"'
    ;
  
    proc run: fibre_scheduler = "$1->frun();";
  
    proc run (p: 1 -> 0) {
      var s = fibre_scheduler();
      spawn_fthread s p;
      s.run;
    }
  
  
    The type of the stop state of the fibre scheduler.
    terminated: the scheduler is terminated.
    blocked: the scheduler is out of threads to run.
    delegated: the scheduler has been issued a service
     request by a thread which it cannot satisfy.
     The scheduler is put in delegated state and awaits
     for another service to satisfy the request and put
     it back in operation.
      //$ Note: there is no "operating" state because the
    stop state can only be queried by the schedulers caller
    when the scheduler returns control to it.
    enum fibre_scheduler_state {
      terminated,
      blocked,
      delegated
    };
    fun get_state : fibre_scheduler -> fibre_scheduler_state = "$1->fs";
  
  
    Core user procedure for launching a fibre.
    proc spawn_fthread (fs:fibre_scheduler) (p:1->0) { spawn_fibre (fs,p.start.mk_thread); }
  
    // *********************************************************
    // ASYNC SCHEDULER
    // *********************************************************
    // FIXME: it is leaked .. to be fixed shortly
  
    // async scheduler type
    type async_scheduler = "::flx::run::async_sched*"
      requires header '#include "flx_async.hpp"',
      package "flx_arun"
    ;
  
    // async scheduler constructor
    ctor async_scheduler: bool = """
      new
      ::flx::run::async_sched
          (
            ptf-> world, // world object
            $1, // debug driver flag
            ptf-> gcp,  // GC profile object
            new(*ptf-> gcp, ::flx::run::fthread_list_ptr_map, false) ::flx::run::fthread_list(ptf-> gcp),
            ::flx::run::async_sched::mainline // temporary hack! thread kind (should be inherited)
          )
        """
      ;
  
    // async scheduler constructor wrapper
    ctor async_scheduler () =>
      async_scheduler (Env::getenv "FLX_DEBUG_DRIVER" != "")
    ;
  
    // spawn fibre on async scheduler from fthread object
    proc spawn_fibre: async_scheduler * fthread = """
        $1->ss->active->push_back($2);
    """;
  
    // spawn fibre on async scheduler from procedure
    proc spawn_fthread (fs:async_scheduler) (p:1->0) { spawn_fibre (fs,p.start.mk_thread); }
  
    proc prun: async_scheduler = "$1->prun();";
  
  
    proc async_run (p: 1 -> 0) {
      var s = async_scheduler();
      spawn_fthread s p;
      s.prun;
    }
  
  // *********************************************************
  // MISC STUFF THAT MAY NOT BE USED, CONSIDER DELETING IT
  // UNRELIABLE ANYHOW .. CHECK PLUGINS ...
  // *********************************************************
  
  
    Execute a single step of a fibre.
    gen step: cont -> cont = "$1->resume()";
  
    Schedule death of a fibre.
    proc kill: fthread = "$1->cc = 0;";
  
    Run a continuation until it terminates.
    Do not use this proc if the underlying
    procedure attempts to read messages.
    This is a low level primitive, bypassing fthreads.
    proc run: cont = "::flx::rtl::executil::run($1);" requires package "flx_executil";
  
    private proc _send[t]: &cont * t =
    """
    {
      using namespace ::flx::rtl;
      con_t *tmp = *(con_t**)$1.get_data();
      // run target until it reaches a service request (or death)
      while(tmp && (!tmp->p_svc || tmp->p_svc->variant == svc_yield)) {
        try { tmp=tmp->resume(); }
        catch (con_t *x) { tmp = x; }
      }
      // check it is alive and making the expected service request
      if (!tmp)
        throw flx_exec_failure_t (__FILE__,"send","Send to terminated procedure");
      if (!tmp->p_svc)
        throw flx_exec_failure_t (__FILE__,"send","Send to unready Procedure");
      if (tmp->p_svc->variant != svc_read)
        throw flx_exec_failure_t (__FILE__,"send","Send to Procedure which is not trying to read");
      // store the message
      **(?1**)tmp->p_svc->data= $2;
      // clear the service request
      tmp->p_svc = 0;
      // run the target until the next service request (or death)
      while(tmp && (!tmp->p_svc || tmp->p_svc->variant == svc_yield)) {
        try { tmp=tmp->resume(); }
        catch (con_t *x) { tmp = x; }
      }
      // save the new continuation
      *(con_t**)$1.get_data() = tmp;
  
    }
    """;
  
    Send a message to a continuation.
    There is no type checking on the message type.
    The procedure is executed until
    the next wait_state, then the message is stored.
    Low level primitive, bypassing fthreads.
    proc send[t] (p:&cont) (x:t)
    {
      _send (p,x);
    }
  
  }