diff --git a/include/gc.h b/include/gc.h index c27f818..d50a02f 100644 --- a/include/gc.h +++ b/include/gc.h @@ -33,6 +33,10 @@ void libab_gc_list_init(libab_gc_list* list); * @param data the data to pass to the visitor. */ void libab_gc_visit_children(struct libab_ref_s* ref, libab_visitor_function_ptr visitor, void* data); +/** + * Applies the given visitor function to this reference. + */ +void libab_gc_visit(struct libab_ref_s* ref, libab_visitor_function_ptr visitor, void* data); /** * Adds the given reference to the given garbage collection list, * and specifies a function used to reach its children. diff --git a/include/refcount_internal.h b/include/refcount_internal.h new file mode 100644 index 0000000..cd9a371 --- /dev/null +++ b/include/refcount_internal.h @@ -0,0 +1,8 @@ +#ifndef LIBABACUS_REFCOUNT_INTERNAL_H +#define LIBABACUS_REFCOUNT_INTERNAL_H + +#include "refcount.h" + +void libab_ref_count_changed(libab_ref_count* count); + +#endif diff --git a/src/gc.c b/src/gc.c index 5e6bacb..da7d3d9 100644 --- a/src/gc.c +++ b/src/gc.c @@ -1,7 +1,9 @@ #include "gc.h" #include "refcount.h" #include +#include #include +#include "refcount_internal.h" void libab_gc_list_init(libab_gc_list* list) { memset(&list->head_sentinel, 0, sizeof(list->head_sentinel)); @@ -17,6 +19,12 @@ void libab_gc_visit_children(libab_ref* ref, libab_visitor_function_ptr func, vo if(!ref->null) _gc_count_visit_children(ref->count, func, data); } +void libab_gc_visit(struct libab_ref_s* ref, libab_visitor_function_ptr visitor, void* data) { + if(!ref->null) { + visitor(ref->count, data); + } +} + void _libab_gc_list_append(libab_gc_list* list, libab_ref_count* node) { libab_ref_count* before; @@ -36,11 +44,11 @@ void libab_gc_add(libab_ref* ref, } void _gc_decrement(libab_ref_count* count, void* data) { - count->gc--; + if(count->visit_children) count->gc--; } void _gc_save(libab_ref_count* count, void* data) { libab_gc_list* list = data; - if(count->visit_children && count->gc != -1) { + if(count->visit_children && count->gc >= 0) { count->gc = -1; _libab_gc_list_append(list, count); _gc_count_visit_children(count, _gc_save, data); @@ -50,31 +58,45 @@ void _gc_save(libab_ref_count* count, void* data) { void libab_gc_run(libab_gc_list* list) { libab_gc_list safe; libab_ref_count* head; - size_t count = 0; #define ITERATE(CODE) head = list->head_sentinel.next; \ while(head != &list->tail_sentinel) { \ + libab_ref_count* node = head; \ CODE;\ head = head->next; \ } libab_gc_list_init(&safe); - ITERATE(head->gc = head->weak); - ITERATE(_gc_count_visit_children(head, _gc_decrement, NULL)); - ITERATE(printf("%d outside references\n", head->gc)); - ITERATE(_gc_count_visit_children(head, _gc_save, &safe)); - ITERATE(count++); + ITERATE(node->gc = node->weak); + ITERATE(_gc_count_visit_children(node, _gc_decrement, NULL)); + + head = list->head_sentinel.next; + while(head != &list->tail_sentinel) { + if(head->gc > 0) { + _gc_save(head, &safe); + head = &list->head_sentinel; + } + head = head->next; + } - printf("Can free %d\n", count); + ITERATE( + node->weak = -1; + node->strong = -1; + if(node->free_func) node->free_func(node->data); + ); + + while ((head = list->head_sentinel.next) != &list->tail_sentinel) { + head->prev->next = head->next; + head->next->prev = head->prev; + free(head); + } if(safe.head_sentinel.next != &safe.tail_sentinel) { - printf("Safe isn't empty!\n"); list->head_sentinel.next = safe.head_sentinel.next; list->head_sentinel.next->prev = &list->head_sentinel; list->tail_sentinel.prev = safe.tail_sentinel.prev; list->tail_sentinel.prev->next = &list->tail_sentinel; } else { - printf("Safe is empty!\n"); list->head_sentinel.next = &list->tail_sentinel; list->tail_sentinel.prev = &list->head_sentinel; } diff --git a/src/refcount_internal.c b/src/refcount_internal.c new file mode 100644 index 0000000..97d383d --- /dev/null +++ b/src/refcount_internal.c @@ -0,0 +1,16 @@ +#include "refcount_internal.h" +#include + +void libab_ref_count_changed(libab_ref_count* count) { + if (count->strong == 0) { + count->strong--; + if (count->free_func) { + count->free_func(count->data); + } + } + if (count->weak == 0) { + if(count->prev) count->prev->next = count->next; + if(count->next) count->next->prev = count->prev; + free(count); + } +} diff --git a/src/util.c b/src/util.c index e024357..bfe25c0 100644 --- a/src/util.c +++ b/src/util.c @@ -189,13 +189,14 @@ libab_result libab_instantiate_basetype(libab_basetype* to_instantiate, void _gc_visit_table_entry(libab_table_entry* entry, libab_visitor_function_ptr visitor, void* data) { if (entry->variant == ENTRY_VALUE) { - libab_gc_visit_children(&entry->data_u.value, visitor, data); + libab_gc_visit(&entry->data_u.value, visitor, data); } } void _gc_visit_table_trie(libab_trie_node* parent, libab_visitor_function_ptr visitor, void* data) { - ll_node* head = parent->values.head; + ll_node* head; if(parent == NULL) return; + head = parent->values.head; _gc_visit_table_trie(parent->child, visitor, data); _gc_visit_table_trie(parent->next, visitor, data); while(head != NULL) { @@ -206,7 +207,8 @@ void _gc_visit_table_trie(libab_trie_node* parent, libab_visitor_function_ptr vi void _gc_visit_table_children(void* parent, libab_visitor_function_ptr visitor, void* data) { libab_table* table = parent; - libab_gc_visit_children(&table->parent, visitor, data); + libab_gc_visit(&table->parent, visitor, data); + _gc_visit_table_trie(table->trie.head, visitor, data); } libab_result libab_create_table(libab* ab, libab_ref* into, libab_ref* parent) { @@ -234,7 +236,7 @@ libab_result libab_create_table(libab* ab, libab_ref* into, libab_ref* parent) { void _gc_visit_value_children(void* val, libab_visitor_function_ptr visitor, void* data) { libab_value* value = val; - libab_gc_visit_children(&value->data, visitor, data); + libab_gc_visit(&value->data, visitor, data); } libab_result libab_create_value_ref(libab* ab, libab_ref* into, @@ -290,7 +292,7 @@ libab_result libab_create_value_raw(libab* ab, libab_ref* into, void _gc_visit_function_children(void* function, libab_visitor_function_ptr visitor, void* data) { libab_function* func = function; - libab_gc_visit_children(&func->scope, visitor, data); + libab_gc_visit(&func->scope, visitor, data); } libab_result libab_create_function_internal(libab* ab, libab_ref* into,