From 76b28c1dc02a669f0d0c1b513c7f9283047d50be Mon Sep 17 00:00:00 2001 From: Alexander Rosenberg Date: Wed, 28 Jan 2026 14:54:15 -0800 Subject: [PATCH] Fix some memory leaks --- Makefile | 2 +- src/gc.c | 23 +++++++++++++++++++++++ src/gc.h | 4 ++++ src/hashtable.c | 1 + src/lisp.c | 5 ++++- src/list.c | 1 + src/stack.c | 16 ++++++++++++++++ src/stack.h | 1 + 8 files changed, 51 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 5ee2e03..383301a 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ DEBUG=1 -LLVM_SAN=0 +LLVM_SAN=1 ifeq ($(DEBUG),1) DEBUG_CFLAGS=-g diff --git a/src/gc.c b/src/gc.c index 865c7bf..045ef59 100644 --- a/src/gc.c +++ b/src/gc.c @@ -307,3 +307,26 @@ void lisp_gc_now(struct timespec *restrict time_took) { ++total_gc_count; lisp_doing_gc = false; } + +void lisp_gc_teardown(void) { + assert(the_stack.depth == 0); + while (white_objects) { + free_object(white_objects->obj); + } + while (grey_objects) { + free_object(grey_objects->obj); + } + while (black_objects) { + free_object(black_objects->obj); + } + while (static_objects) { + struct GCObjectList *next = static_objects->next; + free(static_objects); + static_objects = next; + } + while (free_objects_list) { + struct GCObjectList *next = free_objects_list->next; + free(free_objects_list); + free_objects_list = next; + } +} diff --git a/src/gc.h b/src/gc.h index fe81348..b2e0a0f 100644 --- a/src/gc.h +++ b/src/gc.h @@ -33,4 +33,8 @@ void gc_move_to_set(void *val, ObjectGCSet new_set); void lisp_gc_now(struct timespec *restrict time_took); +// Unregister all static objects and prepare for shutdown +// The stack MUST be empty when this is called +void lisp_gc_teardown(void); + #endif diff --git a/src/hashtable.c b/src/hashtable.c index 8f5b010..02090e0 100644 --- a/src/hashtable.c +++ b/src/hashtable.c @@ -92,6 +92,7 @@ static void rehash(LispHashTable *ht, size_t new_size) { nb->value = cob->value; } } + lisp_free(old_data); } static void maybe_rehash(LispHashTable *ht) { diff --git a/src/lisp.c b/src/lisp.c index d1d1424..affb0a7 100644 --- a/src/lisp.c +++ b/src/lisp.c @@ -52,7 +52,10 @@ void lisp_init(void) { lisp_init_stack(); } -void lisp_shutdown(void) {} +void lisp_shutdown(void) { + lisp_gc_teardown(); + lisp_teardown_stack(); +} DEFUN(eval, "eval", (LispVal * form), "(form)", "") { if (!OBJECTP(form)) { diff --git a/src/list.c b/src/list.c index efc4b09..cc45a59 100644 --- a/src/list.c +++ b/src/list.c @@ -31,6 +31,7 @@ DEFUN(cons, "cons", (LispVal * car, LispVal *cdr), "(car cdr)", DEFUN(length, "length", (LispVal * list), "(list)", "") { // TODO type check + // TODO list may be circular return MAKE_FIXNUM(list_length(list)); } diff --git a/src/stack.c b/src/stack.c index 2a31b3f..eacee16 100644 --- a/src/stack.c +++ b/src/stack.c @@ -26,6 +26,21 @@ void lisp_init_stack(void) { the_stack.nogc_retval = Qnil; } +static void teardown_stack_frame(struct StackFrame *restrict frame) { + for (size_t i = 0; i < frame->local_refs.num_blocks; ++i) { + lisp_free(frame->local_refs.blocks[i]); + } + lisp_free(frame->local_refs.blocks); +} + +void lisp_teardown_stack(void) { + assert(the_stack.depth == 0); + for (size_t i = 0; i < the_stack.max_depth; ++i) { + teardown_stack_frame(&the_stack.frames[i]); + } + lisp_free(the_stack.frames); +} + void push_stack_frame(LispVal *name, LispVal *fobj, LispVal *args) { assert(the_stack.depth < the_stack.max_depth); struct StackFrame *frame = &the_stack.frames[the_stack.depth++]; @@ -164,6 +179,7 @@ void add_local_reference(LispVal *obj) { while ((cur = next_local_reference(&i))) { add_local_refs_for_object_sub_vals(seen_objs, cur); } + release_hash_table_no_gc(seen_objs); } void compact_stack_frame(struct StackFrame *restrict frame) { diff --git a/src/stack.h b/src/stack.h index c6945d2..85c53f7 100644 --- a/src/stack.h +++ b/src/stack.h @@ -41,6 +41,7 @@ static ALWAYS_INLINE struct StackFrame *LISP_STACK_TOP(void) { } void lisp_init_stack(void); +void lisp_teardown_stack(void); void push_stack_frame(LispVal *name, LispVal *fobj, LispVal *args); void pop_stack_frame(void); void add_local_reference_no_recurse(LispVal *obj);