Exception Codes

The /usr/include/sys/except.h file contains a list of code numbers corresponding to the various types of hardware exceptions. When an exception handler is invoked (the return from the setjmpx kernel service is not equal to 0), it is the responsibility of the handler to test the code to ensure that the exception is one the routine can handle.

If it is not an expected code, the exception handler must:

  • Release any resources that would not otherwise be freed (buffers, segment registers, storage acquired using the xmalloc routines)
  • Call the longjmpx kernel service, passing it the exception code as a parameter

Thus, when an exception handler does not recognize the exception for which it has been invoked, it passes the exception on to the next most recent exception handler. This continues until an exception handler is reached that recognizes the code and can handle it. Eventually, if no exception handler can handle the exception, the stack is exhausted and the system default action is taken.

In this manner, a component can allocate resources (after calling the setjmpx kernel service to establish an exception handler) and be assured that the resources will later be released. This ensures the exception handler gets a chance to release those resources regardless of what events occur before the instruction stream (a process- or interrupt-level code) is terminated.

By coding the exception handler to recognize what exception codes it can process rather than encoding this knowledge in the stack entries, a powerful and simple-to-use mechanism is created. Each handler need only investigate the exception code that it receives rather than just assuming that it was invoked because a particular exception has occurred to implement this scheme. The set of exception codes used cannot have duplicates.

Exceptions generated by hardware use one of the codes in the /usr/include/sys/except.h file. However, the longjmpx kernel service can be invoked by any kernel component, and any integer can serve as the exception code. A mechanism similar to the old-style setjmp and longjmp kernel services can be implemented on top of the setjmpx/longjmpx stack by using exception codes outside the range of those used for hardware exceptions.

To implement this old-style mechanism, a unique set of exception codes is needed. These codes must not conflict with either the pre-assigned hardware codes or codes used by any other component. A simple way to get such codes is to use the addresses of unique objects as code values.

For example, a program that establishes an exception handler might compare the exception code to the address of its own entry point. Later on in the calling sequence, after any number of intervening calls to the setjmpx kernel service by other programs, a program can issue a call to the longjmpx kernel service and pass the address of the agreed-on function descriptor as the code. This code is only recognized by a single exception handler. All the intervening ones just clean up their resources and pass the code to the longjmpx kernel service again.

Addresses of functions are not the only possibilities for unique code numbers. For example, addresses of external variables can also be used. By using unigue, system-wide addresses, the problem of code-space collision is transformed into a problem of external-name collision. This problem is easier to solve, and is routinely solved whenever the system is built. By comparison, pre-assigning exception numbers by using #define statements in a header file is a much more cumbersome and error-prone method.