git @ Cat's Eye Technologies Bhuna / master src / lib / gc.c
master

Tree @master (Download .tar.gz)

gc.c @masterraw · history · blame

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "mem.h"
#include "activation.h"
#include "value.h"

#include "list.h"
#include "closure.h"
#include "gc.h"
#include "vm.h"
#include "process.h"

#ifdef DEBUG
extern int trace_gc;
#endif

int gc_trigger = DEFAULT_GC_TRIGGER;
int gc_target;

extern struct activation *a_head;

extern struct s_value *sv_head;

/*
 * Garbage collector.  Not a cheesy little reference counter, but
 * the real meat-and-potatoes mark-and-sweep.  (Which we need,
 * because an activation record can contain a closure which contain
 * an activation record, and refcounts can't handle that cycle.)
 *
 * This is not particularly sophisticated; I'm more concerned with
 * correctness than performance here.
 */
static void activation_mark(struct activation *a);

static void
value_mark(struct value v)
{
	struct list *l;

	if (!(v.type & VALUE_STRUCTURED) || v.v.s->admin & ADMIN_MARKED)
		return;

#ifdef DEBUG
	if (trace_gc > 1) {
		printf("[GC] MARKING VALUE ");
		value_print(v);
		printf(" AS REACHABLE\n");
	}
#endif

	v.v.s->admin |= ADMIN_MARKED;
	switch (v.type) {
	case VALUE_LIST:
		for (l = v.v.s->v.l; l != NULL; l = l->next) {
			value_mark(l->value);
		}
		break;
	case VALUE_CLOSURE:
		activation_mark(v.v.s->v.k->ar);
		break;
	case VALUE_DICT:
		/* XXX for each key in v->v.d, value_mark(d[k]) */
		break;
	default:
		/*
		 * No need to go through other values as they
		 * are not containers.
		 */
		break;
	}
}

static void
activation_mark(struct activation *a)
{
	int i;

	if (a == NULL)
		return;
	if (a->admin & AR_ADMIN_MARKED) {
#ifdef DEBUG
		if (trace_gc > 1) {
			printf("[GC] ar ");
			activation_dump(a, 0);
			printf(" aready marked\n");
		}
#endif
		return;
	}

#ifdef DEBUG
	if (trace_gc > 1) {
		printf("[GC] MARKING AR ");
		activation_dump(a, 0);
		printf(" AS REACHABLE\n");
	}
#endif

	a->admin |= AR_ADMIN_MARKED;
	activation_mark(a->caller);
	activation_mark(a->enclosing);
	for (i = 0; i < a->size; i++) {
		value_mark(VALARY(a, i));
	}
}

void
gc(void)
{
	struct process *p;
	struct activation *a, *a_next;
	struct activation *ta_head = NULL;

	struct value *vsc;
	struct s_value *sv, *sv_next, *tsv_head = NULL;

	/*
	 * Mark...
	 */
	for (p = run_head; p != NULL; p = p->next) {
		activation_mark(p->vm->current_ar);
		for (vsc = p->vm->vstack; vsc < p->vm->vstack_ptr; vsc++)
			value_mark(*vsc);
	}

	/*
	 * ...and sweep
	 */
	for (a = a_head; a != NULL; a = a_next) {
		a_next = a->next;
		if (a->admin & AR_ADMIN_MARKED) {
			a->admin &= ~AR_ADMIN_MARKED;
			a->next = ta_head;
			ta_head = a;
		} else {
#ifdef DEBUG
			if (trace_gc > 1) {
				printf("[GC] FOUND UNREACHABLE AR ");
				activation_dump(a, 0);
				printf("\n");
			}
#endif
			activation_free_from_heap(a);
		}
	}

	a_head = ta_head;

	for (sv = sv_head; sv != NULL; sv = sv_next) {
		sv_next = sv->next;
		if (sv->admin & ADMIN_MARKED || sv->admin & ADMIN_PERMANENT) {
			sv->admin &= ~ADMIN_MARKED;
			sv->next = tsv_head;
			tsv_head = sv;
		} else {
#ifdef DEBUG
			if (trace_gc > 1) {
				printf("[GC] FOUND UNREACHABLE VALUE ");
				/*value_print(v);*/
				printf("\n");
			}
#endif
			s_value_free(sv);
		}
	}

	sv_head = tsv_head;
}