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/22/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 12/22/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 12/22/24)