This is exc_handling.c 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. * */ /* */ /* ** ** NAME: ** ** exception_handling.c ** ** FACILITY: ** ** Exceptions ** ** ABSTRACT: ** ** Pthread based exception package support routines. ** ** These support routines help to define a TRY/CATCH exception mechanism ** that runs 'on top of' P1003.4a/D4 pthreads. ** ** The following implementation models all exceptions by converting them ** into pthread "cancel"'s. The package counts on the pthread cancel ** mechanism to maintain the cancel cleanup handler chain; it piggyback ** on the cancel cleanup handler list at the expense of a slightly more ** expensive RAISE(). ** ** The exception currently being processed is recorded in per thread ** data which is set by the excpetion handler package. ** ** Exception handlers execute with general cancellability disabled. ** ** Arbitrary application pthread_cancel's that are not part of a TRY/CATCH ** scoped macro will unwind to the mort recent TRY/CATCH exception handler. ** That is, if an application nests a pthread_cleanup_push/pop() macro ** set inside a TRY/CATCH macro set the pthread cleanup macros outside ** the exception package scope are represented as a "cancel" exception. ** */ #include <pthread.h> #include <signal.h> #include <stdio.h> #include <exc_handling.h> EXCEPTION exc_uninitexc_e; EXCEPTION exc_exquota_e; EXCEPTION exc_insfmem_e; EXCEPTION exc_nopriv_e; EXCEPTION exc_illaddr_e; EXCEPTION exc_illinstr_e; EXCEPTION exc_resaddr_e; EXCEPTION exc_privinst_e; EXCEPTION exc_resoper_e; EXCEPTION exc_aritherr_e; EXCEPTION exc_intovf_e; EXCEPTION exc_intdiv_e; EXCEPTION exc_fltovf_e; EXCEPTION exc_fltdiv_e; EXCEPTION exc_fltund_e; EXCEPTION exc_decovf_e; EXCEPTION exc_subrng_e; EXCEPTION exc_excpu_e; EXCEPTION exc_exfilsiz_e; EXCEPTION exc_SIGTRAP_e; EXCEPTION exc_SIGIOT_e; EXCEPTION exc_SIGUNUSED_e; EXCEPTION exc_SIGIOT_e; EXCEPTION exc_SIGPIPE_e; EXCEPTION exc_unksyncsig_e; EXCEPTION pthread_cancel_e; pthread_key_t _exc_key; /* -------------------------------------------------------------------- */ #ifdef apollo /* * For apollos, tell the compiler to place all static data, declared * within the scope of a function, in a section named exc_pure_data$. * This section will be loaded as a R/O, shared, initialized data section. * All other data, global or statics at the file scope, will be loaded * as R/W, per-process, and zero-filled. */ #pragma HP_SECTION( , exc_pure_data$) #endif /* -------------------------------------------------------------------- */ /* * Make sure NULL is defined for init_once_block */ #ifndef NULL #define NULL 0 #endif static pthread_once_t init_once_block = pthread_once_init; /* -------------------------------------------------------------------- */ static void sync_signal_handler _EXCHND_PROTO_(( int signal, int code, /* struct sigcontext *scp @@ */ void *scp )); static void setup_sync_signal_handlers _EXCHND_PROTO_(( void )); static void destroy_exc _EXCHND_PROTO_(( void *_exc_cur )); static void init_once _EXCHND_PROTO_(( void )); static void set_cur_exc _EXCHND_PROTO_(( EXCEPTION *exc )); /* -------------------------------------------------------------------- */ /* * S Y N C _ S I G N A L _ H A N D L E R * * This synchronous signal handler is already running on the correct * thread stack. A RAISE is all that's required to initiate the unwind. * * Opon receipt of a synchronous signal, a pthread_cancel() is posted * to the thread in which the synchronous signal occurred cancel delivery * will be forced. The cancel initiates a cleanup handler in the context * of the synchronous signal handler. The cleanup handler then does * a _exc_longjmp_np from the context of the signal handler back to * the most recent exception handler. * * NOTE: * * It is assumed that it is okay to do a RAISE from a SYNCHRONOUS signal * handler without any ill effects. While RAISE is doing a couple of * pthread operations, the fact that these whould only performed in the * thread that had the fault due to a synchronous fault in the thread * (i.e. we were in user code, not the pthread library when the fault * occurred) should mean that there are no pthread re-entrency problems. */ static void sync_signal_handler(signal, code, scp) int signal; int code; /* struct sigcontext *scp; @@ */ void *scp; { EXCEPTION *exc; switch (signal) { case SIGILL: exc = &exc_illinstr_e; break; case SIGTRAP: exc = &exc_SIGTRAP_e; break; case SIGUNUSED: exc = &exc_SIGUNUSED_e; break; case SIGFPE: exc = &exc_aritherr_e; break; case SIGBUS: exc = &exc_illaddr_e; break; case SIGSEGV: exc = &exc_illaddr_e; break; case SIGIOT: exc = &exc_SIGIOT_e; break; case SIGPIPE: exc = &exc_SIGPIPE_e; break; default: exc = &exc_unksyncsig_e; break; } RAISE(*exc); } typedef void (*sig_handler_t)(int sig); /* @@ */ /* * S E T U P _ S Y N C _ S I G N A L _ H A N D L E R S * * Setup a signal handler to catch all synchronous signals and convert * them to exceptions. The occurance of a synchronous signal results * in an immediate exception unwind on the stack of the thrad that caused * the signal. */ static void setup_sync_signal_handlers() { /* * Setup synchronous handlers. Note that we get the current state of * each signal and then just change the handler field. Reputed to * be better for some implementations. */ #define SIGACTION(_sig) \ { \ struct sigaction action; \ (void)sigaction((_sig), (struct sigaction *) 0, &action); \ if (action.sa_handler == SIG_DFL) \ action.sa_handler = (sig_handler_t) sync_signal_handler; \ (void)sigaction((_sig), &action, (struct sigaction *) 0); \ } SIGACTION(SIGILL); SIGACTION(SIGTRAP); SIGACTION(SIGIOT); SIGACTION(SIGUNUSED); SIGACTION(SIGFPE); SIGACTION(SIGBUS); SIGACTION(SIGSEGV); SIGACTION(SIGIOT); SIGACTION(SIGPIPE); #undef SIGACTION } /* * D E S T R O Y _ E X C * * Called at thread exit to destroy the thread specific exception state * storage. */ static void destroy_exc(_exc_cur) void *_exc_cur; { free(_exc_cur); } /* * I N I T _ O N C E * * Initialize the exception package. This function is run through pthread_once(). * Create the key for the thread specific exception state. * Setup signal handlers for the process wide asynchronous signals. */ static void init_once() { EXCEPTION_INIT(exc_uninitexc_e); EXCEPTION_INIT(exc_exquota_e); EXCEPTION_INIT(exc_insfmem_e); EXCEPTION_INIT(exc_nopriv_e); EXCEPTION_INIT(exc_illaddr_e); EXCEPTION_INIT(exc_illinstr_e); EXCEPTION_INIT(exc_resaddr_e); EXCEPTION_INIT(exc_privinst_e); EXCEPTION_INIT(exc_resoper_e); EXCEPTION_INIT(exc_aritherr_e); EXCEPTION_INIT(exc_intovf_e); EXCEPTION_INIT(exc_intdiv_e); EXCEPTION_INIT(exc_fltovf_e); EXCEPTION_INIT(exc_fltdiv_e); EXCEPTION_INIT(exc_fltund_e); EXCEPTION_INIT(exc_decovf_e); EXCEPTION_INIT(exc_subrng_e); EXCEPTION_INIT(exc_excpu_e); EXCEPTION_INIT(exc_exfilsiz_e); EXCEPTION_INIT(exc_SIGTRAP_e); EXCEPTION_INIT(exc_SIGIOT_e); EXCEPTION_INIT(exc_SIGUNUSED_e); EXCEPTION_INIT(exc_SIGIOT_e); EXCEPTION_INIT(exc_SIGPIPE_e); EXCEPTION_INIT(exc_unksyncsig_e); EXCEPTION_INIT(pthread_cancel_e); pthread_keycreate(&_exc_key, destroy_exc); } /* * _ E X C _ T H R E A D _ I N I T * * Initialize the exception package for per_thread stuff. */ void _exc_thread_init() { EXCEPTION *_cur_exc; /* * One time initialization for all threads. */ pthread_once( &init_once_block, init_once ); /* * If we already have the thread-specific storage to hold this thread's * "current exception", we're done. */ pthread_getspecific( _exc_key, (void *) &_cur_exc); if (_cur_exc != 0) { return; } /* * Allocate the storage to hold this thread's "current exception". * Initial the current exception to "cancel" so that the thread sees * a cancel exception when its cancelled. (Non-cancel * exceptions--i.e., those raised via RAISE--will set the value to * something else. ENDTRY resets it back to cancel.) */ _cur_exc = (EXCEPTION *) malloc(sizeof(*_cur_exc)); *_cur_exc = pthread_cancel_e; pthread_setspecific(_exc_key, (void *)_cur_exc); /* * Set up signal handlers for this thread. */ setup_sync_signal_handlers(); } /* * S E T _ C U R _ E X C * * Set the thread's current exception to the specified exception. */ static void set_cur_exc(exc) EXCEPTION *exc; { EXCEPTION *_cur_exc; pthread_getspecific(_exc_key, (void *) &_cur_exc); *_cur_exc = *exc; } /* * _ E X C _ R A I S E * * RAISE operation. All exceptions are mapped into a pthread_cancel() * to start the cancel cleanup handler popping. Before starting the unwind, * setup the thread's current exception. * * THIS IS CALLED FROM A SYNCHRONOUS SIGNAL HANDLER (see above). */ void _exc_raise(exc) EXCEPTION *exc; { #define UNHANDLED_EXC_MSG "Unhandled exception; exiting!\n" #ifdef OSF { /* * Detect the case where there are no more cleanup routines on * our stack and if so, cause the process to exit. The reason * we do this is because (a) it seems like not such a bad idea * (unhandled exception => process exit), and (b) if an exception * goes unhandled in the initial thread and we didn't do this, * the initial thread would exit but the process would hang around, * making things pretty confusing for the poor user. * * Unfortunately, there's not a portable way to do this. For OSF/1 * we use knowledge about how "pthread_cleanup_{push,pop}" are * implemented. */ __pthread_cleanup_handler_t **__handler_queue; pthread_getspecific(__pthread_cleanup_handlerqueue, (void *) &__handler_queue); if (__handler_queue != 0 && *__handler_queue == 0) { write(2, UNHANDLED_EXC_MSG, sizeof UNHANDLED_EXC_MSG); abort(); } } #endif set_cur_exc(exc); pthread_cancel( pthread_self() ); pthread_setcancel( CANCEL_ON ); pthread_testcancel(); } /* * E X C _ M A T C H E S * * Return true iff two exceptions match. */ int exc_matches(cur_exc, exc) EXCEPTION *cur_exc; EXCEPTION *exc; { if (cur_exc->kind == exc->kind && (cur_exc->kind == _exc_c_kind_status ? cur_exc->match.value == exc->match.value : cur_exc->match.address == exc->match.address)) return 1; return 0; } /* * E X C _ R E P O R T * * */ void exc_report(exc) EXCEPTION *exc; { fflush(stdout); if (exc->kind == _exc_c_kind_status) { fprintf(stderr, "%%Exception report; value is 0x%x.\n", exc->match.value); } else { fprintf(stderr, "%%Exception report; exception address is %p.\n", exc->match.address); } } /* * _ E X C _ C L E A N U P _ H A N D L E R * * The exception package's standard cancel cleanup handler. * Do a non-local goto using this exeception handler's * exception state block to get back to the exception handler. * * THIS IS CALLED FROM A PTHREAD CLEANUP HANDLER * * NOTE: * * This cleanup handler is invoked within the context of a signal handler. It * assumes that doing this is valid. */ void _exc_cleanup_handler(exc_buf) _exc_buf *exc_buf; { _exc_longjmp(&exc_buf->jb, 1); } /* * _ E X C _ S E T J M P _ P O S T L U D E * * More code that would live in the definition of the _exc_setjmp macro if * we didn't care about code size. */ void _exc_setjmp_postlude(jmpbuf) _exc_jmpbuf *jmpbuf; { #if 0 && ( defined(OSF) || defined(apollo) ) pthread_t current_thread = pthread_self(); /* * The pending_cancel bit in the pthread structure must be set prior * to calling any pthread_set<foo>cancel() functions to avoid taking * undesired calls to the pthread exception handlers. Remember that * the pthread_set<foo>cancel() functions are pthread cancellation * points. */ current_thread->pending_cancel = 0; /* !!! Eh? */ #endif #ifdef HPUX_MAROVICH current_thread->value.cancelled = 0; #endif pthd4_setasynccancel(jmpbuf->setasynccancel); pthread_setcancel(jmpbuf->setcancel); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.