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.