summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy Wingo <wingo@pobox.com>2014-02-20 09:45:01 +0100
committerAndy Wingo <wingo@pobox.com>2014-02-20 09:45:01 +0100
commit7e2fd4e7f53c281307efd12b80df46346002a33d (patch)
tree324241c65afa58b0b1f75600716b69e932bd4334
parent5d20fd49fe53c2520e36e8bf983c7b7214b0ad2a (diff)
Unwind-only stack overflow exceptions
* module/ice-9/boot-9.scm (catch): Signal an early error if the handler or pre-unwind handler types aren't right. This is more important than it was, given that we dispatch on type now when finding matching catch clauses. * libguile/vm.c (vm_expand_stack): Use the standard scm_report_stack_overflow to signal stack overflow. This will avoid running pre-unwind handlers. * libguile/throw.h: Move scm_report_stack_overflow here. * libguile/throw.c (catch): Define a version of catch in C. (throw_without_pre_unwind): New helper. Besides serving as the pre-boot "throw" binding, it allows stack overflow to throw without running pre-unwind handlers. (scm_catch, scm_catch_with_pre_unwind_handler) (scm_with_throw_handler): Use the new catch in C. (scm_report_stack_overflow): Moved from stackchk.c; throws an unwind-only exception. * libguile/stackchk.h: * libguile/stackchk.c: Remove the scm_report_stack_overflow bits.
-rw-r--r--libguile/stackchk.c24
-rw-r--r--libguile/stackchk.h3
-rw-r--r--libguile/throw.c279
-rw-r--r--libguile/throw.h6
-rw-r--r--libguile/vm.c10
-rw-r--r--module/ice-9/boot-9.scm14
6 files changed, 181 insertions, 155 deletions
diff --git a/libguile/stackchk.c b/libguile/stackchk.c
index 208ba97ed..7e2441b53 100644
--- a/libguile/stackchk.c
+++ b/libguile/stackchk.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1995,1996,1997, 2000, 2001, 2006, 2008, 2010, 2011 Free Software Foundation, Inc.
+/* Copyright (C) 1995,1996,1997, 2000, 2001, 2006, 2008, 2010, 2011, 2014 Free Software Foundation, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
@@ -36,34 +36,12 @@
int scm_stack_checking_enabled_p;
-SCM_SYMBOL (scm_stack_overflow_key, "stack-overflow");
-
static void
reset_scm_stack_checking_enabled_p (void *arg)
{
scm_stack_checking_enabled_p = (int)(scm_t_bits)arg;
}
-void
-scm_report_stack_overflow ()
-{
- scm_dynwind_begin (0); /* non-rewindable frame */
- scm_dynwind_unwind_handler (reset_scm_stack_checking_enabled_p,
- (void*)(scm_t_bits)scm_stack_checking_enabled_p,
- SCM_F_WIND_EXPLICITLY);
-
- scm_stack_checking_enabled_p = 0;
-
- scm_error (scm_stack_overflow_key,
- NULL,
- "Stack overflow",
- SCM_BOOL_F,
- SCM_BOOL_F);
-
- /* not reached */
- scm_dynwind_end ();
-}
-
long
scm_stack_size (SCM_STACKITEM *start)
{
diff --git a/libguile/stackchk.h b/libguile/stackchk.h
index 1ed170fef..23dbdba7b 100644
--- a/libguile/stackchk.h
+++ b/libguile/stackchk.h
@@ -3,7 +3,7 @@
#ifndef SCM_STACKCHK_H
#define SCM_STACKCHK_H
-/* Copyright (C) 1995,1996,1998,2000, 2003, 2006, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+/* Copyright (C) 1995,1996,1998,2000, 2003, 2006, 2008, 2009, 2010, 2011, 2014 Free Software Foundation, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
@@ -57,7 +57,6 @@ SCM_API int scm_stack_checking_enabled_p;
-SCM_API void scm_report_stack_overflow (void);
SCM_API long scm_stack_size (SCM_STACKITEM *start);
SCM_API void scm_stack_report (void);
SCM_API SCM scm_sys_get_stack_size (void);
diff --git a/libguile/throw.c b/libguile/throw.c
index 37be4cd3f..98149a167 100644
--- a/libguile/throw.c
+++ b/libguile/throw.c
@@ -45,9 +45,18 @@
#include "libguile/private-options.h"
-/* Pleasantly enough, the guts of catch are defined in Scheme, in terms of
- prompt, abort, and the %exception-handler fluid. This file just provides
- shims so that it's easy to have catch functionality from C.
+/* Pleasantly enough, the guts of catch are defined in Scheme, in terms
+ of prompt, abort, and the %exception-handler fluid. Check boot-9 for
+ the definitions.
+
+ Still, it's useful to be able to throw unwind-only exceptions from C,
+ for example so that we can recover from stack overflow. We also need
+ to have an implementation of catch and throw handy before boot time.
+ For that reason we have a parallel implementation of "catch" that
+ uses the same fluids here. Throws from C still call out to Scheme
+ though, so that pre-unwind handlers can be run. Getting the dynamic
+ environment right for pre-unwind handlers is tricky, and it's
+ important to have all of the implementation in one place.
All of these function names and prototypes carry a fair bit of historical
baggage. */
@@ -55,43 +64,155 @@
-static SCM catch_var, throw_var, with_throw_handler_var;
+static SCM throw_var;
static SCM exception_handler_fluid;
+static SCM
+catch (SCM tag, SCM thunk, SCM handler, SCM pre_unwind_handler)
+{
+ struct scm_vm *vp;
+ SCM eh, prompt_tag;
+ SCM res;
+ scm_t_dynstack *dynstack = &SCM_I_CURRENT_THREAD->dynstack;
+ SCM dynamic_state = SCM_I_CURRENT_THREAD->dynamic_state;
+ scm_i_jmp_buf registers;
+ scm_t_ptrdiff saved_stack_depth;
+
+ if (!scm_is_eq (tag, SCM_BOOL_T) && !scm_is_symbol (tag))
+ scm_wrong_type_arg ("catch", 1, tag);
+
+ if (SCM_UNBNDP (handler))
+ handler = SCM_BOOL_F;
+ else if (!scm_is_true (scm_procedure_p (handler)))
+ scm_wrong_type_arg ("catch", 3, handler);
+
+ if (SCM_UNBNDP (pre_unwind_handler))
+ pre_unwind_handler = SCM_BOOL_F;
+ else if (!scm_is_true (scm_procedure_p (pre_unwind_handler)))
+ scm_wrong_type_arg ("catch", 4, pre_unwind_handler);
+
+ prompt_tag = scm_cons (SCM_INUM0, SCM_EOL);
+
+ eh = scm_c_make_vector (4, SCM_BOOL_F);
+ scm_c_vector_set_x (eh, 0, scm_fluid_ref (exception_handler_fluid));
+ scm_c_vector_set_x (eh, 1, tag);
+ scm_c_vector_set_x (eh, 2, prompt_tag);
+ scm_c_vector_set_x (eh, 3, pre_unwind_handler);
+
+ vp = scm_the_vm ();
+ saved_stack_depth = vp->sp - vp->stack_base;
+
+ /* Push the prompt and exception handler onto the dynamic stack. */
+ scm_dynstack_push_prompt (dynstack,
+ SCM_F_DYNSTACK_PROMPT_ESCAPE_ONLY
+ | SCM_F_DYNSTACK_PROMPT_PUSH_NARGS,
+ prompt_tag,
+ vp->fp - vp->stack_base,
+ saved_stack_depth,
+ vp->ip,
+ &registers);
+ scm_dynstack_push_fluid (dynstack, exception_handler_fluid, eh,
+ dynamic_state);
+
+ if (SCM_I_SETJMP (registers))
+ {
+ /* A non-local return. */
+
+ /* FIXME: We know where the args will be on the stack; we could
+ avoid consing them. */
+ SCM args = scm_i_prompt_pop_abort_args_x (vp);
+
+ /* Cdr past the continuation. */
+ args = scm_cdr (args);
+
+ return scm_apply_0 (handler, args);
+ }
+
+ res = scm_call_0 (thunk);
+
+ scm_dynstack_unwind_fluid (dynstack, dynamic_state);
+ scm_dynstack_pop (dynstack);
+
+ return res;
+}
+
+static void
+default_exception_handler (SCM k, SCM args)
+{
+ static int error_printing_error = 0;
+ static int error_printing_fallback = 0;
+
+ if (error_printing_fallback)
+ fprintf (stderr, "\nFailed to print exception.\n");
+ else if (error_printing_error)
+ {
+ fprintf (stderr, "\nError while printing exception:\n");
+ error_printing_fallback = 1;
+ fprintf (stderr, "Key: ");
+ scm_write (k, scm_current_error_port ());
+ fprintf (stderr, ", args: ");
+ scm_write (args, scm_current_error_port ());
+ scm_newline (scm_current_error_port ());
+ }
+ else
+ {
+ fprintf (stderr, "Uncaught exception:\n");
+ error_printing_error = 1;
+ scm_handle_by_message (NULL, k, args);
+ }
+
+ /* Normally we don't get here, because scm_handle_by_message will
+ exit. */
+ fprintf (stderr, "Aborting.\n");
+ abort ();
+}
+
+static SCM
+throw_without_pre_unwind (SCM tag, SCM args)
+{
+ SCM eh;
+
+ for (eh = scm_fluid_ref (exception_handler_fluid);
+ scm_is_true (eh);
+ eh = scm_c_vector_ref (eh, 0))
+ {
+ SCM catch_key, prompt_tag;
+
+ catch_key = scm_c_vector_ref (eh, 1);
+ if (!scm_is_eq (catch_key, SCM_BOOL_T) && !scm_is_eq (catch_key, tag))
+ continue;
+
+ if (scm_is_true (scm_c_vector_ref (eh, 3)))
+ fprintf (stderr, "\nWarning: unwind-only exception, perhaps due to "
+ "stack overflow; not running pre-unwind handlers.");
+
+ prompt_tag = scm_c_vector_ref (eh, 2);
+ if (scm_is_true (prompt_tag))
+ scm_abort_to_prompt_star (prompt_tag, scm_cons (tag, args));
+ }
+
+ default_exception_handler (tag, args);
+ return SCM_UNSPECIFIED;
+}
+
SCM
scm_catch (SCM key, SCM thunk, SCM handler)
{
- return scm_call_3 (scm_variable_ref (catch_var), key, thunk, handler);
+ return catch (key, thunk, handler, SCM_UNDEFINED);
}
SCM
scm_catch_with_pre_unwind_handler (SCM key, SCM thunk, SCM handler,
SCM pre_unwind_handler)
{
- if (SCM_UNBNDP (pre_unwind_handler))
- return scm_catch (key, thunk, handler);
- else
- return scm_call_4 (scm_variable_ref (catch_var), key, thunk, handler,
- pre_unwind_handler);
-}
-
-static void
-init_with_throw_handler_var (void)
-{
- with_throw_handler_var
- = scm_module_variable (scm_the_root_module (),
- scm_from_latin1_symbol ("with-throw-handler"));
+ return catch (key, thunk, handler, pre_unwind_handler);
}
SCM
scm_with_throw_handler (SCM key, SCM thunk, SCM handler)
{
- static scm_i_pthread_once_t once = SCM_I_PTHREAD_ONCE_INIT;
- scm_i_pthread_once (&once, init_with_throw_handler_var);
-
- return scm_call_3 (scm_variable_ref (with_throw_handler_var),
- key, thunk, handler);
+ return catch (key, thunk, SCM_UNDEFINED, handler);
}
SCM
@@ -443,103 +564,26 @@ scm_ithrow (SCM key, SCM args, int no_return SCM_UNUSED)
return scm_throw (key, args);
}
-/* Unfortunately we have to support catch and throw before boot-9 has, um,
- booted. So here are lame versions, which will get replaced with their scheme
- equivalents. */
+SCM_SYMBOL (scm_stack_overflow_key, "stack-overflow");
-SCM_SYMBOL (sym_pre_init_catch_tag, "%pre-init-catch-tag");
-
-static SCM
-pre_init_catch (SCM tag, SCM thunk, SCM handler, SCM pre_unwind_handler)
-{
- struct scm_vm *vp;
- volatile SCM v_handler;
- SCM res;
- scm_t_dynstack *dynstack = &SCM_I_CURRENT_THREAD->dynstack;
- scm_i_jmp_buf registers;
-
- /* Only handle catch-alls without pre-unwind handlers */
- if (!SCM_UNBNDP (pre_unwind_handler))
- abort ();
- if (scm_is_false (scm_eqv_p (tag, SCM_BOOL_T)))
- abort ();
-
- /* These two are volatile, so we know we can access them after a
- nonlocal return to the setjmp. */
- vp = scm_the_vm ();
- v_handler = handler;
-
- /* Push the prompt onto the dynamic stack. */
- scm_dynstack_push_prompt (dynstack,
- SCM_F_DYNSTACK_PROMPT_ESCAPE_ONLY
- | SCM_F_DYNSTACK_PROMPT_PUSH_NARGS,
- sym_pre_init_catch_tag,
- vp->fp - vp->stack_base,
- vp->sp - vp->stack_base,
- vp->ip,
- &registers);
-
- if (SCM_I_SETJMP (registers))
- {
- /* nonlocal exit */
- SCM args;
- /* vp is not volatile */
- vp = scm_the_vm ();
- args = scm_i_prompt_pop_abort_args_x (vp);
- /* cdr past the continuation */
- return scm_apply_0 (v_handler, scm_cdr (args));
- }
-
- res = scm_call_0 (thunk);
- scm_dynstack_pop (dynstack);
-
- return res;
-}
-
-static int
-find_pre_init_catch (void)
+void
+scm_report_stack_overflow (void)
{
- if (scm_dynstack_find_prompt (&SCM_I_CURRENT_THREAD->dynstack,
- sym_pre_init_catch_tag,
- NULL, NULL, NULL, NULL, NULL))
- return 1;
+ /* Arguments as if from:
- return 0;
-}
+ scm_error (stack-overflow, NULL, "Stack overflow", #f, #f);
-static SCM
-pre_init_throw (SCM k, SCM args)
-{
- if (find_pre_init_catch ())
- return scm_abort_to_prompt_star (sym_pre_init_catch_tag, scm_cons (k, args));
- else
- {
- static int error_printing_error = 0;
- static int error_printing_fallback = 0;
-
- if (error_printing_fallback)
- fprintf (stderr, "\nFailed to print exception.\n");
- else if (error_printing_error)
- {
- fprintf (stderr, "\nError while printing exception:\n");
- error_printing_fallback = 1;
- fprintf (stderr, "Key: ");
- scm_write (k, scm_current_error_port ());
- fprintf (stderr, ", args: ");
- scm_write (args, scm_current_error_port ());
- scm_newline (scm_current_error_port ());
- }
- else
- {
- fprintf (stderr, "Throw without catch before boot:\n");
- error_printing_error = 1;
- scm_handle_by_message_noexit (NULL, k, args);
- }
+ We build the arguments manually because we throw without running
+ pre-unwind handlers. (Pre-unwind handlers could rewind the
+ stack.) */
+ SCM args = scm_list_4 (SCM_BOOL_F,
+ scm_from_latin1_string ("Stack overflow"),
+ SCM_BOOL_F,
+ SCM_BOOL_F);
+ throw_without_pre_unwind (scm_stack_overflow_key, args);
- fprintf (stderr, "Aborting.\n");
- abort ();
- return SCM_BOOL_F; /* not reached */
- }
+ /* Not reached. */
+ abort ();
}
void
@@ -553,10 +597,9 @@ scm_init_throw ()
throw, and with-throw-handler are created in boot-9.scm. */
scm_c_define ("%exception-handler", exception_handler_fluid);
- catch_var = scm_c_define ("catch", scm_c_make_gsubr ("catch", 3, 1, 0,
- pre_init_catch));
+ scm_c_define ("catch", scm_c_make_gsubr ("catch", 3, 1, 0, catch));
throw_var = scm_c_define ("throw", scm_c_make_gsubr ("throw", 1, 0, 1,
- pre_init_throw));
+ throw_without_pre_unwind));
#include "libguile/throw.x"
}
diff --git a/libguile/throw.h b/libguile/throw.h
index 62592d22e..531aadd33 100644
--- a/libguile/throw.h
+++ b/libguile/throw.h
@@ -3,7 +3,7 @@
#ifndef SCM_THROW_H
#define SCM_THROW_H
-/* Copyright (C) 1995,1996,1998,2000, 2006, 2008, 2010 Free Software Foundation, Inc.
+/* Copyright (C) 1995,1996,1998,2000, 2006, 2008, 2010, 2014 Free Software Foundation, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
@@ -81,6 +81,10 @@ SCM_API SCM scm_catch (SCM tag, SCM thunk, SCM handler);
SCM_API SCM scm_with_throw_handler (SCM tag, SCM thunk, SCM handler);
SCM_API SCM scm_ithrow (SCM key, SCM args, int no_return);
+/* This throws to the `stack-overflow' key, without running pre-unwind
+ handlers. */
+SCM_API void scm_report_stack_overflow (void);
+
SCM_API SCM scm_throw (SCM key, SCM args);
SCM_INTERNAL void scm_init_throw (void);
diff --git a/libguile/vm.c b/libguile/vm.c
index b071a54cb..db1309432 100644
--- a/libguile/vm.c
+++ b/libguile/vm.c
@@ -1016,13 +1016,7 @@ vm_expand_stack (struct scm_vm *vp)
old_stack = vp->stack_base;
new_stack = expand_stack (vp->stack_base, vp->stack_size, new_size);
if (!new_stack)
- /* It would be nice to throw an exception here, but that is
- extraordinarily hard. Exceptionally hard, you might say!
- "throw" is implemented in Scheme, and there may be arbitrary
- pre-unwind handlers that push on more frames. We will
- endeavor to do so in the future, but for now we just
- abort. */
- abort ();
+ scm_report_stack_overflow ();
vp->stack_base = new_stack;
vp->stack_size = new_size;
@@ -1068,6 +1062,8 @@ vm_expand_stack (struct scm_vm *vp)
/* Finally, reset the limit, to catch further overflows. */
vp->stack_limit = vp->stack_base + vp->max_stack_size;
+ /* FIXME: Use scm_report_stack_overflow, but in a mode that allows
+ pre-unwind handlers to run. */
vm_error ("VM: Stack overflow", SCM_UNDEFINED);
}
diff --git a/module/ice-9/boot-9.scm b/module/ice-9/boot-9.scm
index 928990230..fd924454e 100644
--- a/module/ice-9/boot-9.scm
+++ b/module/ice-9/boot-9.scm
@@ -797,10 +797,16 @@ A @var{pre-unwind-handler} can exit either normally or non-locally.
If it exits normally, Guile unwinds the stack and dynamic context
and then calls the normal (third argument) handler. If it exits
non-locally, that exit determines the continuation."
- (if (not (or (symbol? k) (eqv? k #t)))
- (scm-error 'wrong-type-arg "catch"
- "Wrong type argument in position ~a: ~a"
- (list 1 k) (list k)))
+ (define (wrong-type-arg n val)
+ (scm-error 'wrong-type-arg "catch"
+ "Wrong type argument in position ~a: ~a"
+ (list n val) (list val)))
+ (unless (or (symbol? k) (eqv? k #t))
+ (wrong-type-arg 1 k))
+ (unless (procedure? handler)
+ (wrong-type-arg 3 handler))
+ (unless (or (not pre-unwind-handler) (procedure? pre-unwind-handler))
+ (wrong-type-arg 4 pre-unwind-handler))
(let ((tag (make-prompt-tag "catch")))
(call-with-prompt
tag