[[title

              Anatomy of a Librified `libarch' Function
                                 ---
                  /`#include <tla/libarch/arch.h>'/
]]


  [[cartouche

        It may be helpful to read this document in parallel with the
        <"template /C/ code for `libarch' functions" --
        $/src/tla/Template-fn>

        The template is described in detail in the appendix
        {Template Details}.
  ]]

* `t_arch const arch' -- The Universal Function Parameter

  Every ordinary function in `libarch' accepts a parameter of type
  `t_arch', conventionally named `arch'.  Some very low level
  functions are an exception to this rule.

  By convention, the output parameters to a function are first
  in its parameter list, then the `t_arch const arch' parameter,
  and then any input parameters.


* Arch Sessions and Arch Calls

  The `arch' parameter holds the state, including dynamically
  allocated resources, of a single "arch session" and of all "arch
  calls" within that session.

  An /arch call/ is the invocation and execution of a single,
  client-facing, ordinary `libarch' function.  Typically an arch
  call performs a primitive arch operation or calculation.  A call may
  temporarily allocate resources as it runs; these resources are
  released before the call returns to the client.   During a single
  call, temporary resources are "attached" to the `t_arch const arch' 
  and, when a call returns, are automatically released.

  An /arch session/ is a sequence of calls which may share 
  persistent state among themselves.   A `t_arch const arch' value
  is the name for a single context in which such persistent state
  can be recorded.   For example, the location of a user's
  `.arch-params' directory defaults to `~/' but it might as easily be
  an element of the persistent state of a `t_arch const arch' object.
  A client can allocate a `t_arch', set its `.arch-params' location
  to a non-default location, and then perform a series of arch 
  calls with that `t_arch'.   Those arch calls will use the
  non-default `.arch-params' location.   Other calls in the 
  same process, using a different `t_arch', might use a 
  different `.arch-params' location.


* Anatomy of an Arch Function

** Entry and Exit

  `libarch' extends the standard /C/ function prolog and
  postlog with its own conventions.   Every `libarch' function
  must be of the general form (here illustrated for a function
  which can not raise errors):

  [[tty

    int
    some_fn (t_arch const arch)
    {
      int answer = -1;

      arch_enter_call (arch);

      /* ... compute the real answer.  Set `answer'.  ... */

      arch_leave_call (arch);
      return answer;
    }

  ]]

  Unbalanced calls to `arch_leave_call' constitute an invariant
  violation resulting from a coding error and cause a
  call to `panic'.


** Error Codes and Raising Errors

  `libarch' uses dynamically assigned */positive*/ integer error codes 
  when reporting error conditions to callers.   Must functions which
  can indicate an error do so by returning a */negative/* integer
  whose magnitude (additive inverse) is an error code.

  In addition to signalling errors via return value, functions must
  explicitly *raise* errors, which records then in the `t_arch const
  arch' parameter.  Typical code looks (schematically) like this:


  [[tty
    ssize_t
    some_fn (t_arch const arch)
    {
      ssize_t answer = -1;
      ssize_t errcode = 0;

      arch_enter_call (arch);

      /* ... compute the real value for `answer' but:
       */
      if (... an XYZZY error has been detected ...)
        {
          errcode = error_arch_XYZZY ();
          goto error_exit;
        }

      /* ... assuming that `answer' is now set 
       */
      invariant (answer >= 0);  /* normal return can't be negative --
                                 * that's reserved for errors.
                                 */
      arch_leave_call (arch);
      return answer;


     error_exit:
       invariant (errcode > 0);
       arch_RAISE (arch, errcode, "some_fn");
       answer = -errcode;
       arch_leave_call (arch);
       return answer;
    }

  ]]

  See also {Error State Transition Table}.


** Error Messages -- The `msgbuf'

  Between a call to `arch_enter_call' and a call to `arch_RAISE',
  clients can format arbitrary text to be associated with the
  raised error.   (The intent is that this is text which
  might be displayed to a user.)

  The exmaple above might be modified to contain:

  [[tty
      if (... an XYZZY error has been detected ...)
        {
          arch_msgbuf_printfmt (arch, "an xyzzy error caused by %s\n", xyzzy_errstr());
          errcode = error_arch_XYZZY ();
          goto error_exit;
        }
  ]]


** Managing Allocted Local State

  Commonly, `libarch' functions need to allocate temporary strings,
  lists, and tables to contain the results of intermediate
  computations.   Those data structures must be reliably freed
  before returning to the caller.   The situation is common enough
  that `libarch' includes help for it.

  Local allocated values, when allocated, are "attached" to a `t_arch
  const arch' value.  When the call is exited (indicated by a call to
  `arch_leave_call'), those attached resources are freed:

  /WARNING:/ this interface is not fully implemented yet and is
  subject to minor changes as it is finalized.

  [[tty
    ssize_t
    some_fn (t_arch const arch)
    {
      ARCH_FN_DECL;
      ssize_t answer = -1;
      t_local_val tmp_str = LOCAL_VAL_NIL_INIT;
      ssize_t status = -1;

      arch_enter_call (arch);


      /* ... */

      /* e.g.: allocate a local string
       */
      status = val_make_str (addr_local_val (tmp_str, arch),
                             arch,
                             "hello world");
      if (status < 0)
        {
          errcode = arch_error_ENOMEM ();
          goto error_exit;
        }

      /* ... */

      arch_leave_call (arch);
      /*
       * The call to `arch_leave_call' above will implicitly 
       * release the storage allocated for `tmp_str'.
       */

      return answer;


     error_exit:
       invariant (errcode > 0);
       arch_RAISE (arch, errcode, "some_fn");
       answer = -errcode;
       arch_leave_call (arch);
       return answer;
    }

  ]]



* Appendix: {*Template Details}

  The file <"src/tla/Template-fn" -- $/src/tla/Template-fn> contains
  a template for librified `libarch' functions.   The template begins:

** Function Prototype

  [[tty
    /** function: FN_NAME
     *
     */
    ssize_t
    FN_NAME (t_arch const arch)
  ]]

  there and elsewhere, replace `FN_NAME' with the name of the function
  you are defining.

  Most functions should return a `ssize_t' value, a negative integer
  indicating an error (whose error code is the magnitude of that
  negative integer).   Functions with no other use for the `ssize_t'
  return value should return `0' upon success.   Other functions may
  return any non-negative value upon success.

  Output parameters should be declared prior to the `arch' parameter;
  other parameters following the `arch' parameter.   All parameters
  should be declared const.  Normally, each parameter should be 
  declared on a separate line.  For example:

  [[tty
    ssize_t
    some_fn (int * const n_ret,
             t_arch const arch,
             int const x,
             int const y)
  ]]

** Local Variable

  All non-const local variables /must/ be declared in the outermost 
  block of a function.  The outermost block in the template begins: 

  [[tty
    {
      ARCH_FN_DECLS;
      ssize_t answer = -1;
      int pc = 0;
      ssize_t errcode = 0;
  ]]

  Additional local variables may be added to that list.

  The sole exception to this role is local variables which are
  `const'.  Such variables may be declared (and necessarily
  initialized) at the top of nested blocks.

  No local variable in the outermost block may have a non-constant
  initializer or may have an initializer which makes a call to 
  a function (even a `const' function).


** Function Entry

  The body of the template reads, schematically:

  [[tty
    if (pc == 0)
      {
        /* init */
        arch_enter_call (arch);
        goto body;
      }
    else
      {
        [... error handling code ...]

      normal_exit:
        invariant (answer >= 0);
        goto exit_with_cleanups;

      exit_with_cleanups:
        goto exit_immediately;

      exit_immediately:
        arch_leave_call (arch);
        return answer;
      }

    body:

    [... main body of the function ...]

    goto normal_exit;

  ]]

  Programs may add code to the section labeled `/* init */'
  between the call to `arch_enter_call' and the 
  `goto body' statement.   Coded added in that location
  should be solely for the purpose of initializing local
  variables to values which are not constant.  For example, 
  a local variable declared:

  [[tty
     time_t now = 0;
  ]]

  might be initialized in the `/* init */' section with:

  [[tty
     time_t now = time (0);
  ]]

  It could not have been declared these ways:

  [[tty
     time_t now = time (0);             /* illegal (by convention) */
     time_t const now = time (0);       /* illegal (by convention) */
  ]]

  because local variable initializers at the top of a `libarch'
  function may not make function calls.

** Single Entry, Single Exit

  The template contains the code:

  [[tty
      exit_immediately:
        arch_leave_call (arch);
        return answer;
  ]]

  That is the only `return' statement in the template and programmers
  /must not/ add addition `return' statements to the function
  definition.   All exit paths from the function must pass through the
  code labeled `exit_immediately'.

  The call to `arch_leave_call' provides run-time enforcement of an
  open ended set of calling convention invarients.  It has has
  responsibility for releasing temporary resources allocated by this
  function call.


** Error and Normal Returns but a Single Cleanup Path

  The template contains:

  [[tty

    error_exit:
      invariant (errcode > 0);
      arch_RAISE (arch, errcode, "FN_NAME");
      answer = -errcode;
      goto exit_with_cleanups;

    efatal_exit:
      errcode = error_arch_EFATAL ();
      goto error_exit;

    eparam_exit:
      errcode = error_arch_EPARAM ();
      goto error_exit;

    normal_exit:
      invariant (answer >= 0);
      goto exit_with_cleanups;

    exit_with_cleanups:
      goto exit_immediately;

  ]]

  Programmers /may/ delete the code labeled `efatal_exit' and
  `eparam_exit' if those exit paths are not needed.

  All error exit paths from the function should involve setting
  `errcode' to a positive integer value and transfering to
  `error_exit'.  

  All ordinary exit paths from the function should involve setting the
  variable `answer' to the return value from the function and
  transfering to `normal_exit'.

  When those conventions are followed, execution passes through the 
  code labled `exit_with_cleanups' and finally to`exit_immediately'.

  Programmers may (have to) customize the template in two ways:
  (1) By adding resource-releasing code to the section labled
  `exit_with_cleanups';  (2) by modifying the `invariant''s in
  `error_exit' and `normal_exit' to more precisely reflect the 
  range of expected return values from the function.


** The Main Function Body

  The template finishes with:

  [[tty

   body:

    goto normal_exit;

  ]]

  Programmers must customize that part of the template by filling in 
  code implementing the function between the `body' and the `goto'.
  In some cases, programmer's must replace the label in the `goto' or
  replace the entire `goto' with `/* not reached */'.



* Appendix: {*Error State Transition Table}

  Any `libarch' function (client facing or ordinary internal function)
  can raise an error.   Caller's *must* respond to all errors raised.

  To an imperfect but practical extent, `libarch' enforces that
  restriction.   In particular, every `t_libarch' is partly
  characterized by an *error state*.   The error state is 
  described by three integers, according to the state diagram below.

  The gist is that if a function you call raises an error, your client 
  must either re-raise the error before returning to *its* caller, or
  must "clear" the error state.

  [[tty

    NORMAL state:
          depth = D
          err_depth = -1;
          last_raise_depth = -1;

      invariants:
          0 <= D

      transitions:

         * arch_enter_call (arch)

            goto NORMAL with
              new_depth := D + 1

         * arch_leave_call (arch)

            if (depth == 0)
              FATAL ERROR
            else
              goto NORMAL with
                new_depth := D - 1

         * arch_RAISE (arch, errcode, tag)

            goto ERROR_HERE with
              new_err_depth := D
              new_last_raise_depth := D


         * arch_clear_error (arch)

            goto NORMAL with
              err_depth = -1;
              last_raise_depth = -1;


    ----

    ERROR_HERE state:

          depth = D
          err_depth = E;
          last_raise_depth = L;

      invariants:
          D >= 0
          D <= E
          D == L

      transitions:

         * arch_enter_call (arch)

            FATAL ERROR

         * arch_leave_call (arch)

            if (depth == 0)
              FATAL ERROR
            else
              goto ERROR_CALLEE with
                new_depth := D - 1

         * arch_RAISE (arch, errcode, tag)

           goto ERROR_HERE

         * arch_clear_error (arch)

            goto NORMAL with
              err_depth = -1;
              last_raise_depth = -1;

    ----

    ERROR_CALLEE state:

          depth = D
          err_depth = E;
          last_raise_depth = L;

      invariants:
          D >= 0
          D < E
          D == (L - 1)

      transitions:

         * arch_enter_call (arch)

            FATAL ERROR

          * arch_leave_call (arch)

            FATAL ERROR

          * arch_RAISE (arch, errcode, tag)

            goto ERROR_HERE with
              new_last_raise_depth = D

         * arch_clear_error (arch)

            goto NORMAL with
              err_depth = -1;
              last_raise_depth = -1;

  ]]






* Copyright

 /Copyright (C) 2004 Tom Lord/

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2, or (at your option)
 any later version.

 This program is distributed in the hope that it will be useful,
 but /WITHOUT ANY WARRANTY/; without even the implied warranty of
 /MERCHANTABILITY/ or /FITNESS FOR A PARTICULAR PURPOSE/.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software Foundation,
 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

 See the file `COPYING' for further information about
 the copyright and warranty status of this work.



[[null
   ; arch-tag: Tom Lord Mon Feb 21 10:08:12 2005 (communications/tla-dynstate.txt)
]]

