Fix gc with exceptions

This commit is contained in:
2026-09-03 21:04:46 -07:00
parent 2533b0db7d
commit 3b2ecf2b16
10 changed files with 139 additions and 275 deletions
+15 -103
View File
@@ -3,7 +3,6 @@
#include "base.h"
#include <setjmp.h>
#include <stdnoreturn.h>
enum StackFrameKind {
@@ -44,13 +43,16 @@ struct _StackFrame {
LispVal *args; // arguments of the function call
} call;
struct {
jmp_buf *target;
void (*handler)(void *user_ptr);
void *user_ptr;
void (*cleanup_user_ptr)(void *data);
} unwind_protect;
struct {
jmp_buf *target;
LispVal *exceptions; // list of exception symbols to catch
size_t datum; // extra value to identify this case
bool enabled;
void (*handler)(LispVal *name, LispVal *data, void *user_ptr);
void *user_ptr;
void (*cleanup_user_ptr)(void *data);
} handler_bind;
struct {
LispVal *symbol;
@@ -59,44 +61,22 @@ struct _StackFrame {
};
};
enum UnwindCause {
UNWIND_NORMAL,
UNWIND_EXCEPTION,
};
struct UnwindInformation {
bool set;
enum UnwindCause cause;
union {
StackFrame *target;
struct {
LispVal *name;
LispVal *data;
StackFrame *handler_frame;
} exception;
};
};
struct LispStack {
size_t max_depth;
size_t depth;
StackFrame *frames;
struct UnwindInformation unwind_info;
};
// ONLY APPLIES TO THE CALLING THREAD
static ALWAYS_INLINE StackFrame *OBJECT_LOWEST_LOCAL_REFERENCE(LispVal *val) {
assert(OBJECTP(val));
LispObject *obj = val;
return tss_get(obj->gc.lowest_local_ref);
return ((LispObject *) val)->gc.lowest_local_ref;
}
static ALWAYS_INLINE void SET_OBJECT_LOWEST_LOCAL_REFERENCE(LispVal *val,
StackFrame *frame) {
assert(OBJECTP(val));
LispObject *obj = val;
tss_set(obj->gc.lowest_local_ref, frame);
((LispObject *) val)->gc.lowest_local_ref = frame;
}
extern struct LispStack the_stack;
@@ -111,32 +91,6 @@ static ALWAYS_INLINE StackFrame *LISP_STACK_REF(void) {
return &the_stack.frames[the_stack.depth - 1];
}
static ALWAYS_INLINE void CLEAR_EXCEPTION(void) {
the_stack.unwind_info.set = false;
}
static ALWAYS_INLINE bool EXCEPTION_SET_P(void) {
return the_stack.unwind_info.set;
}
static ALWAYS_INLINE LispVal *EXCEPTION_NAME(void) {
assert(the_stack.unwind_info.set
&& the_stack.unwind_info.cause == UNWIND_EXCEPTION);
return the_stack.unwind_info.exception.name;
}
static ALWAYS_INLINE LispVal *EXCEPTION_DATA(void) {
assert(the_stack.unwind_info.set
&& the_stack.unwind_info.cause == UNWIND_EXCEPTION);
return the_stack.unwind_info.exception.data;
}
static ALWAYS_INLINE StackFrame *EXCEPTION_HANDLER_FRAME(void) {
assert(the_stack.unwind_info.set
&& the_stack.unwind_info.cause == UNWIND_EXCEPTION);
return the_stack.unwind_info.exception.handler_frame;
}
// functions
void push_call_frame(LispVal *name, LispVal *args);
// replace the args in the top stack frame with ARGS and mark them as evaluated
@@ -145,10 +99,15 @@ void set_stack_evaluated_args(StackFrame *restrict ref, LispVal *fobj,
LispVal *args);
// unwind protect
void push_unwind_protect_frame(jmp_buf *buf);
void push_unwind_protect_frame(void (*handler)(void *user_ptr), void *user_ptr,
void (*cleanup_user_ptr)(void *data));
// handler bind
bool *push_handler_bind_frame(jmp_buf *buf, LispVal *exceptions, size_t datum);
void push_handler_bind_frame(LispVal *exceptions,
void (*handler)(LispVal *name, LispVal *data,
void *user_ptr),
void *user_ptr,
void (*cleanup_user_ptr)(void *data));
// local references
void push_local_reference_frame(void);
@@ -177,7 +136,6 @@ static ALWAYS_INLINE LispVal *UNWIND_AND_RETURN(StackFrame *frame,
unwind_to(frame);
return val;
}
noreturn void continue_unwinding(void);
noreturn void lisp_signal(LispVal *name, LispVal *data);
@@ -186,52 +144,6 @@ noreturn void lisp_signal(LispVal *name, LispVal *data);
*/
DECLARE_FUNCTION(backtrace, (void) );
#define UNWIND_PROTECT(body, cleanup) \
{ \
jmp_buf _internal_jb; \
if (setjmp(_internal_jb) == 0) { \
push_unwind_protect_frame(&_internal_jb); \
StackFrame *_internal_target = LISP_STACK_REF(); \
{body}; \
unwind_to(_internal_target); \
} else { \
{cleanup}; \
continue_unwinding(); \
} \
};
#define HANDLER_BIND1(exceptions, body, handler) \
{ \
jmp_buf _internal_jb; \
StackFrame *_internal_target = LISP_STACK_REF(); \
bool *enabled; \
if (setjmp(_internal_jb) == 0) { \
enabled = push_handler_bind_frame(&_internal_jb, (exceptions), 0); \
{body}; \
unwind_to(_internal_target); \
} else { \
*enabled = false; \
{handler}; \
*enabled = true; \
continue_unwinding(); \
} \
};
#define CONDITION_CASE1(exceptions, body, handler) \
{ \
jmp_buf _internal_jb; \
StackFrame *_internal_target = LISP_STACK_REF(); \
if (setjmp(_internal_jb) == 0) { \
push_handler_bind_frame(&_internal_jb, (exceptions), 0); \
{body}; \
unwind_to(_internal_target); \
} else { \
unwind_to(_internal_target); \
{handler}; \
CLEAR_EXCEPTION(); \
} \
};
DECLARE_SYMBOL(excessive_lisp_nesting_error);
MAKE_CONDITION_CLASS(excessive_lisp_nesting_error);