/* * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers * Copyright (c) 1991 by Xerox Corporation. All rights reserved. * * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. * * Permission is hereby granted to copy this garbage collector for any purpose, * provided the above notices are retained on all copies. */ #define DEBUG #undef DEBUG #include <stdio.h> #include "gc←private.h" /**/ /* allocate/free routines for heap blocks /* Note that everything called from outside the garbage collector /* should be prepared to abort at any point as the result of a signal. /**/ /* * Free heap blocks are kept on a list sorted by address. * The hb←hdr.hbh←sz field of a free heap block contains the length * (in bytes) of the entire block. * Neighbors are coalesced. */ # define MAX←BLACK←LIST←ALLOC (2*HBLKSIZE) /* largest block we will allocate starting on a black */ /* listed block. Must be >= HBLKSIZE. */ struct hblk * GC←hblkfreelist = 0; struct hblk *GC←savhbp = (struct hblk *)0; /* heap block preceding next */ /* block to be examined by */ /* GC←allochblk. */ /* Initialize hdr for a block containing the indicated size and */ /* kind of objects. */ static setup←header(hhdr, sz, kind) register hdr * hhdr; word sz; /* object size in words */ int kind; { /* Set size, kind and mark proc fields */ hhdr -> hb←sz = sz; hhdr -> hb←obj←kind = kind; hhdr -> hb←mark←proc = GC←obj←kinds[kind].ok←mark←proc; /* Add description of valid object pointers */ GC←add←map←entry(sz); hhdr -> hb←map = GC←obj←map[sz > MAXOBJSZ? 0 : sz]; /* Clear mark bits */ GC←clear←hdr←marks(hhdr); } /* * Allocate (and return pointer to) a heap block * for objects of size |sz| words. * * NOTE: We set obj←map field in header correctly. * Caller is resposnsible for building an object freelist in block. * * We clear the block if it is destined for large objects, and if * kind requires that newly allocated objects be cleared. */ struct hblk * GC←allochblk(sz, kind) word sz; int kind; { register struct hblk *thishbp; register hdr * thishdr; /* Header corr. to thishbp */ register struct hblk *hbp; register hdr * hhdr; /* Header corr. to hbp */ struct hblk *prevhbp; register hdr * phdr; /* Header corr. to prevhbp */ signed←word size←needed; /* number of bytes in requested objects */ signed←word size←avail; /* bytes available in this block */ bool first←time = TRUE; size←needed = WORDS←TO←BYTES(sz); size←needed = (size←needed+HDR←BYTES+HBLKSIZE-1) & ~HBLKMASK; /* search for a big enough block in free list */ hbp = GC←savhbp; hhdr = HDR(hbp); for(;;) { prevhbp = hbp; phdr = hhdr; hbp = (prevhbp == 0? GC←hblkfreelist : phdr->hb←next); hhdr = HDR(hbp); if( prevhbp == GC←savhbp && !first←time) { return(0); } first←time = FALSE; if( hbp == 0 ) continue; size←avail = hhdr->hb←sz; if (size←avail < size←needed) continue; /* If the next heap block is obviously better, go on. */ /* This prevents us from disassembling a single large block */ /* to get tiny blocks. */ { word next←size; thishbp = hhdr -> hb←next; if (thishbp == 0) thishbp = GC←hblkfreelist; thishdr = HDR(thishbp); next←size = thishdr -> hb←sz; if (next←size < size←avail && next←size >= size←needed && !GC←is←black←listed(thishbp, (word)size←needed)) { continue; } } if ( kind != PTRFREE || size←needed > MAX←BLACK←LIST←ALLOC) { struct hblk * lasthbp = hbp; while (size←avail >= size←needed && (thishbp = GC←is←black←listed(lasthbp, (word)size←needed))) { lasthbp = thishbp; } size←avail -= (ptr←t)lasthbp - (ptr←t)hbp; thishbp = lasthbp; if (size←avail >= size←needed && thishbp != hbp) { /* Split the block at thishbp */ GC←install←header(thishbp); thishdr = HDR(thishbp); /* GC←invalidate←map not needed, since we will */ /* allocate this block. */ thishdr -> hb←next = hhdr -> hb←next; thishdr -> hb←sz = size←avail; hhdr -> hb←sz = (ptr←t)thishbp - (ptr←t)hbp; hhdr -> hb←next = thishbp; /* Advance to thishbp */ prevhbp = hbp; phdr = hhdr; hbp = thishbp; hhdr = thishdr; } else if (size←avail == 0 && size←needed == HBLKSIZE && prevhbp != 0) { static unsigned count = 0; /* The block is completely blacklisted. We need */ /* to drop some such blocks, since otherwise we spend */ /* all our time traversing them if pointerfree */ /* blocks are unpopular. */ /* A dropped block will be reconsidered at next GC. */ if ((++count & 3) == 0) { /* Allocate and drop the block */ phdr -> hb←next = hhdr -> hb←next; GC←install←counts(hbp, hhdr->hb←sz); setup←header(hhdr, BYTES←TO←WORDS(hhdr->hb←sz - HDR←BYTES), PTRFREE); if (GC←savhbp == hbp) GC←savhbp = prevhbp; /* Restore hbp to point at free block */ hbp = prevhbp; hhdr = phdr; if (hbp == GC←savhbp) first←time = TRUE; } } } if( size←avail >= size←needed ) { /* found a big enough block */ /* let thishbp --> the block */ /* set prevhbp, hbp to bracket it */ thishbp = hbp; thishdr = hhdr; if( size←avail == size←needed ) { hbp = hhdr->hb←next; hhdr = HDR(hbp); } else { hbp = (struct hblk *) (((unsigned)thishbp) + size←needed); GC←install←header(hbp); hhdr = HDR(hbp); GC←invalidate←map(hhdr); hhdr->hb←next = thishdr->hb←next; hhdr->hb←sz = size←avail - size←needed; } /* remove *thishbp from hblk freelist */ if( prevhbp == 0 ) { GC←hblkfreelist = hbp; } else { phdr->hb←next = hbp; } /* save current list search position */ GC←savhbp = hbp; break; } } /* Clear block if necessary */ if (sz > MAXOBJSZ && GC←obj←kinds[kind].ok←init) { bzero((char *)thishbp + HDR←BYTES, (int)(size←needed - HDR←BYTES)); } /* Set up header */ setup←header(thishdr, sz, kind); /* Add it to map of valid blocks */ GC←install←counts(thishbp, (word)size←needed); return( thishbp ); } /* * Free a heap block. * * Coalesce the block with its neighbors if possible. * * All mark words are assumed to be cleared. */ void GC←freehblk(p) register struct hblk *p; { register hdr *phdr; /* Header corresponding to p */ register struct hblk *hbp, *prevhbp; register hdr *hhdr, *prevhdr; register signed←word size; /* GC←savhbp may become invalid due to coalescing. Clear it. */ GC←savhbp = (struct hblk *)0; phdr = HDR(p); size = phdr->hb←sz; size = ((WORDS←TO←BYTES(size)+HDR←BYTES+HBLKSIZE-1) & (~HBLKMASK)); GC←remove←counts(p, (word)size); phdr->hb←sz = size; GC←invalidate←map(phdr); prevhbp = 0; hbp = GC←hblkfreelist; hhdr = HDR(hbp); while( (hbp != 0) && (hbp < p) ) { prevhbp = hbp; prevhdr = hhdr; hbp = hhdr->hb←next; hhdr = HDR(hbp); } /* Check for duplicate deallocation in the easy case */ if (hbp != 0 && (ptr←t)p + size > (ptr←t)hbp || prevhbp != 0 && (ptr←t)prevhbp + prevhdr->hb←sz > (ptr←t)p) { GC←printf1("Duplicate large block deallocation of 0x%lx\n", (unsigned long) p); GC←printf2("Surrounding free blocks are 0x%lx and 0x%lx\n", (unsigned long) prevhbp, (unsigned long) hbp); } /* Coalesce with successor, if possible */ if( (((word)p)+size) == ((word)hbp) ) { phdr->hb←next = hhdr->hb←next; phdr->hb←sz += hhdr->hb←sz; GC←remove←header(hbp); } else { phdr->hb←next = hbp; } if( prevhbp == 0 ) { GC←hblkfreelist = p; } else if( (((word)prevhbp) + prevhdr->hb←sz) == ((word)p) ) { /* Coalesce with predecessor */ prevhdr->hb←next = phdr->hb←next; prevhdr->hb←sz += phdr->hb←sz; GC←remove←header(p); } else { prevhdr->hb←next = p; } }