This is the mail archive of the
gdb-patches@sourceware.cygnus.com
mailing list for the GDB project.
RFA: top.c: new catch_errors + cleanup interface
- To: gdb-patches at sourceware dot cygnus dot com
- Subject: RFA: top.c: new catch_errors + cleanup interface
- From: Nick Duffek <nsd at cygnus dot com>
- Date: Tue, 11 Apr 2000 22:48:59 -0400
The appended patch aims to relieve the tedium of protecting against quit
and error longjmps.
Normally, when calling a function during which an error or quit might
occur, we do some or all of the following:
1. Create a wrapper function with the prototype expected by
catch_errors(), organize parameter and return values into a structure
that can be passed to catch_errors(), and call the original function
via catch_errors(wrapper_function).
2. Maybe add the wrapper function to wrapper.c and wrapper.h.
3. Create a cleanup function to free memory, restore state, etc. in the
event that an error or quit occurs, add that function to the cleanup
chain, and call do_cleanups().
This patch implements the following alternative:
CATCH_BEGIN(<msg>, <mask>);
... code that might return nonlocally ...
CATCH_CLEANUP;
... code that always gets executed ...
CATCH_END;
or just
CATCH_BEGIN(<msg>, <mask>);
... code that might return nonlocally ...
CATCH_END;
These sequences cause the following to occur:
(1) The code immediately following CATCH_BEGIN is executed in the
normal way, subject to premature completion as a result of a
nonlocal return. If <msg> is non-null and a nonlocal return
occurs, <msg> is displayed before error or quit messages.
(2) If CATCH_CLEANUP is present, the code following it is executed.
(3) If a nonlocal return occurred in (1) for a reason not
specified in <mask>, the nonlocal return is performed;
otherwise, execution resumes after CATCH_END.
Between CATCH_CLEANUP and CATCH_END, the macro CATCH_CAUGHT evaluates
to the appropriate enum return_reason if a nonlocal return occurred
and to 0 otherwise.
No regressions are evident on i686-pc-linux-gnu or sparc-sun-solaris2.6,
and the new interface works correctly in a thread.c bugfix that I expect
to post tomorrow.
Comments?
Nick Duffek
nsd@cygnus.com
[patch follows]
Index: defs.h
===================================================================
RCS file: /cvs/src/src/gdb/defs.h,v
retrieving revision 1.13
diff -u -r1.13 defs.h
--- defs.h 2000/03/30 18:54:28 1.13
+++ defs.h 2000/04/11 21:46:58
@@ -38,6 +38,8 @@
#include <unistd.h>
#endif
+#include <setjmp.h> /* for catch_context and CATCH_BEGIN */
+
/* Just in case they're not defined in stdio.h. */
#ifndef SEEK_SET
@@ -873,6 +875,95 @@
typedef int (catch_errors_ftype) (PTR);
extern int catch_errors (catch_errors_ftype *, PTR, char *, return_mask);
+
+/* Platform-independent longjmp interfaces for use by catch_errors() et al.
+ One should use catch_errors rather than manipulating these directly. */
+
+#if defined(HAVE_SIGSETJMP)
+#define CATCH_JMPBUF sigjmp_buf
+#define CATCH_SETJMP(buf) sigsetjmp(buf, 1)
+#define CATCH_LONGJMP(buf,val) siglongjmp(buf,val)
+#else
+#define CATCH_JMPBUF jmp_buf
+#define CATCH_SETJMP(buf) setjmp(buf)
+#define CATCH_LONGJMP(buf,val) longjmp(buf,val)
+#endif
+
+/* Context for linking consecutive calls to catch_begin(), catch_unwind(),
+ and catch_end(). */
+
+struct catch_context
+ {
+ return_mask mask; /* types of exceptions to suppress */
+ CATCH_JMPBUF *saved_catch; /* saved catch_return from top.c */
+ CATCH_JMPBUF catch; /* jump buffer used by CATCH_BEGIN */
+ struct cleanup
+ *saved_cleanups; /* cleanups prior to CATCH_BEGIN */
+ char *saved_error_pre; /* error_pre_print prior to CATCH_BEGIN */
+ char *saved_quit_pre; /* quit_pre_print prior to CATCH_BEGIN */
+ int caught; /* return value from setjmp: return_reason
+ if error or quit caught, 0 otherwise */
+ int cleanuped; /* whether CATCH_CLEANUP has occurred */
+ };
+
+/* Helper functions for CATCH_BEGIN, CATCH_CLEANUP, and CATCH_END. */
+
+extern void catch_begin (struct catch_context *, char *, return_mask);
+extern void catch_cleanup (struct catch_context *);
+extern void catch_end (struct catch_context *);
+
+/* The following macros provide a safe way to execute code that might
+ return nonlocally, e.g. as a result of a user quit request or a call
+ to error(). Usage:
+
+ CATCH_BEGIN(<msg>, <mask>);
+ ... code that might return nonlocally ...
+ CATCH_CLEANUP;
+ ... code that always gets executed ...
+ CATCH_END;
+
+ or just
+
+ CATCH_BEGIN(<msg>, <mask>);
+ ... code that might return nonlocally ...
+ CATCH_END;
+
+ These sequences cause the following to occur:
+
+ (1) The code immediately following CATCH_BEGIN is executed in the
+ normal way, subject to premature completion as a result of a
+ nonlocal return. If <msg> is non-null and a nonlocal return
+ occurs, <msg> is displayed before error or quit messages.
+
+ (2) If CATCH_CLEANUP is present, the code following it is executed.
+
+ (3) If a nonlocal return occurred in (1) for a reason not
+ specified in <mask>, the nonlocal return is performed;
+ otherwise, execution resumes after CATCH_END.
+
+ Between CATCH_CLEANUP and CATCH_END, the macro CATCH_CAUGHT evaluates
+ to the appropriate enum return_reason if a nonlocal return occurred
+ and to 0 otherwise.
+
+ Return statements are illegal between CATCH_BEGIN and CATCH_END. */
+
+#define CATCH_BEGIN(errstr, mask) \
+ { \
+ struct catch_context context; \
+ catch_begin (&context, errstr, mask); \
+ context.caught = CATCH_SETJMP (context.catch); \
+ if (!context.caught) \
+ {
+
+#define CATCH_CLEANUP \
+ } \
+ catch_cleanup (&context)
+
+#define CATCH_CAUGHT context.caught
+
+#define CATCH_END \
+ catch_end (&context); \
+ }
/* Template to catch_errors() that wraps calls to command
functions. */
Index: top.c
===================================================================
RCS file: /cvs/src/src/gdb/top.c,v
retrieving revision 1.9
diff -u -r1.9 top.c
--- top.c 2000/04/06 15:24:36 1.9
+++ top.c 2000/04/11 21:47:01
@@ -44,8 +44,6 @@
#include <sys/types.h>
-#include <setjmp.h>
-
#include "event-top.h"
#include "gdb_string.h"
#include "gdb_stat.h"
@@ -489,20 +487,8 @@
NORETURN void (*error_hook) (void) ATTR_NORETURN;
-/* One should use catch_errors rather than manipulating these
- directly. */
-#if defined(HAVE_SIGSETJMP)
-#define SIGJMP_BUF sigjmp_buf
-#define SIGSETJMP(buf) sigsetjmp(buf, 1)
-#define SIGLONGJMP(buf,val) siglongjmp(buf,val)
-#else
-#define SIGJMP_BUF jmp_buf
-#define SIGSETJMP(buf) setjmp(buf)
-#define SIGLONGJMP(buf,val) longjmp(buf,val)
-#endif
-
/* Where to go for return_to_top_level. */
-static SIGJMP_BUF *catch_return;
+static CATCH_JMPBUF *catch_return;
/* Return for reason REASON to the nearest containing catch_errors(). */
@@ -538,8 +524,93 @@
/* Jump to the containing catch_errors() call, communicating REASON
to that call via setjmp's return value. Note that REASON can't
be zero, by definition in defs.h. */
+
+ (NORETURN void) CATCH_LONGJMP (*catch_return, (int) reason);
+}
+
+/* Helper function for macro CATCH_BEGIN: prepare to catch errors specified
+ by MASK. */
+
+void
+catch_begin (struct catch_context *context, char *msg, return_mask mask)
+{
+ context->mask = mask;
+ context->cleanuped = 0;
+
+ /* Override error/quit messages during FUNC. */
+
+ context->saved_error_pre = error_pre_print;
+ context->saved_quit_pre = quit_pre_print;
+
+ if (mask & RETURN_MASK_ERROR)
+ error_pre_print = msg;
+ if (mask & RETURN_MASK_QUIT)
+ quit_pre_print = msg;
+
+ /* Prevent error/quit during the protected code from calling cleanups
+ established prior to now. */
+
+ context->saved_cleanups = save_cleanups ();
+
+ /* Catch error/quit events. */
+
+ context->saved_catch = catch_return;
+ catch_return = &context->catch;
+}
+
+/* Helper function for macro CATCH_CLEANUP: restore context saved by
+ catch_begin(). */
+
+void
+catch_cleanup (struct catch_context *context)
+{
+ /* Restore previous event trap. */
+
+ catch_return = context->saved_catch;
+
+ /* FIXME: cagney/1999-11-05: A correct FUNC implementaton will
+ clean things up (restoring the cleanup chain) to the state they
+ were just prior to the call. Unfortunatly, many FUNC's are not
+ that well behaved. This could be fixed by adding either a
+ do_cleanups call (to cover the problem) or an assertion check to
+ detect bad FUNCs code. */
+
+ /* Restore the cleanup chain and error/quit messages to their
+ original states. */
+
+ restore_cleanups (context->saved_cleanups);
+
+ if (context->mask & RETURN_MASK_QUIT)
+ quit_pre_print = context->saved_quit_pre;
+ if (context->mask & RETURN_MASK_ERROR)
+ error_pre_print = context->saved_error_pre;
+
+ context->cleanuped = 1;
+}
+
+/* Helper function for macro CATCH_END: perform nonlocal return if
+ appropriate. */
+
+void
+catch_end (struct catch_context *context)
+{
+ /* Allow CATCH_CLEANUP to be omitted between CATCH_BEGIN and CATCH_END. */
- (NORETURN void) SIGLONGJMP (*catch_return, (int) reason);
+ if (!context->cleanuped)
+ catch_cleanup (context);
+
+ /* Proceed normally if no error/quit event occurred. */
+
+ if (!context->caught)
+ return;
+
+ /* If the caller didn't request that the event be caught, relay the
+ event to the next containing catch_errors(). */
+
+ if (!(context->mask & RETURN_MASK (context->caught)))
+ return_to_top_level (context->caught);
+
+ /* The event was caught, so proceed normally. */
}
/* Call FUNC with arg ARGS, catching any errors. If there is no
@@ -580,89 +651,15 @@
char *errstring;
return_mask mask;
{
- SIGJMP_BUF *saved_catch;
- SIGJMP_BUF catch;
- int val;
- struct cleanup *saved_cleanup_chain;
- char *saved_error_pre_print;
- char *saved_quit_pre_print;
-
- /* Return value from SIGSETJMP(): enum return_reason if error or
- quit caught, 0 otherwise. */
- int caught;
-
- /* Override error/quit messages during FUNC. */
-
- saved_error_pre_print = error_pre_print;
- saved_quit_pre_print = quit_pre_print;
-
- if (mask & RETURN_MASK_ERROR)
- error_pre_print = errstring;
- if (mask & RETURN_MASK_QUIT)
- quit_pre_print = errstring;
-
- /* Prevent error/quit during FUNC from calling cleanups established
- prior to here. */
-
- saved_cleanup_chain = save_cleanups ();
-
- /* Call FUNC, catching error/quit events. */
-
- saved_catch = catch_return;
- catch_return = &catch;
- caught = SIGSETJMP (catch);
- if (!caught)
- val = (*func) (args);
- catch_return = saved_catch;
-
- /* FIXME: cagney/1999-11-05: A correct FUNC implementaton will
- clean things up (restoring the cleanup chain) to the state they
- were just prior to the call. Unfortunatly, many FUNC's are not
- that well behaved. This could be fixed by adding either a
- do_cleanups call (to cover the problem) or an assertion check to
- detect bad FUNCs code. */
-
- /* Restore the cleanup chain and error/quit messages to their
- original states. */
-
- restore_cleanups (saved_cleanup_chain);
-
- if (mask & RETURN_MASK_QUIT)
- quit_pre_print = saved_quit_pre_print;
- if (mask & RETURN_MASK_ERROR)
- error_pre_print = saved_error_pre_print;
-
- /* Return normally if no error/quit event occurred. */
-
- if (!caught)
- return val;
-
- /* If the caller didn't request that the event be caught, relay the
- event to the next containing catch_errors(). */
-
- if (!(mask & RETURN_MASK (caught)))
- return_to_top_level (caught);
-
- /* Tell the caller that an event was caught.
-
- FIXME: nsd/2000-02-22: When MASK is RETURN_MASK_ALL, the caller
- can't tell what type of event occurred.
-
- A possible fix is to add a new interface, catch_event(), that
- returns enum return_reason after catching an error or a quit.
-
- When returning normally, i.e. without catching an error or a
- quit, catch_event() could return RETURN_NORMAL, which would be
- added to enum return_reason. FUNC would return information
- exclusively via ARGS.
+ int val, caught;
- Alternatively, normal catch_event() could return FUNC's return
- value. The caller would need to be aware of potential overlap
- with enum return_reason, which could be publicly restricted to
- negative values to simplify return value processing in FUNC and
- in the caller. */
+ CATCH_BEGIN (errstring, mask);
+ val = (*func) (args);
+ CATCH_CLEANUP;
+ caught = CATCH_CAUGHT;
+ CATCH_END;
- return 0;
+ return caught ? 0 : val;
}
struct captured_command_args