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.