From: Michael D. Lowis Date: Mon, 14 Dec 2020 19:29:44 +0000 (-0500) Subject: integrated deferred reference counter X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=bee4139ccfc51174662bea5caf5606f4014f1bd1;p=proto%2Faos.git integrated deferred reference counter --- diff --git a/bin/init.c b/bin/init.c index 99842e7..0e276ef 100644 --- a/bin/init.c +++ b/bin/init.c @@ -18,8 +18,9 @@ static void spawn(char* const argv[]) } } -int main(void) +int main(int argc, char** argv) { + (void)argc, (void)argv; if (getpid() != 1) return 1; if (chdir("/") < 0) return 1; sigfillset(&set); diff --git a/inc/liba.h b/inc/liba.h index 4ba18de..d18dbf1 100644 --- a/inc/liba.h +++ b/inc/liba.h @@ -140,6 +140,15 @@ static inline char* _getopt_(int* p_argc, char*** p_argv) { typedef char unique_id[( expr )?1:-1] #endif + +/* rename user's main routine so GC is auto-initialized */ +extern int usermain(int, char**); +#define main usermain + +void* gc_alloc(size_t sz); +void gc_addref(void* p); +void gc_delref(void* p); + void fatal(const char* fmt, ...); void warn(const char* fmt, ...); void esignal(int sig, void (*func)(int)); diff --git a/lib/a/gc.c b/lib/a/gc.c new file mode 100644 index 0000000..b60578d --- /dev/null +++ b/lib/a/gc.c @@ -0,0 +1,326 @@ +#include +#include +#ifdef __APPLE__ +#include +#endif + +/* the real main lives here */ +#undef main + +/* TODO: + * Process all increments *then* all decrements +*/ + +#define THOLD 0.65 +#define DELETED ((void*)(intptr_t)-1) +#define NUM_SLOTS 4u +#define LOG_SIZE 1023u +#define NUM_PRIMES (sizeof(Primes)/sizeof(unsigned int)) + +enum { + INCREMENT = 0, + DECREMENT = 1, +}; + +typedef struct { + uintptr_t index; + uintptr_t log[LOG_SIZE]; +} buffer_t; + +typedef struct { + uintptr_t refcount; + uintptr_t data[]; +} object_t; + +typedef struct { + void* slots[NUM_SLOTS]; +} bucket_t; + +static void obj_addref(object_t* obj); +static void obj_delref(object_t* obj); +static void gc_collect(void); +static void log_add(object_t* obj, int op); + +typedef struct { + size_t size; + size_t nslots; + size_t thold; + void** slots; +} hash_t; + +static void hash_init(hash_t* hash, int nslots); +static void hash_grow(hash_t* hash); +static void hash_add(hash_t* hash, void* entry); +static int hash_del(hash_t* hash, void* entry); + +static intptr_t* Stack_Bot = 0; +static buffer_t Log = {0}; +static hash_t ZCT; +static unsigned int Primes[] = { + 1543, 3079, 6151, 12289, 24593, + 49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469, + 12582917, 25165843, 50331653, 100663319, 201326611, 402653189, + 805306457, 1610612741 +}; + +/* Public Routines + ***************************************/ + +void* gc_alloc(size_t sz) +{ + sz = (sz / sizeof(intptr_t)) + ((sz % sizeof(intptr_t)) ? 1 : 0); + object_t* obj = ecalloc(1, sizeof(object_t) + sz); + obj->refcount = 0; + hash_add(&ZCT, obj->data); + return obj->data; +} + +void gc_addref(void* p) +{ + log_add((object_t*)p-1, INCREMENT); +} + +void gc_delref(void* p) +{ + log_add((object_t*)p-1, DECREMENT); +} + +int main(int argc, char** argv) +{ + (void)argc, (void)argv; + Stack_Bot = &(intptr_t){0}; + Log.index = 0; + hash_init(&ZCT,0); + return usermain(argc, argv); +} + +/* Private Routines + ***************************************/ + +static void gc_scan_region(hash_t* oldzct, intptr_t* p_beg, intptr_t* p_end) +{ + /* scan the region word by word */ + for (; p_beg < p_end; p_beg++) + { + void* ptr = (void*)*(volatile intptr_t*)p_beg; + if (hash_del(oldzct, ptr)) + { + hash_add(&ZCT, ptr); + } + } +} + +static void gc_scan_stack(hash_t* oldzct) +{ + /* save the registers on the stack */ + jmp_buf regs; + (void)setjmp(regs); + intptr_t* stack_top = (intptr_t*)®s; + + if (Stack_Bot < stack_top) + { + stack_top = (intptr_t*)(®s+1); + gc_scan_region(oldzct, (intptr_t*)Stack_Bot, (intptr_t*)stack_top); + } + else + { + gc_scan_region(oldzct, (intptr_t*)stack_top, (intptr_t*)Stack_Bot); + } +} + +static void gc_scan_globals(hash_t* oldzct) +{ +#ifdef __APPLE__ + intptr_t rbeg = (intptr_t )get_edata(); + intptr_t rend = (intptr_t )get_end(); +#else + extern intptr_t etext, end; + intptr_t rbeg = (intptr_t)&etext; + intptr_t rend = (intptr_t)&end; +#endif + intptr_t mask = sizeof(intptr_t)-1; + if (rbeg & mask) + { + rbeg = (rbeg & ~mask) + sizeof(intptr_t); + } + if (rend & mask) + { + rend = (rbeg & ~mask); + } +#ifdef __APPLE__ + printf("%p %p %p %p-%p\n", + (void*)get_etext(), + (void*)get_edata(), + (void*)get_end(), + (void*)rbeg, + (void*)rend + ); +#else + printf("%p %p %p-%p\n", + (void*)&etext, + (void*)&end, + (void*)rbeg, + (void*)rend + ); +#endif + gc_scan_region(oldzct, (intptr_t*)rbeg, (intptr_t*)rend); +} + +static void gc_collect(void) +{ + /* process the log */ + for (uintptr_t i = 0; i < Log.index; i++) + { + object_t* obj = (object_t*)(Log.log[i] & ~DECREMENT); + if (Log.log[i] & DECREMENT) + { + obj_delref(obj); + } + else + { + obj_addref(obj); + } + } + Log.index = 0; + + /* identify which ZCT entries are still live */ + hash_t oldzct = ZCT; + hash_init(&ZCT, oldzct.nslots); + gc_scan_stack(&oldzct); + gc_scan_globals(&oldzct); + + /* free the rest as they are dead for sure */ + size_t nslots = Primes[oldzct.nslots]; + void** slots = oldzct.slots; + for (size_t i = 0; i < nslots; i++) + { + if (slots[i] != NULL && slots[i] != DELETED) + { + free( ((object_t*)slots[i])-1 ); + } + } + free(slots); +} + +static void log_add(object_t* obj, int op) +{ + Log.log[Log.index++] = (((uintptr_t)obj) | op); + if (Log.index >= ((sizeof(Log)/sizeof(uintptr_t))-1)) + { + /* process the log */ + gc_collect(); + } +} + +static void obj_addref(object_t* obj) +{ + obj->refcount++; + if (obj->refcount == 1) + { + hash_del(&ZCT, obj->data); + } +} + +static void obj_delref(object_t* obj) +{ + obj->refcount--; + if (obj->refcount == 0) + { + hash_add(&ZCT, obj->data); + } +} + +/* Hashtable Routines + ***************************************/ + +static uint64_t hash64(uint64_t key) +{ + key = (~key) + (key << 21); + key = key ^ (key >> 24); + key = (key + (key << 3)) + (key << 8); + key = key ^ (key >> 14); + key = (key + (key << 2)) + (key << 4); + key = key ^ (key >> 28); + key = key + (key << 31); + return key; +} + +static void hash_init(hash_t* hash, int nslots) +{ + memset(hash, 0, sizeof(hash_t)); + hash->nslots = nslots; + hash->thold = (size_t)(Primes[hash->nslots] * THOLD); + hash->slots = calloc(Primes[hash->nslots], sizeof(void*)); +} + +static void hash_grow(hash_t* hash) +{ + size_t nslots = Primes[hash->nslots]; + void** slots = hash->slots; + hash->size = 0; + hash->nslots++; + hash->thold = (size_t)(Primes[hash->nslots] * THOLD); + hash->slots = calloc(Primes[hash->nslots], sizeof(void*)); + + for (size_t i = 0; i < nslots; i++) + { + if (slots[i] != NULL) + { + hash_add(hash, slots[i]); + } + } + free(slots); +} + +static void hash_add(hash_t* hash, void* entry) +{ + /* check if a resize is needed first */ + if (hash->size >= hash->thold) + { + hash_grow(hash); + } + + /* now hash and add the new item */ + uint64_t hcode = hash64((uint64_t)entry); + size_t index = (hcode % Primes[hash->nslots]); + while (1) + { + if (hash->slots[index] == entry) + { + break; + } + else if ((hash->slots[index] == NULL) || (hash->slots[index] == DELETED)) + { + hash->slots[index] = entry; + hash->size++; + break; + } + else + { + index = (index == Primes[hash->nslots]-1 ? 0 : index+1); + } + } +} + +static int hash_del(hash_t* hash, void* entry) +{ + uint64_t hcode = hash64((uint64_t)entry); + size_t index = (hcode % Primes[hash->nslots]); + + while (1) + { + if (hash->slots[index] == NULL) + { + return 0; + } + else if (hash->slots[index] == entry) + { + hash->slots[index] = DELETED; + return 1; + } + else + { + index = (index == Primes[hash->nslots]-1 ? 0 : index+1); + } + } +}