Begin working on a garbage collector.
This commit is contained in:
parent
71b6092654
commit
ec2421e5d7
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user