This is exc_handling.h in view mode; [Download] [Up]
/* * * (c) Copyright 1991 OPEN SOFTWARE FOUNDATION, INC. * (c) Copyright 1991 HEWLETT-PACKARD COMPANY * (c) Copyright 1991 DIGITAL EQUIPMENT CORPORATION * To anyone who acknowledges that this file is provided "AS IS" * without any express or implied warranty: * permission to use, copy, modify, and distribute this * file for any purpose is hereby granted without fee, provided that * the above copyright notices and this notice appears in all source * code copies, and that none of the names of Open Software * Foundation, Inc., Hewlett-Packard Company, or Digital Equipment * Corporation be used in advertising or publicity pertaining to * distribution of the software without specific, written prior * permission. Neither Open Software Foundation, Inc., Hewlett- * Packard Company, nor Digital Equipment Corporation makes any * representations about the suitability of this software for any * purpose. * */ /* */ #ifndef EXC_HANDLING_H #define EXC_HANDLING_H /* ** ** NAME: ** ** exc_handling.h ** ** FACILITY: ** ** Exceptions ** ** ABSTRACT: ** ** Pthread based exception package support header file. ** ** This header file defines a TRY/CATCH exception mechanism that runs ** 'on top of' P1003.4a/D4 pthreads. ** ** The following implementation models all exceptions by converting them ** into a "cancel" and counting on pthreads to maintain the cancel cleanup ** handler chain. So, rather than really maintain our own exception ** handler list, we just piggyback on the cancel cleanup handler list ** at the expense of a slightly more expensive RAISE(). There is a per ** thread global "current exception" that is set to the exception that ** is being processed. ** ** Exception handlers execute with general cancellability disabled. ** ** Arbitrary application pthread_cancel(pthread_self()) (i.e. something ** not part of the exception package; not a RAISE) that are delivered ** while in the scope of an exception handler will unwind to the exception ** handler; these "outside" pthread_cancel's are represented as a "cancel" ** exception. ** */ #include <pthread.h> /* @@ */ #include <sys/types.h> #include <setjmp.h> #ifdef __STDC__ # define _EXCHND_PROTO_(x) x #else # define _EXCHND_PROTO_(x) () #endif /* --------------------------------------------------------------------------- */ typedef enum _exc_kind_t { _exc_c_kind_address = 0x02130455, _exc_c_kind_status = 0x02130456 } _exc_kind_t; typedef struct _exc_exception { _exc_kind_t kind; union match_value { int value; struct _exc_exception *address; } match; } EXCEPTION; #define EXCEPTION_INIT(e) { \ (e).kind = _exc_c_kind_address; \ (e).match.address = &(e); \ } #define exc_set_status(e, s) ( \ (e)->match.value = (s), \ (e)->kind = _exc_c_kind_status \ ) extern pthread_key_t _exc_key; /* --------------------------------------------------------------------------- */ typedef struct _exc_jmpbuf { unsigned setcancel; /* state of general cancel delivery mode */ unsigned setasynccancel; /* state of async cancel delivery mode */ jmp_buf jb; } _exc_jmpbuf; /* * An exception handler buffer (state block). */ typedef struct _exc_buf { _exc_jmpbuf jb; } _exc_buf; /* --------------------------------------------------------------------------- */ int exc_matches _EXCHND_PROTO_(( EXCEPTION *cur_exc, EXCEPTION *exc )); void exc_report _EXCHND_PROTO_(( EXCEPTION *exc )); void _exc_thread_init _EXCHND_PROTO_(( void )); void _exc_raise _EXCHND_PROTO_(( EXCEPTION *exc )); void _exc_cleanup_handler _EXCHND_PROTO_(( _exc_buf *exc_buf )); void _exc_setjmp_postlude _EXCHND_PROTO_(( _exc_jmpbuf *jmpbuf )); /* --------------------------------------------------------------------------- */ /* * The following exception is used to report an attempt to raise an * uninitialized exception object. It should never be raised by any other * code. */ extern EXCEPTION exc_uninitexc_e; /* Uninitialized exception */ /* * The following exceptions are common error conditions which may be raised * by the thread library or any other facility following this exception * specification. */ extern EXCEPTION exc_exquota_e; /* Exceeded quota */ extern EXCEPTION exc_insfmem_e; /* Insufficient memory */ extern EXCEPTION exc_nopriv_e; /* No privilege */ /* * The following exceptions describe hardware or operating system error * conditions that are appropriate for most hardware and operating system * platforms. These are raised by the exception facility to report operating * system and hardware error conditions. */ extern EXCEPTION exc_illaddr_e; /* Illegal address */ extern EXCEPTION exc_illinstr_e; /* Illegal instruction */ extern EXCEPTION exc_resaddr_e; /* Reserved addressing mode */ extern EXCEPTION exc_privinst_e; /* Privileged instruction */ extern EXCEPTION exc_resoper_e; /* Reserved operand */ extern EXCEPTION exc_aritherr_e; /* Arithmetic error */ extern EXCEPTION exc_intovf_e; /* Integer overflow */ extern EXCEPTION exc_intdiv_e; /* Integer divide by zero */ extern EXCEPTION exc_fltovf_e; /* Floating overflow */ extern EXCEPTION exc_fltdiv_e; /* Floating divide by zero */ extern EXCEPTION exc_fltund_e; /* Floating underflow */ extern EXCEPTION exc_decovf_e; /* Decimal overflow */ extern EXCEPTION exc_subrng_e; /* Subrange */ extern EXCEPTION exc_excpu_e; /* Exceeded CPU quota */ extern EXCEPTION exc_exfilsiz_e; /* Exceeded file size */ /* * The following exceptions correspond directly to UNIX synchronous * terminating signals. This is distinct from the prior list in that those * are generic and likely to have equivalents on most any operating system, * whereas these are highly specific to UNIX platforms. */ extern EXCEPTION exc_SIGTRAP_e; /* SIGTRAP received */ extern EXCEPTION exc_SIGIOT_e; /* SIGIOT received */ extern EXCEPTION exc_SIGUNUSED_e; /* SIGUNUSED received */ extern EXCEPTION exc_SIGIOT_e; /* SIGIOT received */ extern EXCEPTION exc_SIGPIPE_e; /* SIGPIPE received */ extern EXCEPTION exc_unksyncsig_e; /* Unknown synchronous signal */ /* * The following exception is raised in the target of a cancel */ extern EXCEPTION pthread_cancel_e; /* * The following aliases exist for backward compatibility with CMA. */ #define cma_e_alerted pthread_cancel_e /* --------------------------------------------------------------------------- */ /* * The following aliases exist for backward compatibility with CMA. */ #define exc_e_uninitexc exc_uninitexc_e #define exc_e_illaddr exc_illaddr_e #define exc_e_exquota exc_exquota_e #define exc_e_insfmem exc_insfmem_e #define exc_e_nopriv exc_nopriv_e #define exc_e_illinstr exc_illinstr_e #define exc_e_resaddr exc_resaddr_e #define exc_e_privinst exc_privinst_e #define exc_e_resoper exc_resoper_e #define exc_e_SIGTRAP exc_SIGTRAP_e #define exc_e_SIGIOT exc_SIGIOT_e #define exc_e_SIGUNUSED exc_SIGUNUSED_e #define exc_e_aritherr exc_aritherr_e #define exc_e_SIGIOT exc_SIGIOT_e #define exc_e_SIGPIPE exc_SIGPIPE_e #define exc_e_excpu exc_excpu_e #define exc_e_exfilsiz exc_exfilsiz_e #define exc_e_intovf exc_intovf_e #define exc_e_intdiv exc_intdiv_e #define exc_e_fltovf exc_fltovf_e #define exc_e_fltdiv exc_fltdiv_e #define exc_e_fltund exc_fltund_e #define exc_e_decovf exc_decovf_e #define exc_e_subrng exc_subrng_e #define exc_e_accvio exc_e_illaddr #define exc_e_SIGILL exc_e_illinstr #define exc_e_SIGFPE exc_e_aritherr #define exc_e_SIGBUS exc_e_illaddr #define exc_e_SIGSEGV exc_e_illaddr #define exc_e_SIGXCPU exc_e_excpu #define exc_e_SIGXFSZ exc_e_exfilsiz /* --------------------------------------------------------------------------- */ /* * _ E X C _ S E T J M P / L O N G J M P * * Similar to setjmp(2) and longjmp(2) these macros also save/restore * the cancel state of a canceled thread providing a means to "handle" * a cancel. * * These macros gives us the basic mechanism to create in-line exception * handlers (i.e. TRY / CATCH) and integrate the exception package with * the pthread cancel mechanism. * * _exc_longjmp() must be defined to be legal to use from within a * pthread cleanup handler. * * Notes: * * (1) _exc_longjmp() is NOT integrated with pthread cleanup handlers. * Specifically, calling _exc_longjmp() will not induce behaviour as * if a cancel had been delivered to the thread. The intent is that * _exc_{setjmp,longjmp}() are to be used by exception package developers. * * (2) cancels must be turned off prior to calling setjmp to set up the * jmpbuf for the exception handler. Therefore the setcancel calls around * setjmp are not clearly separable into an exception prelude function. */ #ifndef CANCEL_ON #define CANCEL_ON ((int) 1) #define CANCEL_OFF ((int) 0) #endif #define _exc_setjmp(jmpbuf, i) \ _exc_thread_init(); \ (jmpbuf)->setasynccancel = pthread_setasynccancel(CANCEL_OFF); \ (jmpbuf)->setcancel = pthread_setcancel(CANCEL_OFF); \ *(i) = _setjmp((jmpbuf)->jb); \ if( *(i) == 0 ) { \ if ((jmpbuf)->setcancel != CANCEL_OFF ) \ pthread_setcancel(CANCEL_ON); \ if ((jmpbuf)->setasynccancel != CANCEL_OFF) \ pthread_setasynccancel(CANCEL_ON); \ } \ if (*(i) != 0) \ _exc_setjmp_postlude((_exc_jmpbuf *) jmpbuf) #define _exc_longjmp(jmpbuf, val) _longjmp((jmpbuf)->jb, (val)) /* --------------------------------------------------------------------------- */ /* * T R Y * * Define the beginning of an exception handler scope and the non-exception * code path. * * - Push a cancel cleanup handler for this exception handler * - Arrange it so that this cancel cleanup handler gets us back to this * code when an exception occurs (_exc_setjmp). * - If an exception occurred, get a handle to the thread's "current * exception" value (so CATCH clauses can RERAISE the exception). * * Note that the rough schema for the exception handler is: * * do { * pthread_cleanup_push("routine that will longjmp back here"); * val = setjmp(...); * if (val == 0) * ...normal code... * else * ...exception code... * ...finally code... * if ("exception happened") * if ("exception handled") * break; * else * ...re-raise exception... * pthread_cleanup_pop(...); * } while (0); * * Exceptions are raised by doing a pthread_cancel against one's self * and then doing a pthread_testcancel. This causes the topmost cleanup * routine to be popped and called. This routine (_exc_cleanup_handler) * longjmp's back to the exception handler. This approach means we can * leverage off the fact that the push/pop routines are maintaining some * per-thread state (hopefully [but likely not] more efficiently than * we could ourselves). We need this state to string together the dynamic * stack of exception handlers. * * Most of this trickery is so we can avoid doing the pop in the "exception * handled" path (since the cleanup routine will already have been popped). * (We also need to keep the push and pop appropriately lexically scoped * per the pthread spec.) * * Note that there's some tricky stuff that deals with the case of an * exception being raised in a FINALLY clause that was entered in the * normal (i.e., non-exception) case. The problem is that we haven't * yet popped the current cleanup routine so when the exception gets * raised, we execute it and longjmp back to the CURRENT exception handler, * not the next one. We deal with this by initializing a flag * (_exc_in_finally) to "false" and set it to "true" in the FINALLY clause. * If we ever jump back and see the flag is "true", we know we should * just re-raise right away. */ #define TRY \ do \ { \ volatile _exc_buf _eb; \ EXCEPTION *_exc_cur; \ volatile char _exc_cur_handled = 0; \ volatile char _exc_in_finally = 0; \ volatile int _setjmp_res; \ \ pthread_cleanup_push((cleanup_t)_exc_cleanup_handler, (void *) &_eb); \ _exc_setjmp(&_eb.jb, (volatile int *) &_setjmp_res); \ if (_setjmp_res != 0) \ { \ pthread_getspecific(_exc_key, (void *) &_exc_cur); \ if (_exc_in_finally) \ _exc_raise(_exc_cur); \ } \ if (_setjmp_res == 0) \ { \ /* normal code here */ /* --------------------------------------------------------------------------- */ /* * C A T C H ( e x c ) * * Define the beginning of an exception handler scope for the exception * "exc". The exception handler code must either "fall through" to * ENDTRY (indicating that the exception has been handled and to resume * execution after the ENDTRY), RERAISE the current exception or RAISE * a different exception (in both cases, propagating an unwind to the next * higher exception handler). */ #define CATCH(exc) \ } \ else if (exc_matches(_exc_cur, &(exc))) \ { \ _exc_cur_handled = 1; \ /* exception code here */ /* * C A T C H _ A L L * * Define the beginning of an exception handler scope for any exception * not explicitly caught by a CATCH(exc). Everything else is just like * CATCH(exc). */ #define CATCH_ALL \ } \ else \ { \ EXCEPTION *THIS_CATCH = _exc_cur; \ _exc_cur_handled = 1; \ /* exception code here */ /* --------------------------------------------------------------------------- */ /* * F I N A L L Y * * Define the beginning of a code block that is to be executed regardless * of whether or not an exception occurs. FINALLY should NOT be used * if a CATCH or CATCH_ALL is used. */ #define FINALLY \ } \ { \ _exc_in_finally = 1; \ /* user finally code here */ /* --------------------------------------------------------------------------- */ /* * E N D T R Y * * Terminate an exception handler scope. * * We can reach the ENDTRY under several conditions. Note that we will * not reach here if an exception occurred, was caught and was explicitly * reraised. * * If we got here because things went fine (i.e., there was no exception) * then we need to pop the cancel cleanup handler and we're done (fall * out the bottom). * * If we got here because an exception occurred AND the exception was * NOT handled, we just then we propagate the exception to the next higher * exception handler (no need to restore the original cancel state as * the induced _exc_longjmp() will reset the state). * * If we got here because an exception occurred (i.e., the cancel cleanup * handler was implicitly popped) AND the exception WAS handled, we just * want to break out (NOT do the pop). Reset the current exception to * "cancel" so that we can detect an unwind from outside of the exception * package (i.e., via the thread's being cancelled). */ #define ENDTRY \ } \ if (_setjmp_res != 0) \ { \ if (! _exc_cur_handled) \ _exc_raise(_exc_cur); \ *_exc_cur = pthread_cancel_e; \ break; \ } \ pthread_cleanup_pop(0); \ } while (0); \ /* End of exception handler scope; continue execution */ /* * R A I S E ( e x c ) * * Raise an exception - i.e. initiate an unwind to the next * exception handler. */ #define RAISE(exc) _exc_raise(&exc) /* --------------------------------------------------------------------------- */ /* * R E R A I S E * * Raise the current exception - i.e. initiate an unwind to next exception * handler. Note: RERAISE is legal only within a CATCH or a CATCH_ALL. * Note _exc_cur may be NULL due to an "outside" pthread_cancel(). */ #define RERAISE _exc_raise(_exc_cur) /* --------------------------------------------------------------------------- */ #endif
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.