Add basic mark-sweep
authorEvgenii Akentev <hi@ak3n.com>
Tue, 10 Sep 2024 08:55:53 +0000 (12:55 +0400)
committerEvgenii Akentev <hi@ak3n.com>
Tue, 10 Sep 2024 08:55:53 +0000 (12:55 +0400)
.gitignore [new file with mode: 0644]
mark-sweep/basic.c [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..cba7efc
--- /dev/null
@@ -0,0 +1 @@
+a.out
diff --git a/mark-sweep/basic.c b/mark-sweep/basic.c
new file mode 100644 (file)
index 0000000..1da70d4
--- /dev/null
@@ -0,0 +1,172 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+// To simplify things sizes are statically defined
+#define FIELDS_SIZE 4
+#define HEAP_SIZE 8
+#define ROOTS_SIZE 4
+#define WORKLIST_SIZE 100
+
+struct Object;
+
+typedef struct Object {
+  int val;
+  int field_size;
+  int isMarked;
+  struct Object* fields[FIELDS_SIZE];
+} Object;
+
+typedef struct {
+  int size;
+  Object** objects;
+} Heap;
+
+typedef struct {
+  int size;
+  Object* objects[ROOTS_SIZE];
+} Roots;
+
+typedef struct {
+  int current;
+  Object* objects[WORKLIST_SIZE];
+} Worklist;
+
+void push(Worklist* w, Object* o) {
+  w->current += 1;
+  w->objects[w->current] = o;
+}
+
+int isEmpty(Worklist *w) {
+  if (w->current == -1) return 1;
+
+  return 0;
+}
+
+Object* pop(Worklist *w) {
+  Object* item = w->objects[w->current];
+  w->current -= 1;
+  return item;
+}
+
+Heap* mkHeap () {
+  Heap* h = malloc(sizeof(Heap));
+  h->size = HEAP_SIZE;
+  h->objects = calloc(h->size, sizeof(Object*));
+  return h;
+}
+
+int heapObjectsNumber(Heap* h) {
+  int c = 0;
+  for (int i = 0; i < h->size; i++) {
+    if (h->objects[i] != NULL) c++;
+  }
+  return c;
+}
+
+void freeHeap (Heap* h) {
+  for (int i = 0; i < h->size; i++) {
+    if (h->objects[i] != NULL) {
+      free(h->objects[i]);
+    }
+  }
+  free(h->objects);
+  free(h);
+}
+
+Object* allocate(int val, Heap* h) {
+  Object* p = NULL;
+  for (int i = 0; i < h->size; i++) {
+    if (h->objects[i] == NULL && p == NULL) {
+      p = malloc(sizeof(Object));
+      *p = (Object){val, FIELDS_SIZE, 0, NULL};
+      h->objects[i] = p;
+    }
+  }
+  return p;
+}
+
+void mark(Heap* h, Roots* roots, Worklist* w) {
+  while (isEmpty(w) == 0) {
+    Object *ref = pop(w);
+    for (int i = 0; i < ref->field_size; i++) {
+       Object* child = ref->fields[i];
+      if (child != NULL && child->isMarked != 1) {
+        child->isMarked = 1;
+        push(w, child);
+      }
+    }
+  }
+}
+
+void markFromRoots(Heap* h, Roots* roots) {
+  Worklist worklist;
+  worklist.current = -1;
+
+  for (int i = 0; i < roots->size; i++) {
+    Object* ref = roots->objects[i];
+    if (ref != NULL && ref->isMarked != 1) {
+      ref->isMarked = 1;
+      push(&worklist, ref);
+      mark(h, roots, &worklist);
+    }
+  }
+}
+
+void sweep(Heap *h) {
+  for (int i = 0; i < h->size; i++) {
+    if (h->objects[i] != NULL) {
+      if (h->objects[i]->isMarked == 1) {
+        h->objects[i]->isMarked = 0; 
+      } else {
+        free(h->objects[i]);
+        h->objects[i] = NULL;
+      }
+    }
+  }
+}
+
+void collect(Heap* h, Roots* roots) {
+  markFromRoots(h, roots);
+  sweep(h);
+}
+
+Object* new(int val, Heap* h, Roots* roots) {
+  Object* ref = allocate(val, h);
+  if (ref == NULL) {
+    printf("Running collect");
+    collect(h, roots);
+    ref = allocate(val, h);
+    if (ref == NULL) {
+      printf("Out of memory\n");
+      exit(0);
+    }
+  }
+  return ref;
+}
+
+int main() {
+  Heap* h = mkHeap();
+  Roots roots;
+  roots.size = ROOTS_SIZE;
+  for (int i = 0; i < roots.size; i++) {
+    roots.objects[i] = NULL;
+  }
+
+  // mutator
+  roots.objects[0] = new(7, h, &roots);
+  roots.objects[0] = new(5, h, &roots);
+
+  roots.objects[0]->fields[0] = new(8, h, &roots);
+
+  printf("heap size before collect: %d\n", heapObjectsNumber(h));
+  printf("heap third obj value: %d\n", h->objects[2]->val);
+
+  collect(h, &roots);
+
+  printf("heap second obj value: %d\n", h->objects[1]->val);
+
+  printf("heap size after collect: %d\n", heapObjectsNumber(h));
+
+  freeHeap(h);
+  return 0;
+}