This is the mail archive of the gdb-patches@sourceware.cygnus.com mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

RFA: top.c: new catch_errors + cleanup interface


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

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]