    CR(VII)                       1/4/75                      CR(VII)

    NAME
         crfork, crexit, crread, crwrite, crexch, crprior - coroutine
         scheme

    SYNOPSIS
         int crfork( [ stack, nwords ] )
         int stack[];
         int nwords;

         crexit()

         int crread(connector, buffer, nbytes)
         int *connector[2];
         char *buffer;
         int nbytes;

         crwrite(connector, buffer, nbytes)
         int *connector[2];
         char *buffer;
         int nbytes;

         crexch(conn1, conn2, i)
         int *conn1[2], *conn2[2];
         int i;

         #define logical char *
         crprior(p)
         logical p;

    DESCRIPTION
         These functions are named by analogy to  fork, exit, read,
         write(II).   They  establish and synchronize `coroutines',
         which behave in many respects like a set of processes  work-
         ing  in  the  same  address  space.   The  functions live in
         /usr/lib/cr.a.

         Coroutines are placed on queues to indicate their  state  of
         readiness.   One  coroutine is always distinguished as `run-
         ning'.  Coroutines that are runnable  but  not  running  are
         registered on a `ready queue'.  The head member of the ready
         queue is started whenever no other coroutine is specifically
         caused to be running.

         Each  connector  heads two queues: Connector[0] is the queue
         of unsatisfied crreads outstanding on the  connector.   Con-
         nector[1]  is  the queue of crwrites.  All queues must start
         empty, i.e.  with heads set to zero.

         Crfork is normally called with no arguments.  It places  the
         running  coroutine at the head of the ready queue, creates a
         new coroutine, and starts the new one running.   Crfork  re-
         turns immediately in the new coroutine with value 0, and up-
         on restarting of the old coroutine with value 1.

         Crexit stops the running coroutine and does not place it  in

                                  - 1 -

    CR(VII)                       1/4/75                      CR(VII)

         any queue.

         Crread  copies  characters from the buffer of the crwrite at
         the head of the connector's write queue to the buffer of cr-
         read.   If  the write queue is empty, copying is delayed and
         the running coroutine is placed on the read queue.  The num-
         ber  of  characters  copied is the minimum of nbytes and the
         number of characters remaining in the write buffer,  and  is
         returned  as  the value of crread.  After copying, the loca-
         tion of the write buffer and the  corresponding  nbytes  are
         updated  appropriately.   If  zero  characters  remain,  the
         coroutine of the crwrite is moved to the head of  the  ready
         queue.  If the write queue remains nonempty, the head member
         of the read queue is moved to the head of the ready queue.

         Crwrite queues the  running  coroutine  on  the  connector's
         write queue, and records the fact that nbytes (zero or more)
         characters in the string buffer are  available  to  crreads.
         If  the  read queue is not empty, its head member is started
         running.

         Crexch exchanges the read queues  of  connectors  conn1  and
         conn2  if i=0; and it exchanges the write queues if i=1.  If
         a nonempty read queue that had been  paired  with  an  empty
         write  queue  becomes  paired  with  a nonempty write queue,
         crexch moves the head member of that read queue to the  head
         of the ready queue.

         Crprior  sets a priority on the running coroutine to control
         the queuing of crreads and crwrites.  When queued, the  run-
         ning  coroutine  will take its place before coroutines whose
         priorities exceed its own priority and after others.  Prior-
         ities  are  compared as logical, i.e.  unsigned, quantities.
         Initially each coroutine's priority is set as large as  pos-
         sible, so default queuing is FIFO.

         Storage allocation.   The  old  and new coroutine share the
         same activation record in the function that invoked  crfork,
         so  only one may return from the invoking function, and then
         only when the other has completed execution  in  that  func-
         tion.

         The activation record for each function execution is dynami-
         cally allocated rather than stacked; a factor of 3  in  run-
         ning  time  overhead  can  result if function calls are very
         frequent.  The overhead may be overcome by providing a sepa-
         rate  stack  for  each coroutine and dispensing with dynamic
         allocation.  The base (lowest) address and size of  the  new
         coroutine's  stack  are supplied to crfork as optional argu-
         ments stack and nwords.  Stacked allocation and dynamic  al-
         location cannot be mixed in one run.  For stacked operation,
         obtain the coroutine functions from  /usr/lib/scr.a  instead
         of /usr/lib/cr.a.

                                  - 2 -

    CR(VII)                       1/4/75                      CR(VII)

    FILES
         /usr/lib/cr.a
         /usr/lib/scr.a

    DIAGNOSTICS
         `rsave  doesn't  work'  -  an  old  C compilation has called
         `rsave'.  It must be recompiled to work with  the  coroutine
         scheme.

    BUGS
         Under  /usr/lib/cr.a  each  function  has  just  12 words of
         anonymous stack for hard expressions and arguments  of  fur-
         ther calls, regardless of actual need.  There is no checking
         for stack overflow.
         Under /usr/lib/scr.a stack overflow checking is  not  rigor-
         ous.

                                  - 3 -