home *** CD-ROM | disk | FTP | other *** search
- /*
- ** Exception Library -- General exception handling for ANSI C programs
- **
- ** Copyright (C) 1992 Computational Vision and Active Perception Lab. (CVAP),
- ** Royal Institute of Technology, Stockholm.
- **
- ** This library is free software; you can redistribute it and/or
- ** modify it under the terms of the GNU Library General Public
- ** License as published by the Free Software Foundation; either
- ** version 2 of the License, or (at your option) any later version.
- **
- ** This library is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- ** Library General Public License for more details.
- **
- ** You should have received a copy of the GNU Library General Public
- ** License along with this library (see COPYING-LIB); if not, write to
- ** the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
- ** USA.
- **
- ** Written by
- **
- ** Harald Winroth, Matti Rendahl
- ** Computational Vision and Active Perception Laboratory
- ** Royal Institute of Technology
- ** S-100 44 Stockholm
- ** Sweden
- **
- ** Report bugs to candela-bug@bion.kth.se, and direct all inquiries to
- ** candela@bion.kth.se.
- **
- */
-
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <signal.h>
-
- #include <exception/exception.h>
-
- /* Place holders (to generate unique addresses) */
- int exc_any;
- int exc_undefined;
-
- /* Data associated with the current (pending) exception */
- static void *current_exception = EXC_UNDEFINED;
- static void *current_type = EXC_UNDEFINED;
-
- /* Pointer to current jump buffer */
- volatile excBuf *exc_current_buf = NULL;
-
- /* Dummy variables */
- volatile excBuf exc_buf; /* Not used, but must exist */
- char exc_in_try = 0; /* Must have size 1 and value 0 */
- char exc_in_tret = 0; /* Must have size 1 and value 0 */
- char exc_in_unwind = 0; /* Must have size 1 and value 0 */
- char exception; /* Not used, but must exist */
-
- /* Callback list */
- typedef struct
- {
- excCallback cb; /* registered callback */
- excCallbackTag tags; /* mask */
- void *cb_data; /* data assoc. with the callback */
-
- } excCallbackRec;
-
- static excCallbackRec cb_list[EXC_MAX_CALLBACKS];
- static unsigned int cb_list_len = 0;
-
- /* Handler list */
- typedef struct
- {
- void *e; /* & SOME_EXCEPTION (or NULL) */
- unsigned long e_sz; /* sizeof (SOME_EXCEPTION) */
- excHandler h; /* registered handler */
- void *h_data; /* user data */
-
- } excHandlerRec;
-
- static excHandlerRec h_list[EXC_MAX_HANDLERS];
- static unsigned int h_list_len = 0;
-
- /*
- * Private functions
- */
-
- static void check_recovered (volatile excBuf *buf);
- static void call_callbacks (volatile excBuf *buf, excCallbackTag tag);
- static void call_handlers (void *e, void *e_type);
-
- /*
- * Functions for internal error handling. These are private, but since they
- * are used in macro expansions, they must be exported.
- */
-
- void exc_vfatal (char *format, va_list list)
- {
- fprintf (stderr, "exception: ");
- vfprintf (stderr, format, list);
- fprintf (stderr, "\n");
-
- exit (1);
- }
-
- void exc_fatal (char *format, ...)
- {
- va_list list;
-
- if (format)
- {
- va_start (list, format);
- exc_vfatal (format, list);
- va_end (list);
- }
-
- exit (1);
- }
-
- void exc_panic (void)
- {
- fprintf (stderr, "exception: exception system panicked--aborting \
- (core dumped).\n");
-
- abort ();
- }
-
- void exc_assertion_failed (char *file, int line)
- {
- exc_fatal ("%s:%d: assertion failed", file, line);
- }
-
- /*
- * Memory management. Also used in other exception lib source files,
- * and therefore exported.
- */
-
- void *exc_malloc (unsigned long size)
- {
- void *s = malloc (size);
- if (!s) exc_fatal ("exc_malloc: cannot allocated %lu bytes", size);
- return s;
- }
-
- void *exc_calloc (unsigned long count, unsigned long size)
- {
- void *s = calloc (count, size);
- if (!s) exc_fatal ("exc_calloc: cannot allocated %lu bytes", count*size);
- return s;
- }
-
- void *exc_realloc (void *s, unsigned long size)
- {
- s = s ? realloc (s, size) : malloc (size);
- if (!s) exc_fatal ("exc_realloc: cannot allocated %lu bytes", size);
- return s;
- }
-
- void exc_free (void *s)
- {
- if (s) free (s);
- }
-
- char *exc_strdup (char *s)
- {
- return strcpy (exc_malloc (strlen (s) + 1), s);
- }
-
- /*
- * Signals
- */
-
- #if defined(SVR4) || defined(SYSV) || defined(_SYSV_)
-
- static sigset_t saved_mask;
-
- int exc_sigemptyset (void)
- {
- return sigemptyset (&saved_mask);
- }
-
- int exc_sigfillset (void)
- {
- return sigfillset (&saved_mask);
- }
-
- int exc_sigaddset (int signo)
- {
- return sigaddset (&saved_mask, signo);
- }
-
- int exc_sigdelset (int signo)
- {
- return sigdelset (&saved_mask, signo);
- }
-
- int exc_sigismember (int signo)
- {
- return sigismember (&saved_mask, signo);
- }
-
- #else
-
- static int saved_mask;
-
- int exc_sigemptyset (void)
- {
- saved_mask = 0;
- return 0;
- }
-
- int exc_sigfillset (void)
- {
- saved_mask = ~(int)0;
- return 0;
- }
-
- int exc_sigaddset (int signo)
- {
- saved_mask |= sigmask (signo);
- return 0;
- }
-
- int exc_sigdelset (int signo)
- {
- saved_mask &= ~sigmask (signo);
- return 0;
- }
-
- int exc_sigismember (int signo)
- {
- return (saved_mask & sigmask (signo)) ? 1 : 0;
- }
-
- #endif
-
- /*
- * Macros for blocking all signal temporarily
- */
-
- #if defined(SVR4) || defined(SYSV) || defined(_SYSV_)
-
- #define BLOCK_SIGNALS(VAR) \
- sigset_t _filled_mask; \
- sigset_t VAR; \
- sigfillset (&_filled_mask); \
- if (sigprocmask (SIG_SETMASK, &_filled_mask, &VAR)) \
- exc_fatal ("BLOCK_SIGNALS: sigprocmask() failed.")
-
- #define UNBLOCK_SIGNALS(VAR) \
- if (sigprocmask (SIG_SETMASK, &VAR, 0)) \
- exc_fatal ("UNBLOCK_SIGNALS: sigprocmask() failed.")
-
- #else
-
- #define BLOCK_SIGNALS(VAR) \
- int VAR = sigsetmask (~(int)0)
-
- #define UNBLOCK_SIGNALS(VAR) \
- sigsetmask (VAR)
-
- #endif
-
- /*
- * Stack of data for callbacks.
- *
- * Note: The list of blocks and the blocks it contains are never reallocated.
- * This is important because signals may cause pointer addresses to be invalid
- * between any two machine instructions.
- */
-
- #define MAX_BLOCKS (8 * sizeof (unsigned int))
-
- static void **try_data_stack[MAX_BLOCKS]; /* Note: Inited to NULL */
- static unsigned int try_data_sp = 0;
-
- /* acc requires the (void **) cast below, it should not be necessary */
- #define TRY_DATA_ADDR(buf, i) \
- (((i) < (EXC_LOCAL_TRY_DATA_SIZE)) ? \
- (void **) &(buf)->local_try_data[i] : \
- try_data_stack_addr ((buf)->try_data_sp+(i)-(EXC_LOCAL_TRY_DATA_SIZE)))
-
- static void try_data_stack_alloc (unsigned int b)
- {
- BLOCK_SIGNALS(tmp_mask);
-
- if (!try_data_stack[b])
- try_data_stack[b] = (void **)
- exc_malloc ((((unsigned int) 1) << b) * sizeof(void *));
-
- UNBLOCK_SIGNALS (tmp_mask);
- }
-
- static void **try_data_stack_addr (unsigned int i)
- {
- unsigned int block, offs, mask;
- void **bptr;
-
- for (i++, block=MAX_BLOCKS-1, mask=((unsigned int) 1)<<block;
- (i & mask) == 0;
- mask >>= 1, block--);
- offs = i & ~mask;
-
- if ((bptr = try_data_stack[block]) == NULL)
- {
- try_data_stack_alloc (block);
- bptr = try_data_stack[block];
- }
-
- return bptr+offs;
- }
-
- /*
- * Public
- */
-
- void exc_breakpoint (void)
- {
- /*
- * Users can place breakpoints in this dummy function to break before
- * the longjmp is taken, no matter which exc_*() function actually
- * contains the longjmp.
- */
-
- return;
- }
-
- void exc_throw_typed (void *e, void *e_type)
- {
- volatile excBuf *buf;
-
- #if defined(SVR4) || defined(SYSV) || defined(_SYSV_)
- sigset_t filled_mask;
- sigset_t cur_mask;
- if (sigfillset (&filled_mask))
- exc_fatal ("exc_throw_typed: sigfillmask() failed.");
- if (sigprocmask (SIG_SETMASK, &filled_mask, &cur_mask))
- exc_fatal ("exc_throw_typed: sigprocmask() failed.");
- #else
- int filled_mask = ~(int)0;
- int cur_mask = sigsetmask (filled_mask);
- #endif
-
- exc_breakpoint (); /* Dummy function */
-
- if (e == EXC_UNDEFINED)
- exc_fatal ("exc_throw_typed: no exception specified");
- if (e_type == EXC_UNDEFINED)
- exc_fatal ("exc_throw_typed: no exception type specified");
-
- if (current_exception == EXC_UNDEFINED)
- saved_mask = cur_mask;
-
- current_exception = e;
- current_type = e_type;
-
- /* Find first unused jump buffer (target TRY-block) */
- for (buf=exc_current_buf; buf && buf->in_unwind; buf=buf->prev);
-
- if (!buf)
- call_handlers (e, e_type);
-
- /* Execute throw callbacks outside the current TRY scope */
- buf->in_unwind = 1;
- call_callbacks (buf, excThrowCallback);
-
- exc_current_buf = buf;
- try_data_sp = buf->try_data_sp;
- EXC_LONGJMP (buf->buf, 1);
- }
-
- void exc_throw (void *e)
- {
- exc_throw_typed (e, e); /* Let `e` be its own (unique) type */
- }
-
- void exc_rethrow (void)
- {
- if (current_exception == EXC_UNDEFINED)
- exc_fatal ("exc_rethrow: no pending exception");
-
- exc_throw_typed (current_exception, current_type);
- }
-
- void *exc_exception (void)
- {
- if (current_exception == EXC_UNDEFINED)
- exc_fatal ("exc_exception: no exception thrown");
-
- return current_exception;
- }
-
- void *exc_type (void)
- {
- if (current_type == EXC_UNDEFINED)
- exc_fatal ("exc_exception: no exception thrown");
-
- return current_type;
- }
-
- int exc_in_domain (void *e, void *domain, unsigned long sizeof_domain)
- {
- return (e >= domain && e < (void *) (((char *) domain) + sizeof_domain));
- }
-
- int exc_equal (void *e1, void *e2)
- {
- return e1 == e2;
- }
-
- /*
- * Functions used in macro expansions and therefore exported. Must not be
- * called explicitly from application programs.
- */
-
- void exc_begin (volatile excBuf *buf, int inside_try)
- {
- EXC_ASSERT (exc_current_buf != buf && (exc_current_buf || !inside_try));
-
- buf->prev = exc_current_buf;
- buf->prev_dynamic = inside_try ? exc_current_buf->prev_dynamic
- : exc_current_buf;
-
- buf->level = exc_current_buf ? exc_current_buf->level+1 : 0;
- buf->cb_list_len = cb_list_len;
- buf->try_data_sp = try_data_sp;
- buf->in_unwind = 0;
-
- if (buf->cb_list_len > (EXC_LOCAL_TRY_DATA_SIZE))
- try_data_sp = buf->try_data_sp + buf->cb_list_len -
- (EXC_LOCAL_TRY_DATA_SIZE);
-
- call_callbacks (buf, excBeginCallback);
- }
-
- void exc_end (volatile excBuf *buf)
- {
- EXC_ASSERT (exc_current_buf == buf);
-
- if (buf->in_unwind)
- check_recovered (buf->prev);
- else
- call_callbacks (buf, excEndCallback);
-
- exc_current_buf = buf->prev;
- try_data_sp = buf->try_data_sp; /* Must be reset *after* exc_current_buf */
- }
-
- int exc_tret (volatile excBuf *buf, int in_tret, char *file, int line)
- {
- if (!in_tret)
- exc_ret_in_try_err (file, line);
-
- EXC_ASSERT (exc_current_buf == buf);
-
- while (exc_current_buf != buf->prev_dynamic)
- exc_end (exc_current_buf);
-
- return 0;
- }
-
- static void check_recovered (volatile excBuf *buf)
- {
- for (; buf; buf=buf->prev)
- if (buf->in_unwind)
- return;
-
- /* The program has recovered from the pending exception */
- current_exception = EXC_UNDEFINED;
- current_type = EXC_UNDEFINED;
-
- call_callbacks (buf, excRecoverCallback);
-
- #if defined(SVR4) || defined(SYSV) || defined(_SYSV_)
- if (sigprocmask (SIG_SETMASK, &saved_mask, 0))
- exc_fatal ("exc_end: check_recovered: sigprocmask() failed.");
- #else
- sigsetmask (saved_mask);
- #endif
- }
-
- /*
- * Error functions used in macro expansions and therefore exported. Must not
- * be called explicitly from application programs.
- */
-
- void exc_break_in_try_err (char *file, int line)
- {
- exc_fatal ("%s:%d: illegal break statement in TRY", file, line);
- }
-
- void exc_rethrow_err (char *file, int line)
- {
- exc_fatal ("%s:%d: THROW(exception) used outside unwind form",
- file, line);
- }
-
- int exc_catch_outside_unwind_err (char *file, int line)
- {
- exc_fatal ("%s:%d: CATCH outside unwind form", file, line);
- return 0; /* Not reached */
- }
-
- void exc_ret_in_try_err (char *file, int line)
- {
- exc_fatal ("%s:%d: illegal return statement in TRY", file, line);
- }
-
- int exc_tret_err (char *file, int line)
- {
- exc_fatal ("%s:%d: tryreturn() used outside TRY", file, line);
- return 0; /* Not reached */
- }
-
- /*
- * Callbacks
- */
-
- void exc_install_callback (excCallbackTag tags, excCallback cb, void *cb_data)
- {
- BLOCK_SIGNALS (tmp_mask);
-
- if (cb_list_len == EXC_MAX_CALLBACKS)
- exc_fatal ("exc_install_callback: Too many (>%d) callbacks installed",
- EXC_MAX_CALLBACKS);
-
- cb_list[cb_list_len].cb = cb;
- cb_list[cb_list_len].tags = tags;
- cb_list[cb_list_len].cb_data = cb_data;
- cb_list_len++;
-
- UNBLOCK_SIGNALS (tmp_mask);
- }
-
- void exc_remove_callback (excCallbackTag tags, excCallback cb, void *cb_data)
- {
- unsigned int i;
-
- for (i=cb_list_len; i-- > 0;)
- if (cb_list[i].cb == cb && cb_list[i].tags == tags &&
- cb_list[i].cb_data == cb_data)
- {
- cb_list[i].tags = excNoCallback;
- return;
- }
-
- exc_fatal ("exc_remove_callback: no such callback installed");
- }
-
- static void call_callbacks (volatile excBuf *buf, excCallbackTag tag)
- {
- /*
- Careful here: Avoid infinite loops--a callback may do THROW or TRY.
- Also, excBeginCallback and excEndCallback calls are not protected from
- async. THROWS (caused by signals).
- */
-
- unsigned int i;
-
- switch (tag)
- {
- case excBeginCallback:
- for (i=0; i<buf->cb_list_len; i++)
- if (cb_list[i].tags & tag)
- (*(cb_list[i].cb)) (tag, cb_list[i].cb_data,
- TRY_DATA_ADDR (buf, i));
- return;
-
- case excEndCallback:
- case excThrowCallback:
- /*
- For excEndCallback calls:
- No try-data are discarded until all excEndCallback calls have
- completed successfully. An explicit or async THROW() should
- trigger excThrowCallback calls for the same try-data.
-
- Watch out for destructive effects in excEndCallback calls which
- may cause a subsequent excThrowCallback call with the same
- try-data to crash.
-
- For excThrowCallback calls:
- Async throws are disabled here (blocked in exc_throw_typed()).
- Explicit throws are allowed and will cause execution to continue
- in the unwind-block of the enclosing TRY() (thus skipping the
- rest of the callbacks).
- */
-
- for (i = cb_list_len; i-- > buf->cb_list_len;)
- if (cb_list[i].tags & tag)
- (*(cb_list[i].cb))(tag, cb_list[i].cb_data, (void **)NULL);
-
- for (i = buf->cb_list_len; i-- > 0;)
- if (cb_list[i].tags & tag)
- (*(cb_list[i].cb))(tag, cb_list[i].cb_data,
- TRY_DATA_ADDR (buf, i));
- return;
-
- case excRecoverCallback:
- for (i=0; i<cb_list_len; i++)
- if (cb_list[i].tags & tag)
- (*(cb_list[i].cb))(tag, cb_list[i].cb_data, (void **)NULL);
-
- return;
-
- default:
- exc_fatal ("call_callbacks: illegal tag or combination of tags");
- }
- }
-
- /*
- * Handlers
- */
-
- void exc_install_handler (void *e, unsigned long e_sz, excHandler h,
- void *h_data)
- {
- BLOCK_SIGNALS (tmp_mask);
-
- if (h_list_len == EXC_MAX_HANDLERS)
- exc_fatal ("exc_install_handler: Too many (>%d) handlers installed",
- EXC_MAX_HANDLERS);
-
- /* Note: Users are allowed to use NULL instead of EXC_ANY */
- h_list[h_list_len].e = e ? e : EXC_ANY;
- h_list[h_list_len].e_sz = e_sz;
- h_list[h_list_len].h = h;
- h_list[h_list_len].h_data = h_data;
- h_list_len++;
-
- UNBLOCK_SIGNALS (tmp_mask);
- }
-
- void exc_remove_handler (void *e, unsigned long e_sz, excHandler h,
- void *h_data)
- {
- unsigned int i;
- static char slot_not_used; /* Generates a unique, private address */
-
- for (i=h_list_len; i>0; i--)
- if (h_list[i-1].e != &slot_not_used
- && h_list[i-1].e == e
- && h_list[i-1].e_sz == e_sz
- && h_list[i-1].h == h
- && h_list[i-1].h_data == h_data)
- {
- BLOCK_SIGNALS (tmp_mask);
-
- h_list[i-1].e = &slot_not_used;
- h_list[i-1].e_sz = 0;
- h_list[i-1].h = NULL;
- h_list[i-1].h_data = NULL;
-
- UNBLOCK_SIGNALS (tmp_mask);
- return;
- }
-
- exc_fatal ("exc_remove_handler: no such handler installed");
- }
-
- static void call_handlers (void *e, void *e_type)
- {
- /*
- The list of handlers is search (in reversed installation order) for
- a matching handler, say H, and calls it. If H returns, we exit with
- exc_fatal(NULL) (i.e. exit(1)). If H throws, the list of handlers will
- be search again, starting with the handler installed before H. If no
- matching handler can be found, we call exc_fatal() with an informative(?)
- text.
-
- Note: Since handlers may throw, we must remove them from the list BEFORE
- calling them, to prevent infinite loops.
- */
-
- /*
- Should signals be blocked when the handlers are executed? If not, error
- message print-outs could be terminated with (e.g.) ctrl-c, causing the
- previously installed handler to be called. Have to think about this...
- */
-
- BLOCK_SIGNALS (tmp_mask);
-
- EXC_ASSERT (e != EXC_UNDEFINED && e_type != EXC_UNDEFINED);
-
- while (h_list_len > 0)
- {
- unsigned int i = --h_list_len;
-
- if ((exc_in_domain (e, h_list[i].e, h_list[i].e_sz)
- || h_list[i].e == EXC_ANY)
- && h_list[i].h)
- {
- int ret_status = (*h_list[i].h) (e, e_type, h_list[i].h_data);
- UNBLOCK_SIGNALS (tmp_mask);
- exit (ret_status);
- }
- }
-
- UNBLOCK_SIGNALS (tmp_mask);
- exc_fatal ("exception not catched and no handler to call, terminating...");
- }
-