From: Taahir Ahmed <ahmed.taahir@gmail.com>
To: guile-devel@gnu.org
Cc: Taahir Ahmed <ahmed.taahir@gmail.com>
Subject: [PATCH 3/3] Implement Guile exceptions with C++ exceptions.
Date: Tue, 14 Jul 2015 22:40:33 -0500 [thread overview]
Message-ID: <2680798.z87ittDu1C@basis> (raw)
In-Reply-To: <1436897249-18167-1-git-send-email-ahmed.taahir@gmail.com>
eh-jmp.{h,cc} contain setjmp/longjmp analogues that use C++ exceptions
to unwind the stack. This means that C++ destructors will be called
properly in when a guile exception passes through a user code frame.
Flow control to resume a continuation remains implemented with
setjmp/longjmp, since it appears that in some places the jump travels
down the stack.
---
libguile/Makefile.am | 1 +
libguile/control.c | 6 ++-
libguile/eh-jmp.cc | 39 ++++++++++++++
libguile/eh-jmp.h | 23 ++++++++
libguile/eval.c | 35 +++++++++---
libguile/threads.h | 5 --
libguile/throw.c | 34 +++++++++---
libguile/vm-engine.c | 5 +-
libguile/vm.c | 57 ++++++++++++++++----
test-suite/standalone/Makefile.am | 7 +++
test-suite/standalone/test-cpp-destructors.cc | 77
+++++++++++++++++++++++++++
11 files changed, 259 insertions(+), 30 deletions(-)
create mode 100644 libguile/eh-jmp.cc
create mode 100644 libguile/eh-jmp.h
create mode 100644 test-suite/standalone/test-cpp-destructors.cc
diff --git a/libguile/Makefile.am b/libguile/Makefile.am
index cc88215..f5e746f 100644
--- a/libguile/Makefile.am
+++ b/libguile/Makefile.am
@@ -140,6 +140,7 @@ libguile_@GUILE_EFFECTIVE_VERSION@_la_SOURCES =
\
deprecation.c \
dynstack.c \
dynwind.c \
+ eh-jmp.cc \
eq.c \
error.c \
eval.c \
diff --git a/libguile/control.c b/libguile/control.c
index 347d697..53e3241 100644
--- a/libguile/control.c
+++ b/libguile/control.c
@@ -24,6 +24,7 @@
#include "libguile/_scm.h"
#include "libguile/control.h"
+#include "libguile/eh-jmp.h"
#include "libguile/programs.h"
#include "libguile/instructions.h"
#include "libguile/vm.h"
@@ -37,7 +38,8 @@
\f
-/* Only to be called if the SCM_I_SETJMP returns 1 */
+/* Only to be called if we are returning to a prompt via nonlocal
+ jump. */
SCM
scm_i_prompt_pop_abort_args_x (struct scm_vm *vp)
{
@@ -177,7 +179,7 @@ scm_c_abort (struct scm_vm *vp, SCM tag, size_t n, SCM
*argv,
*(++(vp->sp)) = scm_from_size_t (n+1); /* +1 for continuation */
/* Jump! */
- SCM_I_LONGJMP (*registers, 1);
+ eh_throw (tag);
/* Shouldn't get here */
abort ();
diff --git a/libguile/eh-jmp.cc b/libguile/eh-jmp.cc
new file mode 100644
index 0000000..f5b7b80
--- /dev/null
+++ b/libguile/eh-jmp.cc
@@ -0,0 +1,39 @@
+#include "libguile/eh-jmp.h"
+
+struct prompt_exception
+{
+ SCM tag;
+};
+
+int eh_catch (SCM *taglist, tryfn_scm fn, SCM *result, ...)
+{
+ va_list ap;
+ va_start (ap, result);
+
+ try
+ {
+ *result = fn (ap);
+ va_end (ap);
+ return 0;
+ }
+ catch (prompt_exception e)
+ {
+ va_end(ap);
+
+ for (SCM rest = *taglist; rest != SCM_EOL; rest = scm_cdr (rest))
+ {
+ SCM cur_key = scm_car (rest);
+
+ if (scm_is_eq (cur_key, e.tag))
+ return 1;
+ }
+
+ throw e;
+ }
+}
+
+void eh_throw(SCM tag)
+{
+ prompt_exception e = {tag};
+ throw e;
+}
diff --git a/libguile/eh-jmp.h b/libguile/eh-jmp.h
new file mode 100644
index 0000000..6730895
--- /dev/null
+++ b/libguile/eh-jmp.h
@@ -0,0 +1,23 @@
+#ifndef LIBGUILE_EH_JMP_H
+#define LIBGUILE_EH_JMP_H
+
+#include <stdarg.h>
+
+#include "libguile/__scm.h"
+#include "libguile/_scm.h"
+#include "libguile/pairs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef SCM (* tryfn_scm) (va_list);
+
+int eh_catch (SCM *taglist, tryfn_scm fn, SCM *result, ...);
+void eh_throw (SCM taglist) SCM_NORETURN;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libguile/eval.c b/libguile/eval.c
index 735e6c0..c83b7e7 100644
--- a/libguile/eval.c
+++ b/libguile/eval.c
@@ -37,6 +37,7 @@
#include "libguile/debug.h"
#include "libguile/deprecation.h"
#include "libguile/dynwind.h"
+#include "libguile/eh-jmp.h"
#include "libguile/eq.h"
#include "libguile/expand.h"
#include "libguile/feature.h"
@@ -238,6 +239,8 @@ truncate_values (SCM x)
}
#define EVAL1(x, env) (truncate_values (eval ((x), (env))))
+static SCM try_call_with_prompt(va_list ap);
+
static SCM
eval (SCM x, SCM env)
{
@@ -424,13 +427,14 @@ eval (SCM x, SCM env)
case SCM_M_CALL_WITH_PROMPT:
{
struct scm_vm *vp;
- SCM k, res;
+ SCM tag, res;
+ SCM taglist;
scm_i_jmp_buf registers;
/* We need the handler after nonlocal return to the setjmp, so
make sure it is volatile. */
volatile SCM handler;
- k = EVAL1 (CAR (mx), env);
+ tag = EVAL1 (CAR (mx), env);
handler = EVAL1 (CDDR (mx), env);
vp = scm_the_vm ();
@@ -438,13 +442,15 @@ eval (SCM x, SCM env)
scm_dynstack_push_prompt (&SCM_I_CURRENT_THREAD->dynstack,
SCM_F_DYNSTACK_PROMPT_ESCAPE_ONLY
| SCM_F_DYNSTACK_PROMPT_PUSH_NARGS,
- k,
+ tag,
vp->fp - vp->stack_base,
vp->sp - vp->stack_base,
vp->ip,
®isters);
- if (SCM_I_SETJMP (registers))
+ taglist = scm_cons (tag, SCM_EOL);
+ if (eh_catch (&taglist, try_call_with_prompt, &res, mx, env,
+ &SCM_I_CURRENT_THREAD->dynstack))
{
/* The prompt exited nonlocally. */
scm_gc_after_nonlocal_exit ();
@@ -453,9 +459,7 @@ eval (SCM x, SCM env)
args = scm_i_prompt_pop_abort_args_x (vp);
goto apply_proc;
}
-
- res = scm_call_0 (eval (CADR (mx), env));
- scm_dynstack_pop (&SCM_I_CURRENT_THREAD->dynstack);
+
return res;
}
@@ -464,6 +468,23 @@ eval (SCM x, SCM env)
}
}
+static SCM
+try_call_with_prompt(va_list ap)
+{
+ SCM res;
+ SCM mx;
+ SCM env;
+ scm_t_dynstack *dynstack;
+
+ mx = va_arg (ap, SCM);
+ env = va_arg (ap, SCM);
+ dynstack = va_arg (ap, scm_t_dynstack*);
+
+ res = scm_call_0 (eval (CADR (mx), env));
+ scm_dynstack_pop (dynstack);
+ return res;
+}
+
\f
/* Simple procedure calls
diff --git a/libguile/threads.h b/libguile/threads.h
index cb61bbc..1c1f0e3 100644
--- a/libguile/threads.h
+++ b/libguile/threads.h
@@ -115,11 +115,6 @@ typedef struct scm_i_thread {
/* For keeping track of the stack and registers. */
struct scm_vm *vp;
SCM_STACKITEM *base;
- scm_i_jmp_buf regs;
-#ifdef __ia64__
- void *register_backing_store_base;
- scm_t_contregs *pending_rbs_continuation;
-#endif
/* Whether this thread is in a critical section. */
int critical_section_level;
diff --git a/libguile/throw.c b/libguile/throw.c
index bbde5e0..f784df7 100644
--- a/libguile/throw.c
+++ b/libguile/throw.c
@@ -23,10 +23,12 @@
#endif
#include <alloca.h>
+#include <stdarg.h>
#include <stdio.h>
#include <unistdio.h>
#include "libguile/_scm.h"
#include "libguile/smob.h"
+#include "libguile/eh-jmp.h"
#include "libguile/eval.h"
#include "libguile/eq.h"
#include "libguile/control.h"
@@ -69,11 +71,34 @@ static SCM throw_var;
static SCM exception_handler_fluid;
+/* A helper function for eh_tryfn_prompt. Indirectly invoked below in
+ catch. */
+static SCM
+call_helper (va_list ap)
+{
+ SCM res;
+ SCM thunk;
+ scm_t_dynstack *dynstack;
+ SCM dynamic_state;
+
+ thunk = va_arg (ap, SCM);
+ dynstack = va_arg (ap, scm_t_dynstack*);
+ dynamic_state = va_arg (ap, SCM);
+
+ res = scm_call_0 (thunk);
+
+ scm_dynstack_unwind_fluid (dynstack, dynamic_state);
+ scm_dynstack_pop (dynstack);
+
+ return res;
+}
+
static SCM
catch (SCM tag, SCM thunk, SCM handler, SCM pre_unwind_handler)
{
struct scm_vm *vp;
SCM eh, prompt_tag;
+ SCM taglist;
SCM res;
scm_t_dynstack *dynstack = &SCM_I_CURRENT_THREAD->dynstack;
SCM dynamic_state = SCM_I_CURRENT_THREAD->dynamic_state;
@@ -116,7 +141,9 @@ catch (SCM tag, SCM thunk, SCM handler, SCM
pre_unwind_handler)
scm_dynstack_push_fluid (dynstack, exception_handler_fluid, eh,
dynamic_state);
- if (SCM_I_SETJMP (registers))
+ taglist = scm_cons (prompt_tag, SCM_EOL);
+ if (eh_catch (&taglist, call_helper, &res, thunk, dynstack,
+ dynamic_state))
{
/* A non-local return. */
SCM args;
@@ -133,11 +160,6 @@ catch (SCM tag, SCM thunk, SCM handler, SCM
pre_unwind_handler)
return scm_apply_0 (handler, args);
}
- res = scm_call_0 (thunk);
-
- scm_dynstack_unwind_fluid (dynstack, dynamic_state);
- scm_dynstack_pop (dynstack);
-
return res;
}
diff --git a/libguile/vm-engine.c b/libguile/vm-engine.c
index 7e752dd..628b951 100644
--- a/libguile/vm-engine.c
+++ b/libguile/vm-engine.c
@@ -419,7 +419,7 @@
((scm_t_uintptr) (ptr) % alignof_type (type) == 0)
static SCM
-VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
+VM_NAME (SCM *catch_taglist, scm_i_thread *thread, struct scm_vm *vp,
scm_i_jmp_buf *registers, int resume)
{
/* Instruction pointer: A pointer to the opcode that is currently
@@ -2069,6 +2069,9 @@ VM_NAME (scm_i_thread *thread, struct scm_vm *vp,
LOCAL_ADDRESS (proc_slot) - vp->stack_base,
ip + offset,
registers);
+
+ *catch_taglist = scm_cons (LOCAL_REF (tag), *catch_taglist);
+
NEXT (3);
}
diff --git a/libguile/vm.c b/libguile/vm.c
index 0e59835..7ac6523 100644
--- a/libguile/vm.c
+++ b/libguile/vm.c
@@ -39,6 +39,7 @@
#include "_scm.h"
#include "control.h"
+#include "eh-jmp.h"
#include "frames.h"
#include "gc-inline.h"
#include "instructions.h"
@@ -783,7 +784,7 @@ scm_i_call_with_current_continuation (SCM proc)
#undef VM_USE_HOOKS
#undef VM_NAME
-typedef SCM (*scm_t_vm_engine) (scm_i_thread *current_thread, struct scm_vm
*vp,
+typedef SCM (*scm_t_vm_engine) (SCM *taglist, scm_i_thread *current_thread,
struct scm_vm *vp,
scm_i_jmp_buf *registers, int resume);
static const scm_t_vm_engine vm_engines[SCM_VM_NUM_ENGINES] =
@@ -1204,6 +1205,26 @@ scm_the_vm (void)
return thread_vm (SCM_I_CURRENT_THREAD);
}
+static SCM
+try_call_engine (va_list ap)
+{
+ scm_t_vm_engine engine;
+ SCM *taglist;
+ scm_i_thread *thread;
+ struct scm_vm *vp;
+ scm_i_jmp_buf *registers;
+ int resume;
+
+ engine = va_arg (ap, scm_t_vm_engine);
+ taglist = va_arg (ap, SCM*);
+ thread = va_arg (ap, scm_i_thread*);
+ vp = va_arg (ap, struct scm_vm*);
+ registers = va_arg (ap, scm_i_jmp_buf*);
+ resume = va_arg (ap, int);
+
+ return engine (taglist, thread, vp, registers, resume);
+}
+
SCM
scm_call_n (SCM proc, SCM *argv, size_t nargs)
{
@@ -1246,16 +1267,34 @@ scm_call_n (SCM proc, SCM *argv, size_t nargs)
vp->fp = &base[5];
{
- int resume = SCM_I_SETJMP (registers);
-
- if (SCM_UNLIKELY (resume))
+ int resume = 0;
+ SCM res;
+ scm_t_vm_engine engine;
+ SCM taglist;
+
+ engine = vm_engines[vp->engine];
+
+ taglist = SCM_EOL;
+
+ while (1)
{
- scm_gc_after_nonlocal_exit ();
- /* Non-local return. */
- vm_dispatch_abort_hook (vp);
+ resume = eh_catch(/* Arguments used by eh_catch. */
+ &taglist, try_call_engine, &res,
+ /* Arguments consumed by try_call_engine. */
+ engine,
+ /* Arguments sent to the engine. */
+ &taglist, thread, vp, ®isters, resume);
+ if (resume)
+ {
+ scm_gc_after_nonlocal_exit ();
+ /* Non-local return. */
+ vm_dispatch_abort_hook (vp);
+ }
+ else
+ {
+ return res;
+ }
}
-
- return vm_engines[vp->engine](thread, vp, ®isters, resume);
}
}
diff --git a/test-suite/standalone/Makefile.am b/test-
suite/standalone/Makefile.am
index 5138b15..01270f9 100644
--- a/test-suite/standalone/Makefile.am
+++ b/test-suite/standalone/Makefile.am
@@ -96,6 +96,13 @@ EXTRA_DIST += test-language.el test-language.js
check_SCRIPTS += test-guild-compile
TESTS += test-guild-compile
+# test-cpp-destructors
+test_cpp_destructors_SOURCES = test-cpp-destructors.cc
+test_cpp_destructors_CFLAGS = ${test_cflags}
+test_cpp_destructors_LDADD = ${LIBGUILE_LDADD}
+check_PROGRAMS += test-cpp-destructors
+TESTS += test-cpp-destructors
+
# test-num2integral
test_num2integral_SOURCES = test-num2integral.c
test_num2integral_CFLAGS = ${test_cflags}
diff --git a/test-suite/standalone/test-cpp-destructors.cc b/test-
suite/standalone/test-cpp-destructors.cc
new file mode 100644
index 0000000..99fbf32
--- /dev/null
+++ b/test-suite/standalone/test-cpp-destructors.cc
@@ -0,0 +1,77 @@
+// test-cpp-destructors.c
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <cstdlib>
+
+#include <iostream>
+
+#include <libguile.h>
+
+// Canary value to track constructor/destructor execution.
+//
+// 0: constructor has not yet run
+//
+// 1: constructor has run
+//
+// 2: destructor has run
+static int canary = 0;
+
+struct miner
+{
+ miner()
+ {
+ canary = 1;
+ }
+
+ ~miner()
+ {
+ canary = 2;
+ }
+};
+
+static SCM test_miner(void *data)
+{
+ miner bob;
+
+ SCM should_be_int = static_cast<SCM>(data);
+ int dummy = scm_to_int(should_be_int);
+
+ return SCM_BOOL_T;
+}
+
+static SCM test_miner_handler(void *data, SCM key, SCM args)
+{
+ // Do nothing.
+
+ return SCM_BOOL_T;
+}
+
+static void tests(void *data, int argc, char **argv)
+{
+ SCM test_data = scm_from_utf8_string("Ceci n'est pas un int.");
+ scm_internal_catch(
+ SCM_BOOL_T,
+ test_miner,
+ static_cast<void*>(&test_data),
+ test_miner_handler,
+ NULL
+ );
+
+ // Check if destructor ran.
+ if(canary != 2)
+ {
+ std::cerr << "Destructor was not run by guile exception." <<
std::endl;
+ std::exit(1);
+ }
+
+ std::exit(0);
+}
+
+int main(int argc, char **argv)
+{
+ scm_boot_guile(argc, argv, tests, NULL);
+ return 0;
+}
--
2.3.6
next prev parent reply other threads:[~2015-07-15 3:40 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-07-14 18:07 Prototype for C++-compatible Guile Exceptions Taahir Ahmed
2015-07-14 18:07 ` [PATCH 1/3] Support C++ source files, use -fexceptions Taahir Ahmed
2015-07-14 18:07 ` [PATCH 2/3] Add C++ extern "C" guards to internal headers Taahir Ahmed
2015-07-14 19:36 ` Andreas Rottmann
2015-07-16 0:22 ` Taahir Ahmed
2015-07-16 18:55 ` Andreas Rottmann
2015-07-15 3:40 ` Taahir Ahmed [this message]
2015-08-10 3:26 ` Prototype for C++-compatible Guile Exceptions Taahir Ahmed
2015-09-04 8:06 ` Mark H Weaver
2015-09-06 23:01 ` Taahir Ahmed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/guile/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=2680798.z87ittDu1C@basis \
--to=ahmed.taahir@gmail.com \
--cc=guile-devel@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).