ftp.nice.ch/Attic/openStep/implementation/gnustep/sources/alpha-snapshots/pthreads.0.9.2.tgz#/pthreads-0.9.2/dcelib/exc_handling.c

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.