diff --git a/include/refcount.h b/include/refcount.h index 76a15a4..5f8b7a3 100644 --- a/include/refcount.h +++ b/include/refcount.h @@ -3,6 +3,12 @@ #include "result.h" +struct libab_ref_s; +struct libab_ref_count_s; + +typedef void (*libab_visitor_function_ptr)(struct libab_ref_count_s* , void*); +typedef void (*libab_visit_function_ptr)(void*, libab_visitor_function_ptr, void*); + /** * A struct for holding * the number of references @@ -10,6 +16,10 @@ * to free the value. */ struct libab_ref_count_s { + /** + * The value this reference holds. + */ + void* data; /** * The fucntion to free the value. * Can be NULL for no-op. @@ -25,6 +35,41 @@ struct libab_ref_count_s { * that still exist, even to a freed instance. */ int weak; + /** + * The number of outside references. + * This is used for garbage collection. + */ + int gc; + /** + * Previous pointer for garbage collection + * linked list. + */ + struct libab_ref_count_s* prev; + /** + * Next pointer for garbage collection + * linked list. + */ + struct libab_ref_count_s* next; + /** + * Function used to visit child containers, + * used by GC. + */ + libab_visit_function_ptr visit_children; +}; + +/** + * Struct used to create an interface + * for a set of objects to be collected. + */ +struct libab_ref_gc_list_s { + /** + * The head of the linked list. + */ + struct libab_ref_count_s* head; + /** + * The tail of the linked list. + */ + struct libab_ref_count_s* tail; }; /** @@ -44,14 +89,11 @@ struct libab_ref_s { * of how many references are pointing to the value. */ struct libab_ref_count_s* count; - /** - * The value this reference holds. - */ - void* data; }; typedef struct libab_ref_s libab_ref; typedef struct libab_ref_count_s libab_ref_count; +typedef struct libab_ref_gc_list_s libab_ref_gc_list; /** * Creates a new referene, using the given data and free function. @@ -69,6 +111,11 @@ libab_result libab_ref_new(libab_ref* ref, void* data, * @param ref the reference to initialize with null. */ void libab_ref_null(libab_ref* ref); +void libab_ref_gc_visit(libab_ref*, libab_visitor_function_ptr visitor, void*); +void libab_ref_gc_add(libab_ref* ref, + libab_visit_function_ptr visit_children, + libab_ref_gc_list* list); +void libab_ref_gc_run(libab_ref_gc_list* list); /** * Turns the given reference into a weak reference, * making it not keep the data allocated. diff --git a/src/refcount.c b/src/refcount.c index 2c35006..5066b07 100644 --- a/src/refcount.c +++ b/src/refcount.c @@ -7,10 +7,12 @@ libab_result libab_ref_new(libab_ref* ref, void* data, libab_result result = LIBAB_SUCCESS; ref->null = 0; ref->strong = 1; - ref->data = data; if ((ref->count = malloc(sizeof(*(ref->count))))) { + ref->count->data = data; ref->count->strong = ref->count->weak = 1; ref->count->free_func = free_func; + ref->count->prev = NULL; + ref->count->next = NULL; } else { result = LIBAB_MALLOC; } @@ -19,11 +21,81 @@ libab_result libab_ref_new(libab_ref* ref, void* data, void libab_ref_null(libab_ref* ref) { ref->null = 1; } +void _ref_gc_count_visit(libab_ref_count* ref, libab_visitor_function_ptr func, void* data) { + if(ref->strong && ref->visit_children) ref->visit_children(ref->data, func, data); +} +void libab_ref_gc_visit(libab_ref* ref, libab_visitor_function_ptr func, void* data) { + _ref_gc_count_visit(ref->count, func, data); +} + +void _libab_ref_gc_list_append(libab_ref_gc_list* list, + libab_ref_count* node) { + if(node->next) node->next->prev = node->prev; + if(node->prev) node->prev->next = node->next; + + node->prev = list->tail; + node->next = NULL; + if(list->head) { + list->tail->next = node; + } else { + list->head = node; + } + list->tail = node; +} +void libab_ref_gc_add(libab_ref* ref, + libab_visit_function_ptr visit_children, + libab_ref_gc_list* list) { + ref->count->visit_children = visit_children; + _libab_ref_gc_list_append(list, ref->count); +} + +void _ref_gc_decrement(libab_ref_count* count, void* data) { + count->gc--; +} +void _ref_gc_save(libab_ref_count* count, void* data) { + libab_ref_gc_list* list = data; + if(count->gc != -1) { + count->gc = -1; + _libab_ref_gc_list_append(list, count); + _ref_gc_count_visit(count, _ref_gc_save, data); + } +} + +void libab_ref_gc_run(libab_ref_gc_list* list) { + libab_ref_gc_list safe; + libab_ref_count* head; + size_t count = 0; + + head = list->head; + while(head) { + head->gc = head->weak; + } + + head = list->head; + while(head) { + _ref_gc_count_visit(head, _ref_gc_decrement, NULL); + } + + head = list->head; + while(head) { + _ref_gc_count_visit(head, _ref_gc_save, &safe); + } + + head = list->head; + while(head) { + count++; + } + printf("Can free %d\n", count); + + list->head = safe.head; + list->tail = safe.tail; +} + void _libab_ref_changed(libab_ref* ref) { if (ref->count->strong == 0) { ref->count->strong--; if (ref->count->free_func) { - ref->count->free_func(ref->data); + ref->count->free_func(ref->count->data); } } if (ref->count->weak == 0) { @@ -67,7 +139,7 @@ void libab_ref_data_free(void* data) { free(data); } void* libab_ref_get(const libab_ref* ref) { void* to_return = NULL; if (!ref->null && ref->count->strong > 0) { - to_return = ref->data; + to_return = ref->count->data; } return to_return; }