initial add
This commit is contained in:
commit
bcb1714206
36
.gitignore
vendored
Normal file
36
.gitignore
vendored
Normal file
@ -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
|
19
Makefile
Normal file
19
Makefile
Normal file
@ -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 $@
|
||||
|
9
README.md
Normal file
9
README.md
Normal file
@ -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.
|
||||
|
115
deque.c
Normal file
115
deque.c
Normal file
@ -0,0 +1,115 @@
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#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;
|
||||
}
|
||||
}
|
21
deque.h
Normal file
21
deque.h
Normal file
@ -0,0 +1,21 @@
|
||||
// Refuse
|
||||
// Copyright © 2016, Jeremy Tregunna, All Rights Reserved.
|
||||
|
||||
#ifndef __REFUSE__DEQUE_H__
|
||||
#define __REFUSE__DEQUE_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#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__
|
92
refuse.c
Normal file
92
refuse.c
Normal file
@ -0,0 +1,92 @@
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#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);
|
||||
}
|
33
refuse.h
Normal file
33
refuse.h
Normal file
@ -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 <stdint.h>
|
||||
#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__
|
112
tests.c
Normal file
112
tests.c
Normal file
@ -0,0 +1,112 @@
|
||||
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||
#pragma GCC diagnostic ignored "-Wtype-limits"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <check.h>
|
||||
#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;
|
||||
}
|
14
types.h
Normal file
14
types.h
Normal file
@ -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__
|
Loading…
Reference in New Issue
Block a user