From bcb1714206af1c63d5e8ecb4c4fe5129434e04c9 Mon Sep 17 00:00:00 2001 From: Jeremy Tregunna Date: Sun, 24 Apr 2016 22:14:47 -0600 Subject: [PATCH] initial add --- .gitignore | 36 +++++++++++++++++ Makefile | 19 +++++++++ README.md | 9 +++++ deque.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++ deque.h | 21 ++++++++++ refuse.c | 92 ++++++++++++++++++++++++++++++++++++++++++ refuse.h | 33 +++++++++++++++ tests.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++ types.h | 14 +++++++ 9 files changed, 451 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 deque.c create mode 100644 deque.h create mode 100644 refuse.c create mode 100644 refuse.h create mode 100644 tests.c create mode 100644 types.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5e2f679 --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +# Object files +*.o +*.ko +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su + +# Tests +tests diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6ee7deb --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +CC=clang +CFLAGS=-Wall -Wextra -Werror -Os -I/usr/local/include +OBJECTS=refuse.o deque.o +LIB=librefuse.a + +all: lib + +lib: $(OBJECTS) + $(AR) rcs $(LIB) $(OBJECTS) + +clean: + $(RM) $(OBJECTS) $(LIB) tests + +test: lib + $(CC) $(CFLAGS) -I/usr/local/include tests.c -o tests librefuse.a -L/usr/local/lib -lcheck + +.c.o: + $(CC) $(CFLAGS) -c $< -o $@ + diff --git a/README.md b/README.md new file mode 100644 index 0000000..ede7a5b --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# Refuse + +Refuse is an implementation of a garbage collector based on the idea of +deferring increments, allocating new objects as dead (reference count = 0), and +only marking objects as live, as references can be found. Additionally, the +object header to be used with the collector is packed for compactness. These +three things, should help increase performance substantially over standard +reference count garbage collectors. + diff --git a/deque.c b/deque.c new file mode 100644 index 0000000..f75fec6 --- /dev/null +++ b/deque.c @@ -0,0 +1,115 @@ +#include +#include +#include "deque.h" + +typedef struct refnode_s +{ + struct refnode_s* next; + struct refnode_s* prev; + refhdr_t* obj; +} refnode_t; + +struct refdeque_s +{ + refnode_t* head; + refnode_t* tail; +}; + +refdeque_t* refdeque_alloc() +{ + refdeque_t* deque = malloc(sizeof(refdeque_t)); + if(deque != NULL) { + deque->head = deque->tail = NULL; + } + return deque; +} + +void refdeque_release(refdeque_t* deque) +{ + free(deque); + deque = NULL; +} + +bool refdeque_empty(refdeque_t* deque) +{ + return deque->head == NULL; +} + +void refdeque_push_front(refdeque_t* deque, refhdr_t* hdr) +{ + refnode_t* node = malloc(sizeof(refnode_t)); + assert(node != NULL); + node->obj = hdr; + node->next = deque->head; + node->prev = NULL; + if(deque->tail == NULL) { + deque->head = deque->tail = node; + } else { + deque->head->prev = node; + deque->head = node; + } +} + +void refdeque_push_back(refdeque_t* deque, refhdr_t* hdr) +{ + refnode_t* node = malloc(sizeof(refnode_t)); + assert(node != NULL); + node->obj = hdr; + node->prev = deque->tail; + node->next = NULL; + if(deque->head == NULL) { + deque->head = deque->tail = node; + } else { + deque->tail->next = node; + deque->tail = node; + } +} + +refhdr_t* refdeque_pop_front(refdeque_t* deque) +{ + if(deque->head == NULL) { + return NULL; + } + + refhdr_t* hdr = deque->head->obj; + refnode_t* node = deque->head; + if(deque->head == deque->tail) { + deque->head = deque->tail = NULL; + } else { + deque->head = node->next; + } + free(node); + return hdr; +} + +refhdr_t* refdeque_pop_back(refdeque_t* deque) +{ + if(deque->tail == NULL) { + return NULL; + } + + refhdr_t* hdr = deque->tail->obj; + refnode_t* node = deque->tail; + if(deque->tail == deque->head) { + deque->tail = deque->head = NULL; + } else { + deque->tail = node->prev; + } + free(node); + return hdr; +} + +void refdeque_foreach(refdeque_t* deque, void (^each)(refhdr_t* hdr)) +{ + if(deque->head == NULL) { + return; + } + + refnode_t* current = deque->head; + while(current != NULL) { + if(each != NULL) { + each(current->obj); + } + current = current->next; + } +} diff --git a/deque.h b/deque.h new file mode 100644 index 0000000..3e9f7a6 --- /dev/null +++ b/deque.h @@ -0,0 +1,21 @@ +// Refuse +// Copyright © 2016, Jeremy Tregunna, All Rights Reserved. + +#ifndef __REFUSE__DEQUE_H__ +#define __REFUSE__DEQUE_H__ + +#include +#include "types.h" + +typedef struct refdeque_s refdeque_t; + +refdeque_t* refdeque_alloc(); +void refdeque_release(refdeque_t*); +bool refdeque_empty(refdeque_t*); +void refdeque_push_front(refdeque_t*, refhdr_t*); +void refdeque_push_back(refdeque_t*, refhdr_t*); +refhdr_t* refdeque_pop_front(refdeque_t*); +refhdr_t* refdeque_pop_back(refdeque_t*); +void refdeque_foreach(refdeque_t*, void (^)(refhdr_t*)); + +#endif // !__REFUSE__DEQUE_H__ \ No newline at end of file diff --git a/refuse.c b/refuse.c new file mode 100644 index 0000000..e5c2950 --- /dev/null +++ b/refuse.c @@ -0,0 +1,92 @@ +#include +#include +#include +#include "refuse.h" + +static inline refhdr_t* _refuse_header(void* ptr) +{ + return (refhdr_t*)((char*)ptr - sizeof(refhdr_t)); +} + +refuse_t* refuse_new(void) +{ + refuse_t* refuse = malloc(sizeof(refuse_t)); + refuse->dec = refdeque_alloc(); + refuse->mod = refdeque_alloc(); + + return refuse; +} + +void refuse_destroy(refuse_t* refuse) +{ + refdeque_release(refuse->dec); + refdeque_release(refuse->mod); + + free(refuse); + refuse = NULL; +} + +void refuse_reconcile(refuse_t* refuse) +{ + refhdr_t* h = refdeque_pop_front(refuse->mod); + while(h != NULL) { + if(h->new) { + h->new = 0; + } + + if(h->dirty) { + h->dirty = 0; + } else { + h->retainCount++; + } + h = refdeque_pop_front(refuse->mod); + } + + refhdr_t* hdr = refdeque_pop_front(refuse->dec); + while(hdr != NULL) { + hdr->retainCount--; + if(hdr->retainCount == 0) { + // Iterate children, delete children + free(hdr); + } + hdr = refdeque_pop_front(refuse->dec); + } +} + +void* refuse_alloc(__unused refuse_t* refuse, size_t size) +{ + refhdr_t* o = (refhdr_t*)calloc(sizeof(refhdr_t) + size, 1); + char* ptr = (char*)o + sizeof(refhdr_t); + + o->new = 1; + o->dirty = 0; + o->retainCount = 0; + + return ptr; +} + +void refuse_set_dirty(__unused refuse_t* refuse, void* ptr) +{ + refhdr_t* hdr = _refuse_header(ptr); + hdr->dirty = 1; +} + +void refuse_retain(refuse_t* refuse, void* ptr) +{ + refhdr_t* o; + char* cptr = (char*)ptr; + cptr -= sizeof(refhdr_t); + o = (refhdr_t*)cptr; + + if(o->dirty) { + return; + } + + refdeque_push_back(refuse->mod, o); +} + +void refuse_release(refuse_t* refuse, void* ptr) +{ + refhdr_t* hdr = _refuse_header(ptr); + refdeque_push_back(refuse->dec, hdr); +} \ No newline at end of file diff --git a/refuse.h b/refuse.h new file mode 100644 index 0000000..42ec902 --- /dev/null +++ b/refuse.h @@ -0,0 +1,33 @@ +// Refuse +// Copyright © 2016, Jeremy Tregunna, All Rights Reserved. + +#ifndef __REFUSE__REFUSE_H__ +#define __REFUSE__REFUSE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "types.h" +#include "deque.h" + +typedef struct +{ + refdeque_t* dec; + refdeque_t* mod; +} refuse_t; + +extern refuse_t* refuse_new(void); +extern void* refuse_alloc(refuse_t*, size_t); +extern void refuse_destroy(refuse_t*); +extern void refuse_set_dirty(refuse_t*, void*); +extern void refuse_reconcile(refuse_t*); +extern void refuse_retain(refuse_t*, void*); +extern void refuse_release(refuse_t*, void*); + +#ifdef __cplusplus +} +#endif + +#endif // !__REFUSE__REFUSE_H__ diff --git a/tests.c b/tests.c new file mode 100644 index 0000000..69bf5f5 --- /dev/null +++ b/tests.c @@ -0,0 +1,112 @@ +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#pragma GCC diagnostic ignored "-Wtype-limits" + +#include +#include +#include "refuse.h" +#include "deque.h" + +struct mock +{ + int a; +}; + +START_TEST(test_refuse_new) +{ + refuse_t* refuse = refuse_new(); + ck_assert(refuse != NULL); +} +END_TEST + +START_TEST(test_refuse_alloc_1) +{ + refuse_t* refuse = refuse_new(); + struct mock* t = refuse_alloc(refuse, sizeof(struct mock)); + ck_assert(t != NULL); + refhdr_t* hdr = (refhdr_t*)((char*)t - sizeof(refhdr_t)); + ck_assert_int_eq(hdr->retainCount, 0); + ck_assert_int_eq(hdr->new, 1); + ck_assert_int_eq(hdr->dirty, 0); +} +END_TEST + +START_TEST(test_refuse_retain_1) +{ + refuse_t* refuse = refuse_new(); + struct mock* t = refuse_alloc(refuse, sizeof(struct mock)); + t->a = 42; + refuse_retain(refuse, t); + refhdr_t* hdr = (refhdr_t*)((char*)t - sizeof(refhdr_t)); + ck_assert_int_eq(hdr->retainCount, 0); + ck_assert_int_eq(hdr->new, 1); + ck_assert_int_eq(hdr->dirty, 0); + refhdr_t* popped = refdeque_pop_back(refuse->mod); + struct mock* s = (struct mock*)((char*)popped + sizeof(refhdr_t)); + ck_assert_int_eq(s->a, 42); +} +END_TEST + +START_TEST(test_refuse_retain_2) +{ + refuse_t* refuse = refuse_new(); + struct mock* t = refuse_alloc(refuse, sizeof(struct mock)); + refuse_retain(refuse, t); + refhdr_t* hdr = (refhdr_t*)((char*)t - sizeof(refhdr_t)); + ck_assert_int_eq(hdr->retainCount, 0); + ck_assert_int_eq(hdr->new, 1); + ck_assert_int_eq(hdr->dirty, 0); + refuse_reconcile(refuse); + ck_assert_int_eq(hdr->new, 0); + ck_assert_int_eq(hdr->retainCount, 1); +} +END_TEST + +START_TEST(test_refuse_release_1) +{ + refuse_t* refuse = refuse_new(); + struct mock* t = refuse_alloc(refuse, sizeof(struct mock)); + refuse_retain(refuse, t); + refuse_reconcile(refuse); + refhdr_t* hdr = (refhdr_t*)((char*)t - sizeof(refhdr_t)); + ck_assert_int_eq(hdr->retainCount, 1); + refuse_release(refuse, t); + ck_assert_int_eq(hdr->retainCount, 1); + refuse_reconcile(refuse); + ck_assert_int_eq(hdr->retainCount, 0); +} +END_TEST + +static Suite* refuse_suite(void) +{ + Suite *s = suite_create("refuse"); + + TCase* tc_refuse_new = tcase_create("refuse_new"); + tcase_add_test(tc_refuse_new, test_refuse_new); + suite_add_tcase(s, tc_refuse_new); + + TCase* tc_refuse_alloc = tcase_create("refuse_alloc"); + tcase_add_test(tc_refuse_alloc, test_refuse_alloc_1); + suite_add_tcase(s, tc_refuse_alloc); + + TCase* tc_refuse_retain = tcase_create("refuse_retain"); + tcase_add_test(tc_refuse_retain, test_refuse_retain_1); + tcase_add_test(tc_refuse_retain, test_refuse_retain_2); + suite_add_tcase(s, tc_refuse_retain); + + TCase* tc_refuse_release = tcase_create("refuse_release"); + tcase_add_test(tc_refuse_release, test_refuse_release_1); + suite_add_tcase(s, tc_refuse_release); + + return s; +} + +int main(void) +{ + int number_failed; + Suite *s = refuse_suite(); + SRunner *sr = srunner_create(s); + srunner_run_all(sr, CK_NORMAL); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/types.h b/types.h new file mode 100644 index 0000000..9f92347 --- /dev/null +++ b/types.h @@ -0,0 +1,14 @@ +// Refuse +// Copyright © 2016, Jeremy Tregunna, All Rights Reserved. + +#ifndef __REFUSE__TYPES_H__ +#define __REFUSE__TYPES_H__ + +typedef struct refhdr_s +{ + unsigned int retainCount:30; + unsigned int new:1; + unsigned int dirty:1; +} refhdr_t; + +#endif // !__REFUSE__TYPES_H__ \ No newline at end of file