Scope and nesting of exception handlers

Exceptions handlers apply only to the thread they are registered on. In a multi-threaded application, it is possible to have a mixture of threads, some with exception handlers registered, and some without. Program checks and ABENDs occurring on threads without active exception handlers cause the usual ANSI/POSIX signal generation. Program checks and ABENDs occurring on threads with active exception handlers will bypass signal generation and will cause the active exception handler to be invoked.

Exception handlers are are also stack-frame based, much like 31-bit user condition handlers. If function a() registers an exception handler, future program checks and ABENDs will drive that handler, until the handler is de-registered. This includes program checks occurring in a() (after the registration), and in any called functions. Function a() can deregister the handler using __reset_exception_handler(). After this is done, program checks and ABENDs once again cause signals to be raised. If function a() returns without calling __reset_exception_handler() to deregister its handler, the handler will be automatically removed when a() returns.

If function a() registers handler ah(), and calls function b(), program checks and ABENDs in b() will also go to ah(). However, b() can register its own handler, bh(), in which case any program checks and ABENDs in b() or any functions it calls will go to bh(). Exception handlers can be nested in this way as deep as required. If they are not explicitly deregistered by calling __reset_exception_handler(), they are automatically removed when the registering function returns. They are also removed, whenever a longjump-type function (longjmp(), _longjmp(), siglongjmp(), setcontext(), or C++ throw) causes control to jump back past the function that registered the handler. (Example: a() registers handler ah(), and calls b(), which registers handler bh(), and calls c(). Function c() longjumps back into a(). In this case, bh() will be removed, but ah() will remain.)
Note: Whenever a program check or ABEND occurs, no more than one exception handler will ever be driven, even when several nested handlers have been registered. The active handler is the one that was most recently registered, and not de-registered/removed. It will usually be the handler registered by the most deeply-nested routine at the time of the program check or ABEND.

During C++ throw processing, as the Language Environment stack is unwound and destructors for automatic C++ object are invoked, handlers registered by more-deeply nested functions are temporarily bypassed, in case program checks or ABENDs occur in the destructors. Example: a() registers handler ah(), and calls b(). Function b() has a dynamic object with destructor bd(). Function b() calls c(), which has a dynamic object with destructor cd(), and it registers handler ch(). Function c() then calls d(), which registers handler dh(), and then throws a C++ exception that will eventually get caught back in a(). As the C++ destructors are run, program checks/ABENDs in cd() go to handler ch(), and program checks/ABENDs in bd() go to ah(). By the time control resumes in the catch clause in a(), dh() and ch() are gone, and ah() is the active exception handler. This same type of exception handler scoping occurs after pthread_exit() is called and all outstanding C++ dynamic destructors still left on the stack are run.

If a program does pthread_exit() while an exception handler is active, that exception handler remains active while any pthread_keycreate() destructor routines and any pthread_cleanup_push() routines are invoked. These routines can register their own exception handlers, too, if required.

When atexit() routines or C++ static destructors are run, any active exception handlers at the time of the exit() or pthread_exit() have already been removed. If these routines need recovery, thay can register their own exception handlers.