]> git.mdlowis.com Git - proto/aos.git/commitdiff
integrated deferred reference counter
authorMichael D. Lowis <mike.lowis@gentex.com>
Mon, 14 Dec 2020 19:29:44 +0000 (14:29 -0500)
committerMichael D. Lowis <mike.lowis@gentex.com>
Mon, 14 Dec 2020 19:29:44 +0000 (14:29 -0500)
bin/init.c
inc/liba.h
lib/a/gc.c [new file with mode: 0644]

index 99842e7cb14e0396593f59c7e7a32c817468f828..0e276ef549125c2efb4904bdc5e2868991863627 100644 (file)
@@ -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);
index 4ba18de2c5e2e8ec607216c1702f9d4fc6d85929..d18dbf18bd11f3f72d8f12106e93452365b99e49 100644 (file)
@@ -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 (file)
index 0000000..b60578d
--- /dev/null
@@ -0,0 +1,326 @@
+#include <liba.h>
+#include <setjmp.h>
+#ifdef __APPLE__
+#include <mach-o/getsect.h>
+#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*)&regs;
+
+    if (Stack_Bot < stack_top)
+    {
+        stack_top = (intptr_t*)(&regs+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);
+        }
+    }
+}