ERROR(10.2)                                           ERROR(10.2)

     NAME
          error, nexterror, poperror, waserror - error handling
          functions

     SYNOPSIS
          void  error(char*);

          void  nexterror(void);

          void  poperror(void);

          int   waserror(void);

     DESCRIPTION
          The Inferno kernel handles error conditions using non-local
          gotos, similar to setjmp/longjmp in ANSI C, but using a
          stack of error labels.  This simplifies many of the internal
          interfaces by eliminating the need for returning and check-
          ing error codes at every level of the call stack, at the
          cost of requiring kernel routines to adhere to a strict dis-
          cipline.

          Each Inferno process has in its defining Proc structure a
          stack of labels, currently 32 elements deep.  A kernel func-
          tion that must perform a clean up or recovery action on an
          error makes a stylised call to waserror, nexterror and
          poperror:

               if(waserror()){
                    /* recovery action */
                    nexterror();
               }
               /* normal action */
               poperror();

          When called in the normal course of events, waserror regis-
          ters an error handling block by pushing its label onto the
          stack, and returns zero.  The return value of waserror
          should be tested as shown above.  If non-zero (true), the
          calling function should perform the needed error recovery,
          ended by a call to nexterror to transfer control to the next
          location on the error stack.  Typical recovery actions
          include deallocating memory, unlocking resources, and reset-
          ting state variables.

          Within the recovery block, after handling an error condi-
          tion, there must normally be a call to nexterror to transfer
          control to any error recovery lower down in the stack.  The
          main exception is in the outermost function in a process,
          which must not call nexterror (there being nothing further

     Page 1                       Plan 9             (printed 4/19/24)

     ERROR(10.2)                                           ERROR(10.2)

          on the stack), but calls pexit (see kproc(10.2)) instead, to
          terminate the process.

          When the need to recover a particular resource has passed, a
          function that has called waserror must remove the corre-
          sponding label from the stack by calling poperror. This must
          be done before returning from the function; otherwise, a
          subsequent call to error will return to an obsolete activa-
          tion record, with unpredictable but unpleasant consequences.

          Error copies the given error message, which is limited to
          ERRLEN characters, into the Osenv.error of the current pro-
          cess, enables interrupts by calling spllo (native only), and
          finally calls nexterror to start invoking the recovery pro-
          cedures currently stacked by waserror. The files
          /os/port/error.h and /emu/error.h offer a wide selection of
          predefined error messages, suitable for almost any occasion.
          The message set by the most recent call to error can be
          obtained within the kernel by examining up->env->error and
          in an application, by using the `%r' directive of sys-
          print(2).

          A complex function can have nested error handlers.  A
          waserror block will follow the acquisition of a resource,
          releasing it on error before calling nexterror, and a
          poperror will precede its release in the normal case.  For
          example:

               void
               outer(Thing *t)
               {
                   qlock(t);
                   if(waserror()){      /* A */
                       qunlock(t);
                       nexterror();
                   }
                   m = mallocz(READSTR, 0);
                   if(m == nil)
                       error(Enomem);
                   if(waserror()){     /* B */
                       free(m);
                       nexterror();    /* invokes A */
                   }
                   inner(t);
                   poperror();         /* pops B */
                   free(m);
                   poperror();         /* pops A */
                   qunlock(t);
               }

               void
               inner(Thing *t)

     Page 2                       Plan 9             (printed 4/19/24)

     ERROR(10.2)                                           ERROR(10.2)

               {
                   if(t->bad)
                       error(Egreg);   /* error() call returns to B */
                   t->valid++;
               }

     SOURCE
          /os/port/proc.c
          /emu/main.c

     CAVEATS
          The description above has many instances of should, will,
          must and must not.

     SEE ALSO
          panic(10.2)

     Page 3                       Plan 9             (printed 4/19/24)