#line 21 "/home/travis/build/felix-lang/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_detached fthr;
    }
  
    proc schedule_fthread(p:1->0)
    {
        var con = start p;              // get continuation of p
        var fthr = mk_thread con;
        svc$ svc_schedule_detached fthr;
    }
  
  
    proc chain : cont = "return $1;";
  
    The type of a fibre scheduler.
    type fibre_scheduler = "::flx::run::sync_sched*" requires header '#include "flx_sync.hpp"';
  
    Construct a fibre scheduler.
     NOTE: NOT GARBAGE COLLECTED!
    ctor fibre_scheduler: 1 = """new ::flx::run::sync_sched(false, PTF gcp,
      new ::std::list<::flx::rtl::fthread_t*>)""";
    ;
    proc delete_fibre_scheduler : fibre_scheduler = """
      if ($1->ft) $1->collector->remove_root($1->ft);
      for(
       ::std::list<::flx::rtl::fthread_t*>::iterator p = $1->active->begin();
       p != $1->active->end();
       p++
      )
      $1->collector->remove_root(*p);
      delete $1->active; delete $1->ft; delete $1;
    """;
  
    Spawn a fibre on a given scheduler with a given continuation.
    proc spawn_fibre: fibre_scheduler * fthread = """
      $1->collector->add_root($2);
      $1->active->push_back($2);
      $1->frun();
    """;
  
    proc frun: (1->0) = "::flx::rtl::executil::frun (PTF gcp, $1);"
      requires header '#include "flx_executil.hpp"'
    ;
  
  
    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); }
  
    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);
    }
  
  }