From 2c43d65c7db47f2b682b8924669b8e6aef22ec0a Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Thu, 14 Jan 2021 19:43:30 -0800 Subject: Made all the heap garbage collection code optional, which saves some time and memory. --- bp.c | 4 +++- types.h | 5 ++++- vm.c | 48 +++++++++++++++++++++++++++++++++++++----------- vm.h | 4 +++- 4 files changed, 47 insertions(+), 14 deletions(-) diff --git a/bp.c b/bp.c index 51d34ca..3257d0f 100644 --- a/bp.c +++ b/bp.c @@ -120,7 +120,9 @@ static int process_file(def_t *defs, const char *filename, vm_op_t *pattern, uns } recycle_if_unused(&m); +#ifdef DEBUG_HEAP check(recycle_all_matches() == 0, "Memory leak: there should no longer be any matches in use at this point."); +#endif destroy_file(&f); return success; @@ -300,7 +302,7 @@ int main(int argc, char *argv[]) } if (flags & BP_JSON) printf("]\n"); -#ifdef FREE_ON_EXIT +#ifdef DEBUG_HEAP // This code frees up all residual heap-allocated memory. Since the program // is about to exit, this step is unnecessary. However, it is useful for // tracking down memory leaks. diff --git a/types.h b/types.h index 30a0e9e..65dcb5a 100644 --- a/types.h +++ b/types.h @@ -94,7 +94,10 @@ typedef struct match_s { struct match_s *child, *nextsibling; vm_op_t *op; // Intrusive linked list nodes for garbage collection: - struct match_s **atme, *next; + struct match_s *next; +#ifdef DEBUG_HEAP + struct match_s **atme; +#endif int refcount; } match_t; diff --git a/vm.c b/vm.c index ff62b4d..b0bc267 100644 --- a/vm.c +++ b/vm.c @@ -12,9 +12,11 @@ #include "utils.h" #include "vm.h" -// Linked list operations: -#define LL_PREPEND(head, node) do { (node)->atme = &(head); (node)->next = head; if (head) (head)->atme = &(node)->next; head = node; } while(0) -#define LL_REMOVE(node) do { *(node)->atme = (node)->next; if ((node)->next) (node)->next->atme = (node)->atme; } while(0) +#ifdef DEBUG_HEAP +// Doubly-linked list operations: +#define DLL_PREPEND(head, node) do { (node)->atme = &(head); (node)->next = head; if (head) (head)->atme = &(node)->next; head = node; } while(0) +#define DLL_REMOVE(node) do { *(node)->atme = (node)->next; if ((node)->next) (node)->next->atme = (node)->atme; } while(0) +#endif // Refcounting ownership-setting macros: #define ADD_OWNER(owner, m) do { owner = m; ++(m)->refcount; } while(0) @@ -26,7 +28,10 @@ // the `unused_matches` linked list so it can be reused without the need for // additional calls to malloc/free. Thus, it is an invariant that every match // object is in one of these two lists: -static match_t *in_use_matches = NULL, *unused_matches = NULL; +static match_t *unused_matches = NULL; +#ifdef DEBUG_HEAP +static match_t *in_use_matches = NULL; +#endif __attribute__((nonnull, pure)) static inline const char *next_char(file_t *f, const char *str); @@ -552,15 +557,27 @@ match_t *get_capture(match_t *m, const char **id) match_t *new_match(void) { match_t *m; + +#ifdef DEBUG_HEAP if (unused_matches) { m = unused_matches; - LL_REMOVE(m); + DLL_REMOVE(m); memset(m, 0, sizeof(match_t)); } else { m = new(match_t); } // Keep track of the object: - LL_PREPEND(in_use_matches, m); + DLL_PREPEND(in_use_matches, m); +#else + if (unused_matches) { + m = unused_matches; + unused_matches = unused_matches->next; + memset(m, 0, sizeof(match_t)); + } else { + m = new(match_t); + } +#endif + return m; } @@ -580,12 +597,20 @@ void recycle_if_unused(match_t **at_m) REMOVE_OWNERSHIP(m->child); REMOVE_OWNERSHIP(m->nextsibling); - LL_REMOVE(m); +#ifdef DEBUG_HEAP + DLL_REMOVE(m); // Remove from in_use_matches memset(m, 0, sizeof(match_t)); - LL_PREPEND(unused_matches, m); + DLL_PREPEND(unused_matches, m); +#else + memset(m, 0, sizeof(match_t)); + m->next = unused_matches; + unused_matches = m; +#endif + *at_m = NULL; } +#ifdef DEBUG_HEAP // // Force all match objects into the pool of unused match objects. // @@ -594,8 +619,8 @@ size_t recycle_all_matches(void) size_t count = 0; while (in_use_matches) { match_t *m = in_use_matches; - LL_REMOVE(m); - LL_PREPEND(unused_matches, m); + DLL_REMOVE(m); + DLL_PREPEND(unused_matches, m); ++count; } return count; @@ -610,12 +635,13 @@ size_t free_all_matches(void) recycle_all_matches(); while (unused_matches) { match_t *m = unused_matches; - LL_REMOVE(m); + DLL_REMOVE(m); free(m); ++count; } return count; } +#endif // // Deallocate memory associated with an op diff --git a/vm.h b/vm.h index 3e7526b..3777492 100644 --- a/vm.h +++ b/vm.h @@ -15,10 +15,12 @@ match_t *get_capture(match_t *m, const char **id); __attribute__((nonnull)) void destroy_op(vm_op_t *op); match_t *new_match(void); -size_t free_all_matches(void); __attribute__((nonnull)) void recycle_if_unused(match_t **at_m); +#ifdef DEBUG_HEAP +size_t free_all_matches(void); size_t recycle_all_matches(void); +#endif #endif // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1 -- cgit v1.2.3