/* * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. * Copyright (c) 1998 by Silicon Graphics. 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 use or copy this program * for any purpose, provided the above notices are retained on all copies. * Permission to modify the code and to distribute modified code is granted, * provided the above notices are retained, and a notice that the code was * modified is included with the above copyright notice. */ /* Boehm, August 9, 1995 5:08 pm PDT */ #define DEBUG #undef DEBUG #include #include "gc_priv.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. */ # if !defined(NO_DEBUGGING) void GC_print_hblkfreelist() { struct hblk * h = GC_hblkfreelist; word total_free = 0; hdr * hhdr = HDR(h); word sz; while (h != 0) { sz = hhdr -> hb_sz; GC_printf2("0x%lx size %lu ", (unsigned long)h, (unsigned long)sz); total_free += sz; if (GC_is_black_listed(h, HBLKSIZE) != 0) { GC_printf0("start black listed\n"); } else if (GC_is_black_listed(h, hhdr -> hb_sz) != 0) { GC_printf0("partially black listed\n"); } else { GC_printf0("not black listed\n"); } h = hhdr -> hb_next; hhdr = HDR(h); } GC_printf1("Total of %lu bytes on free list\n", (unsigned long)total_free); } # endif /* NO_DEBUGGING */ /* Initialize hdr for a block containing the indicated size and */ /* kind of objects. */ /* Return FALSE on failure. */ static GC_bool setup_header(hhdr, sz, kind, flags) register hdr * hhdr; word sz; /* object size in words */ int kind; unsigned char flags; { register word descr; /* Add description of valid object pointers */ if (!GC_add_map_entry(sz)) return(FALSE); hhdr -> hb_map = GC_obj_map[sz > MAXOBJSZ? 0 : sz]; /* Set size, kind and mark proc fields */ hhdr -> hb_sz = sz; hhdr -> hb_obj_kind = kind; hhdr -> hb_flags = flags; descr = GC_obj_kinds[kind].ok_descriptor; if (GC_obj_kinds[kind].ok_relocate_descr) descr += WORDS_TO_BYTES(sz); hhdr -> hb_descr = descr; /* Clear mark bits */ GC_clear_hdr_marks(hhdr); hhdr -> hb_last_reclaimed = (unsigned short)GC_gc_no; return(TRUE); } #ifdef EXACT_FIRST # define LAST_TRIP 2 #else # define LAST_TRIP 1 #endif word GC_max_hblk_size = HBLKSIZE; /* * 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, flags) word sz; int kind; unsigned char flags; /* IGNORE_OFF_PAGE or 0 */ { 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 */ int trip_count = 0; size_needed = HBLKSIZE * OBJ_SZ_TO_BLOCKS(sz); if ((word)size_needed > GC_max_hblk_size) GC_max_hblk_size = size_needed; /* 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) { if (trip_count == LAST_TRIP) return(0); ++trip_count; } if( hbp == 0 ) continue; size_avail = hhdr->hb_sz; # ifdef EXACT_FIRST if (trip_count <= 1 && size_avail != size_needed) continue; # endif if (size_avail < size_needed) continue; # ifdef PRESERVE_LAST if (size_avail != size_needed && !GC_incremental && (word)size_needed <= GC_max_hblk_size/2 && GC_in_last_heap_sect((ptr_t)hbp) && GC_should_collect()) { continue; } # endif /* If the next heap block is obviously better, go on. */ /* This prevents us from disassembling a single large block */ /* to get tiny blocks. */ { signed_word next_size; thishbp = hhdr -> hb_next; if (thishbp == 0) thishbp = GC_hblkfreelist; thishdr = HDR(thishbp); next_size = (signed_word)(thishdr -> hb_sz); if (next_size < size_avail && next_size >= size_needed && !GC_is_black_listed(thishbp, (word)size_needed)) { continue; } } if ( !IS_UNCOLLECTABLE(kind) && (kind != PTRFREE || size_needed > MAX_BLACK_LIST_ALLOC)) { struct hblk * lasthbp = hbp; ptr_t search_end = (ptr_t)hbp + size_avail - size_needed; signed_word orig_avail = size_avail; signed_word eff_size_needed = ((flags & IGNORE_OFF_PAGE)? HBLKSIZE : size_needed); while ((ptr_t)lasthbp <= search_end && (thishbp = GC_is_black_listed(lasthbp, (word)eff_size_needed))) { lasthbp = thishbp; } size_avail -= (ptr_t)lasthbp - (ptr_t)hbp; thishbp = lasthbp; if (size_avail >= size_needed) { if (thishbp != hbp && GC_install_header(thishbp)) { /* Split the block at 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_needed > (signed_word)BL_LIMIT && orig_avail - size_needed > (signed_word)BL_LIMIT) { /* Punt, since anything else risks unreasonable heap growth. */ WARN("Needed to allocate blacklisted block at 0x%lx\n", (word)hbp); thishbp = hbp; size_avail = orig_avail; } else if (size_avail == 0 && size_needed == HBLKSIZE && prevhbp != 0) { # ifndef FIND_LEAK 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 in small chunks, to */ /* maximize the chance that we will recover some */ /* later. */ struct hblk * limit = hbp + (hhdr->hb_sz/HBLKSIZE); struct hblk * h; GC_words_wasted += hhdr->hb_sz; phdr -> hb_next = hhdr -> hb_next; for (h = hbp; h < limit; h++) { if (h == hbp || GC_install_header(h)) { hhdr = HDR(h); (void) setup_header( hhdr, BYTES_TO_WORDS(HBLKSIZE - HDR_BYTES), PTRFREE, 0); /* Cant fail */ if (GC_debugging_started) { BZERO(hbp + HDR_BYTES, HBLKSIZE - HDR_BYTES); } } } /* Restore hbp to point at free block */ if (GC_savhbp == hbp) GC_savhbp = prevhbp; hbp = prevhbp; hhdr = phdr; if (hbp == GC_savhbp) --trip_count; } # endif } } 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 *) (((word)thishbp) + size_needed); if (!GC_install_header(hbp)) { hbp = thishbp; continue; } 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; } } /* Notify virtual dirty bit implementation that we are about to write. */ GC_write_hint(thishbp); /* This should deal better with large blocks. */ /* Add it to map of valid blocks */ if (!GC_install_counts(thishbp, (word)size_needed)) return(0); /* This leaks memory under very rare conditions. */ /* Set up header */ if (!setup_header(thishdr, sz, kind, flags)) { GC_remove_counts(thishbp, (word)size_needed); return(0); /* ditto */ } /* Clear block if necessary */ if (GC_debugging_started || sz > MAXOBJSZ && GC_obj_kinds[kind].ok_init) { BZERO(thishbp + HDR_BYTES, size_needed - HDR_BYTES); } /* We just successfully allocated a block. Restart count of */ /* consecutive failures. */ { extern unsigned GC_fail_count; GC_fail_count = 0; } return( thishbp ); } struct hblk * GC_freehblk_ptr = 0; /* Search position hint for GC_freehblk */ /* * 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 = HBLKSIZE * OBJ_SZ_TO_BLOCKS(size); GC_remove_counts(p, (word)size); phdr->hb_sz = size; GC_invalidate_map(phdr); prevhbp = 0; /* The following optimization was suggested by David Detlefs. */ /* Note that the header cannot be NIL, since there cannot be an */ /* intervening call to GC_freehblk without resetting */ /* GC_freehblk_ptr. */ if (GC_freehblk_ptr != 0 && HDR(GC_freehblk_ptr)->hb_map == GC_invalid_map && (ptr_t)GC_freehblk_ptr < (ptr_t)p) { hbp = GC_freehblk_ptr; } else { hbp = GC_hblkfreelist; }; hhdr = HDR(hbp); while( (hbp != 0) && (hbp < p) ) { prevhbp = hbp; prevhdr = hhdr; hbp = hhdr->hb_next; hhdr = HDR(hbp); } GC_freehblk_ptr = prevhbp; /* 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; } }