mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-01 06:35:42 +00:00
416 lines
12 KiB
C
416 lines
12 KiB
C
/*
|
|
* 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 <stdio.h>
|
|
#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;
|
|
}
|
|
}
|
|
|