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 12/21/25)
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 12/21/25)
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 12/21/25)