static size_t gc_sweep_page(pic_state *pic, struct heap_page *page) { union header *bp, *p, *head = NULL, *tail = NULL; struct object *obj; size_t alive = 0, nunits; for (bp = page->u.basep; ; bp = bp->s.ptr) { p = bp + (bp->s.size ? bp->s.size : 1); /* first bp's size is 0, so force advnce */ while (p != bp->s.ptr) { if (p < page->u.basep || page->u.basep + PAGE_UNITS <= p) { goto escape; } obj = (struct object *) p; nunits = unitsof(obj_type(pic, obj)); if (obj->u.basic.tt & GC_MARK) { obj->u.basic.tt &= ~GC_MARK; alive += nunits; } else { gc_finalize_object(pic, obj); if (head == NULL) { head = p; } if (tail != NULL) { tail->s.ptr = p; } tail = p; tail->s.size = nunits; tail->s.ptr = NULL; /* We can safely reuse ptr field of dead object */ } p += nunits; } } escape: /* free! */ while (head != NULL) { p = head; head = head->s.ptr; free_chunk(pic, p); } return alive; }
void pic_gc(pic_state *pic) { struct context *cxt; size_t j; khash_t(oblist) *s = &pic->oblist; struct symbol *sym; int it; struct object *obj, *prev, *next; assert(pic->gc_attrs == NULL); if (! pic->gc_enable) { return; } /* scan objects */ for (cxt = pic->cxt; cxt != NULL; cxt = cxt->prev) { if (cxt->fp) gc_mark_object(pic, (struct object *)cxt->fp); if (cxt->sp) gc_mark_object(pic, (struct object *)cxt->sp); if (cxt->irep) gc_mark_object(pic, (struct object *)cxt->irep); gc_mark(pic, cxt->conts); } for (j = 0; j < pic->ai; ++j) { gc_mark_object(pic, (struct object *)pic->arena[j]); } gc_mark(pic, pic->globals); gc_mark(pic, pic->halt); /* scan weak references */ do { struct object *key; pic_value val; int it; khash_t(attr) *h; struct attr *attr; j = 0; attr = pic->gc_attrs; while (attr != NULL) { h = &attr->hash; for (it = kh_begin(h); it != kh_end(h); ++it) { if (! kh_exist(h, it)) continue; key = kh_key(h, it); val = kh_val(h, it); if (is_alive(key)) { if (pic_obj_p(pic, val) && ! is_alive((struct object *) pic_ptr(pic, val))) { gc_mark(pic, val); ++j; } } } attr = attr->prev; } } while (j > 0); /* reclaim dead weak references */ while (pic->gc_attrs != NULL) { khash_t(attr) *h = &pic->gc_attrs->hash; for (it = kh_begin(h); it != kh_end(h); ++it) { if (! kh_exist(h, it)) continue; obj = kh_key(h, it); if (! is_alive(obj)) { kh_del(attr, h, it); } } pic->gc_attrs = pic->gc_attrs->prev; } for (it = kh_begin(s); it != kh_end(s); ++it) { if (! kh_exist(s, it)) continue; sym = kh_val(s, it); if (sym && ! is_alive((struct object *)sym)) { kh_del(oblist, s, it); } } /* reclaim dead objects */ for (prev = &pic->gc_head, obj = prev->next; obj != &pic->gc_head; prev = obj, obj = next) { next = obj->next; if (is_alive(obj)) { unmark(obj); } else { gc_finalize_object(pic, obj); pic_free(pic, obj); prev->next = next; obj = prev; } } }