This is mark.c in view mode; [Download] [Up]
/* * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers * Copyright (c) 1991,1992 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. * */ # include <stdio.h> # include "gc_private.h" # define INITIAL_MARK_STACK_SIZE (1*HBLKSIZE) /* INITIAL_MARK_STACK_SIZE * sizeof(mse) should be a */ /* multiple of HBLKSIZE. */ /* * Limits of stack for GC_mark routine. * All ranges between GC_mark_stack(incl.) and GC_mark_stack_top(incl.) still * need to be marked from. */ word GC_n_rescuing_pages; /* Number of dirty pages we marked from */ /* excludes ptrfree pages, etc. */ mse * GC_mark_stack; word GC_mark_stack_size = 0; mse * GC_mark_stack_top; static struct hblk * scan_ptr; typedef int mark_state_t; /* Current state of marking, as follows:*/ /* Used to remember where we are during */ /* concurrent marking. */ /* We say something is dirty if it was */ /* written since the last time we */ /* retrieved dirty bits. We say it's */ /* grungy if it was marked dirty in the */ /* last set of bits we retrieved. */ /* Invariant I: all roots and marked */ /* objects p are either dirty, or point */ /* objects q that are either marked or */ /* a pointer to q appears in a range */ /* on the mark stack. */ # define MS_NONE 0 /* No marking in progress. I holds. */ /* Mark stack is empty. */ # define MS_PUSH_RESCUERS 1 /* Rescuing objects are currently */ /* being pushed. I holds, except */ /* that grungy roots may point to */ /* unmarked objects, as may marked */ /* grungy objects above scan_ptr. */ # define MS_PUSH_UNCOLLECTABLE 2 /* I holds, except that marked */ /* uncollectable objects above scan_ptr */ /* may point to unmarked objects. */ /* Roots may point to unmarked objects */ # define MS_ROOTS_PUSHED 3 /* I holds, mark stack may be nonempty */ # define MS_PARTIALLY_INVALID 4 /* I may not hold, e.g. because of M.S. */ /* overflow. However marked heap */ /* objects below scan_ptr point to */ /* marked or stacked objects. */ # define MS_INVALID 5 /* I may not hold. */ mark_state_t GC_mark_state = MS_NONE; bool GC_objects_are_marked = FALSE; /* Are there collectable marked */ /* objects in the heap? */ bool GC_collection_in_progress() { return(GC_mark_state != MS_NONE); } /* clear all mark bits in the header */ void GC_clear_hdr_marks(hhdr) register hdr * hhdr; { bzero((char *)(hhdr -> hb_marks), (int)(MARK_BITS_SZ*sizeof(word))); } /* * Clear all mark bits associated with block h. */ /*ARGSUSED*/ static void clear_marks_for_block(h, dummy) struct hblk *h; word dummy; { register hdr * hhdr = HDR(h); if (hhdr -> hb_obj_kind == UNCOLLECTABLE) return; /* Mark bit for these is cleared only once the object is */ /* explicitly deallocated. This either frees the block, or */ /* the bit is cleared once the object is on the free list. */ GC_clear_hdr_marks(hhdr); } /* Slow but general routines for setting/clearing/asking about mark bits */ void GC_set_mark_bit(p) ptr_t p; { register struct hblk *h = HBLKPTR(p); register hdr * hhdr = HDR(h); register int word_no = (word *)p - (word *)h; set_mark_bit_from_hdr(hhdr, word_no); } void GC_clear_mark_bit(p) ptr_t p; { register struct hblk *h = HBLKPTR(p); register hdr * hhdr = HDR(h); register int word_no = (word *)p - (word *)h; clear_mark_bit_from_hdr(hhdr, word_no); } bool GC_is_marked(p) ptr_t p; { register struct hblk *h = HBLKPTR(p); register hdr * hhdr = HDR(h); register int word_no = (word *)p - (word *)h; return(mark_bit_from_hdr(hhdr, word_no)); } /* * Clear mark bits in all allocated heap blocks. This invalidates * the marker invariant, and sets GC_mark_state to reflect this. * (This implicitly starts marking to reestablish the */ void GC_clear_marks() { GC_apply_to_all_blocks(clear_marks_for_block, (word)0); GC_objects_are_marked = FALSE; GC_mark_state = MS_INVALID; scan_ptr = 0; # ifdef GATHERSTATS /* Counters reflect currently marked objects: reset here */ GC_composite_in_use = 0; GC_atomic_in_use = 0; # endif } /* * Push some dummy entries onto bottom of mark stack to allow * marker to operate in bigger chunks between bounds checks. * This is a pretty extreme performance hack ... */ void GC_prime_marker() { register int i; static word dummy = 0; for (i = 0; i < INITIAL_MARK_STACK_SIZE/64; i++) { GC_push_all((ptr_t)(&dummy), (ptr_t)(&dummy + 1)); } } /* Initiate full marking. */ void GC_initiate_full() { # ifdef PRINTSTATS GC_printf2("Full mark for collection %lu after %ld allocd bytes\n", (unsigned long) GC_gc_no+1, (long)WORDS_TO_BYTES(GC_words_allocd)); # endif GC_promote_black_lists(); GC_reclaim_or_delete_all(); GC_clear_marks(); GC_read_dirty(); # ifdef STUBBORN_ALLOC GC_read_changed(); # endif # ifdef CHECKSUMS { extern void GC_check_dirty(); GC_check_dirty(); } # endif # ifdef GATHERSTATS GC_n_rescuing_pages = 0; # endif GC_prime_marker(); } /* Initiate partial marking. */ /*ARGSUSED*/ void GC_initiate_partial(gc_no) word gc_no; { # ifdef PRINTSTATS if (gc_no > GC_gc_no) { GC_printf2("Partial mark for collection %lu after %ld allocd bytes\n", (unsigned long) gc_no, (long)WORDS_TO_BYTES(GC_words_allocd)); } /* else the world is stopped, and we just printed this */ # endif if (GC_incremental) GC_read_dirty(); # ifdef STUBBORN_ALLOC GC_read_changed(); # endif # ifdef CHECKSUMS { extern void GC_check_dirty(); if (GC_incremental) GC_check_dirty(); } # endif # ifdef GATHERSTATS GC_n_rescuing_pages = 0; # endif if (GC_mark_state == MS_NONE) { GC_mark_state = MS_PUSH_RESCUERS; } else if (GC_mark_state != MS_INVALID) { ABORT("unexpected state"); } /* else this is really a full collection, and mark */ /* bits are invalid. */ scan_ptr = 0; GC_prime_marker(); } static void alloc_mark_stack(); /* Perform a small amount of marking. */ /* We try to touch roughly a page of memory. */ /* Return TRUE if we just finished a mark phase. */ bool GC_mark_some() { switch(GC_mark_state) { case MS_NONE: return(FALSE); case MS_PUSH_RESCUERS: if (GC_mark_stack_top >= GC_mark_stack + INITIAL_MARK_STACK_SIZE/4) { GC_mark_from_mark_stack(); return(FALSE); } else { scan_ptr = GC_push_next_marked_dirty(scan_ptr); if (scan_ptr == 0) { # ifdef PRINTSTATS GC_printf1("Marked from %lu dirty pages\n", (unsigned long)GC_n_rescuing_pages); # endif GC_push_roots(FALSE); if (GC_mark_state != MS_INVALID) { GC_mark_state = MS_ROOTS_PUSHED; } } } return(FALSE); case MS_PUSH_UNCOLLECTABLE: if (GC_mark_stack_top >= GC_mark_stack + INITIAL_MARK_STACK_SIZE/4) { GC_mark_from_mark_stack(); return(FALSE); } else { scan_ptr = GC_push_next_marked_uncollectable(scan_ptr); if (scan_ptr == 0) { GC_push_roots(TRUE); if (GC_mark_state != MS_INVALID) { GC_mark_state = MS_ROOTS_PUSHED; } } } return(FALSE); case MS_ROOTS_PUSHED: if (GC_mark_stack_top >= GC_mark_stack) { GC_mark_from_mark_stack(); return(FALSE); } else { GC_mark_state = MS_NONE; return(TRUE); } case MS_INVALID: case MS_PARTIALLY_INVALID: if (!GC_objects_are_marked) { GC_mark_state = MS_PUSH_UNCOLLECTABLE; return(FALSE); } if (GC_mark_stack_top >= GC_mark_stack) { GC_mark_from_mark_stack(); return(FALSE); } if (scan_ptr == 0 && GC_mark_state == MS_INVALID) { alloc_mark_stack(2*GC_mark_stack_size); # ifdef PRINTSTATS GC_printf1("Grew mark stack to %lu frames\n", (unsigned long) GC_mark_stack_size); # endif GC_mark_state = MS_PARTIALLY_INVALID; } scan_ptr = GC_push_next_marked(scan_ptr); if (scan_ptr == 0 && GC_mark_state == MS_PARTIALLY_INVALID) { GC_push_roots(TRUE); if (GC_mark_state != MS_INVALID) { GC_mark_state = MS_ROOTS_PUSHED; } } return(FALSE); default: ABORT("GC_mark_some: bad state"); return(FALSE); } } /* Mark procedure for objects that may contain arbitrary pointers. */ /* Msp is the current mark stack pointer. Msl limits the stack. */ /* We return the new stack pointer value. */ /* The object at addr has already been marked. Our job is to make */ /* sure that its descendents are marked. */ mse * GC_normal_mark_proc(addr, hhdr, msp, msl) register word * addr; register hdr * hhdr; register mse * msp, * msl; { register word sz = hhdr -> hb_sz; msp++; /* Push the contents of the object on the mark stack. */ if (msp >= msl) { GC_mark_state = MS_INVALID; # ifdef PRINTSTATS GC_printf1("Mark stack overflow; current size = %lu entries\n", GC_mark_stack_size); # endif return(msp-INITIAL_MARK_STACK_SIZE/8); } msp -> mse_start = addr; msp -> mse_end = addr + sz; # ifdef GATHERSTATS GC_composite_in_use += sz; # endif return(msp); } /* Mark procedure for objects that are known to contain no pointers. */ /*ARGSUSED*/ mse * GC_no_mark_proc(addr, hhdr, msp, msl) register word * addr; register hdr * hhdr; register mse * msp, * msl; { # ifdef GATHERSTATS GC_atomic_in_use += hhdr -> hb_sz; # endif return(msp); } bool GC_mark_stack_empty() { return(GC_mark_stack_top < GC_mark_stack); } /* * Mark objects pointed to by the regions described by * mark stack entries between GC_mark_stack and GC_mark_stack_top, * inclusive. Assumes the upper limit of a mark stack entry * is never 0. A mark stack entry never has size 0. * We try to traverse on the order of a hblk of memory before we return. * Caller is responsible for calling this until the mark stack is empty. */ void GC_mark_from_mark_stack() { mse * GC_mark_stack_reg = GC_mark_stack; mse * GC_mark_stack_top_reg = GC_mark_stack_top; mse * mark_stack_limit = &(GC_mark_stack[GC_mark_stack_size]); int credit = HBLKSIZE; /* Remaining credit for marking work */ register int safe_credit; /* Amount of credit we can safely use */ /* before checking stack bounds. */ /* Gross hack to safe a couple of */ /* instructions in the loop. */ register word * current_p; /* Pointer to current candidate ptr. */ register word current; /* Candidate pointer. */ register word * limit; /* (Incl) limit of current candidate */ /* range */ register ptr_t greatest_ha = GC_greatest_plausible_heap_addr; register ptr_t least_ha = GC_least_plausible_heap_addr; # define SPLIT_RANGE_WORDS 128 GC_objects_are_marked = TRUE; while (GC_mark_stack_top_reg >= GC_mark_stack_reg && credit > 0) { safe_credit = WORDS_TO_BYTES(GC_mark_stack_top_reg - GC_mark_stack_reg + 1); if (safe_credit > credit) { safe_credit = credit; credit = 0; } else { credit -= safe_credit; } while (safe_credit > 0) { /* Stack must be nonempty */ register int displ; /* Displacement in block; first bytes, then words */ register hdr * hhdr; register map_entry_type map_entry; current_p = GC_mark_stack_top_reg -> mse_start; limit = GC_mark_stack_top_reg -> mse_end; safe_credit -= (ptr_t)limit - (ptr_t)current_p; if (limit - current_p > SPLIT_RANGE_WORDS) { /* Process part of the range to avoid pushing too much on the */ /* stack. */ GC_mark_stack_top_reg -> mse_start = limit = current_p + SPLIT_RANGE_WORDS; /* Make sure that pointers overlapping the two ranges are */ /* considered. */ limit += sizeof(word) - ALIGNMENT; } else { GC_mark_stack_top_reg--; } limit -= 1; while (current_p <= limit) { current = *current_p; current_p = (word *)((char *)current_p + ALIGNMENT); if ((ptr_t)current < least_ha) continue; if ((ptr_t)current >= greatest_ha) continue; GET_HDR(current,hhdr); if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { # ifdef ALL_INTERIOR_POINTERS if (hhdr != 0) { register word orig = current; current = (word)HBLKPTR(current) + HDR_BYTES; do { current = current - HBLKSIZE*(int)hhdr; hhdr = HDR(current); } while(IS_FORWARDING_ADDR_OR_NIL(hhdr)); /* current points to the start of the large object */ if ((word *)orig - (word *)current >= hhdr->hb_sz) { /* Pointer past the end of the block */ GC_ADD_TO_BLACK_LIST_NORMAL(current); continue; } } else { GC_ADD_TO_BLACK_LIST_NORMAL(current); continue; } # else GC_ADD_TO_BLACK_LIST_NORMAL(current); continue; # endif } displ = HBLKDISPL(current); map_entry = MAP_ENTRY((hhdr -> hb_map), displ); if (map_entry == OBJ_INVALID) { GC_ADD_TO_BLACK_LIST_NORMAL(current); continue; } displ = BYTES_TO_WORDS(displ); displ -= map_entry; { register word * mark_word_addr = hhdr -> hb_marks + divWORDSZ(displ); register word mark_word = *mark_word_addr; register word mark_bit = (word)1 << modWORDSZ(displ); if (mark_word & mark_bit) { /* Mark bit is already set */ continue; } *mark_word_addr = mark_word | mark_bit; } GC_mark_stack_top_reg = (* (hhdr -> hb_mark_proc))((word *)(HBLKPTR(current)) + displ, hhdr, GC_mark_stack_top_reg, mark_stack_limit); } } } GC_mark_stack_top = GC_mark_stack_top_reg; } /* Allocate or reallocate space for mark stack of size s words */ /* May silently fail. */ static void alloc_mark_stack(n) word n; { mse * new_stack = (mse *)GC_scratch_alloc(n * sizeof(struct ms_entry)); if (GC_mark_stack_size != 0) { if (new_stack != 0) { /* Recycle old space */ GC_add_to_heap((struct hblk *)GC_mark_stack, GC_mark_stack_size * sizeof(struct ms_entry)); GC_mark_stack = new_stack; GC_mark_stack_size = n; } } else { if (new_stack == 0) { GC_err_printf0("No space for mark stack\n"); EXIT(); } GC_mark_stack = new_stack; GC_mark_stack_size = n; } GC_mark_stack_top = GC_mark_stack-1; } void GC_mark_init() { alloc_mark_stack(INITIAL_MARK_STACK_SIZE); } /* * Push all locations between b and t onto the mark stack. * b is the first location to be checked. t is one past the last * location to be checked. */ void GC_push_all(bottom, top) ptr_t bottom; ptr_t top; { word * b = (word *)(((long) bottom + ALIGNMENT-1) & ~(ALIGNMENT-1)); word * t = (word *)(((long) top) & ~(ALIGNMENT-1)); if (top == 0 || bottom == top) return; GC_mark_stack_top++; GC_mark_stack_top -> mse_start = b; GC_mark_stack_top -> mse_end = t; } /* * Analogous to the above, but push only those pages that may have been * dirtied. */ void GC_push_dirty(bottom, top) ptr_t bottom; ptr_t top; { register struct hblk * h; if (top == 0 || bottom == top) return; h = HBLKPTR(bottom + HBLKSIZE); if (top <= (ptr_t) h) { if (GC_page_was_dirty(h-1)) { GC_push_all(bottom, top); } return; } if (GC_page_was_dirty(h-1)) { GC_push_all(bottom, (ptr_t)h); } while ((ptr_t)(h+1) <= top) { if (GC_page_was_dirty(h)) { GC_mark_stack_top++; GC_mark_stack_top -> mse_start = (word *)h; h++; GC_mark_stack_top -> mse_end = (word *)h; } else { h++; } } if ((ptr_t)h != top) { if (GC_page_was_dirty(h)) { GC_push_all((ptr_t)h, top); } } } void GC_push_conditional(bottom, top, all) ptr_t bottom; ptr_t top; { if (all) { GC_push_all(bottom, top); } else { GC_push_dirty(bottom, top); } } /* * Push a single value onto mark stack. Mark from the object pointed to by p. * GC_push_one is normally called by GC_push_regs, and thus must be defined. * P is considered valid even if it is an interior pointer. * Previously marked objects are not pushed. Hence we make progress even * if the mark stack overflows. */ # define GC_PUSH_ONE_STACK(p) \ if ((ptr_t)(p) >= GC_least_plausible_heap_addr \ && (ptr_t)(p) < GC_greatest_plausible_heap_addr) { \ GC_push_one_checked(p,TRUE); \ } /* * As above, but interior pointer recognition as for * normal for heap pointers. */ # ifdef ALL_INTERIOR_POINTERS # define AIP TRUE # else # define AIP FALSE # endif # define GC_PUSH_ONE_HEAP(p) \ if ((ptr_t)(p) >= GC_least_plausible_heap_addr \ && (ptr_t)(p) < GC_greatest_plausible_heap_addr) { \ GC_push_one_checked(p,AIP); \ } void GC_push_one(p) word p; { GC_PUSH_ONE_STACK(p); } # ifdef __STDC__ # define BASE(p) (word)GC_base((void *)(p)) # else # define BASE(p) (word)GC_base((char *)(p)) # endif /* As above, but argument passed preliminary test. */ void GC_push_one_checked(p, interior_ptrs) register word p; register bool interior_ptrs; { register word r; register hdr * hhdr; register int displ; GET_HDR(p, hhdr); if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { if (hhdr != 0 && interior_ptrs) { r = BASE(p); hhdr = HDR(r); displ = BYTES_TO_WORDS(HBLKDISPL(r)); } else { hhdr = 0; } } else { register map_entry_type map_entry; displ = HBLKDISPL(p); map_entry = MAP_ENTRY((hhdr -> hb_map), displ); if (map_entry == OBJ_INVALID) { if (interior_ptrs) { r = BASE(p); displ = BYTES_TO_WORDS(HBLKDISPL(r)); if (r == 0) hhdr = 0; } else { hhdr = 0; } } else { displ = BYTES_TO_WORDS(displ); displ -= map_entry; r = (word)((word *)(HBLKPTR(p)) + displ); } } /* If hhdr != 0 then r == GC_base(p), only we did it faster. */ /* displ is the word index within the block. */ if (hhdr == 0) { if (interior_ptrs) { GC_add_to_black_list_stack(p); } else { GC_ADD_TO_BLACK_LIST_NORMAL(p); } } else { if (!mark_bit_from_hdr(hhdr, displ)) { set_mark_bit_from_hdr(hhdr, displ); GC_mark_stack_top = (* (hhdr -> hb_mark_proc))((word *)r, hhdr, GC_mark_stack_top, &(GC_mark_stack[GC_mark_stack_size])); } } } /* * A version of GC_push_all that treats all interior pointers as valid */ void GC_push_all_stack(bottom, top) ptr_t bottom; ptr_t top; { # ifdef ALL_INTERIOR_POINTERS GC_push_all(bottom, top); # else word * b = (word *)(((long) bottom + ALIGNMENT-1) & ~(ALIGNMENT-1)); word * t = (word *)(((long) top) & ~(ALIGNMENT-1)); register word *p; register word q; register word *lim; register ptr_t greatest_ha = GC_greatest_plausible_heap_addr; register ptr_t least_ha = GC_least_plausible_heap_addr; # define GC_greatest_plausible_heap_addr greatest_ha # define GC_least_plausible_heap_addr least_ha if (top == 0) return; /* check all pointers in range and put in push if they appear */ /* to be valid. */ lim = t - 1 /* longword */; for (p = b; p <= lim; p = (word *)(((char *)p) + ALIGNMENT)) { q = *p; GC_PUSH_ONE_STACK(q); } # undef GC_greatest_plausible_heap_addr # undef GC_least_plausible_heap_addr # endif } /* Push all objects reachable from marked objects in the given block */ /* of size 1 objects. */ void GC_push_marked1(h, hhdr) struct hblk *h; register hdr * hhdr; { word * mark_word_addr = &(hhdr->hb_marks[divWORDSZ(HDR_WORDS)]); register word *p; word *plim; register int i; register word q; register word mark_word; register ptr_t greatest_ha = GC_greatest_plausible_heap_addr; register ptr_t least_ha = GC_least_plausible_heap_addr; # define GC_greatest_plausible_heap_addr greatest_ha # define GC_least_plausible_heap_addr least_ha p = (word *)(h->hb_body); plim = (word *)(((word)h) + HBLKSIZE); /* go through all words in block */ while( p < plim ) { mark_word = *mark_word_addr++; i = 0; while(mark_word != 0) { if (mark_word & 1) { q = p[i]; GC_PUSH_ONE_HEAP(q); } i++; mark_word >>= 1; } p += WORDSZ; } # undef GC_greatest_plausible_heap_addr # undef GC_least_plausible_heap_addr } /* Push all objects reachable from marked objects in the given block */ /* of size 2 objects. */ void GC_push_marked2(h, hhdr) struct hblk *h; register hdr * hhdr; { word * mark_word_addr = &(hhdr->hb_marks[divWORDSZ(HDR_WORDS)]); register word *p; word *plim; register int i; register word q; register word mark_word; register ptr_t greatest_ha = GC_greatest_plausible_heap_addr; register ptr_t least_ha = GC_least_plausible_heap_addr; # define GC_greatest_plausible_heap_addr greatest_ha # define GC_least_plausible_heap_addr least_ha p = (word *)(h->hb_body); plim = (word *)(((word)h) + HBLKSIZE); /* go through all words in block */ while( p < plim ) { mark_word = *mark_word_addr++; i = 0; while(mark_word != 0) { if (mark_word & 1) { q = p[i]; GC_PUSH_ONE_HEAP(q); q = p[i+1]; GC_PUSH_ONE_HEAP(q); } i += 2; mark_word >>= 2; } p += WORDSZ; } # undef GC_greatest_plausible_heap_addr # undef GC_least_plausible_heap_addr } /* Push all objects reachable from marked objects in the given block */ /* of size 4 objects. */ /* There is a risk of mark stack overflow here. But we handle that. */ /* And only unmarked objects get pushed, so it's not very likely. */ void GC_push_marked4(h, hhdr) struct hblk *h; register hdr * hhdr; { word * mark_word_addr = &(hhdr->hb_marks[divWORDSZ(HDR_WORDS)]); register word *p; word *plim; register int i; register word q; register word mark_word; register ptr_t greatest_ha = GC_greatest_plausible_heap_addr; register ptr_t least_ha = GC_least_plausible_heap_addr; # define GC_greatest_plausible_heap_addr greatest_ha # define GC_least_plausible_heap_addr least_ha p = (word *)(h->hb_body); plim = (word *)(((word)h) + HBLKSIZE); /* go through all words in block */ while( p < plim ) { mark_word = *mark_word_addr++; i = 0; while(mark_word != 0) { if (mark_word & 1) { q = p[i]; GC_PUSH_ONE_HEAP(q); q = p[i+1]; GC_PUSH_ONE_HEAP(q); q = p[i+2]; GC_PUSH_ONE_HEAP(q); q = p[i+3]; GC_PUSH_ONE_HEAP(q); } i += 4; mark_word >>= 4; } p += WORDSZ; } # undef GC_greatest_plausible_heap_addr # undef GC_least_plausible_heap_addr } /* Push all objects reachable from marked objects in the given block */ void GC_push_marked(h, hhdr) struct hblk *h; register hdr * hhdr; { register int sz = hhdr -> hb_sz; register word * p; register int word_no; register word * lim; register mse * GC_mark_stack_top_reg; register mse * mark_stack_limit = &(GC_mark_stack[GC_mark_stack_size]); /* Some quick shortcuts: */ if (hhdr -> hb_obj_kind == PTRFREE) return; if (GC_block_empty(hhdr)/* nothing marked */) return; # ifdef GATHERSTATS GC_n_rescuing_pages++; # endif if (sz > MAXOBJSZ) { lim = (word *)(h + 1); } else { lim = (word *)(h + 1) - sz; } switch(sz) { case 1: GC_push_marked1(h, hhdr); break; case 2: GC_push_marked2(h, hhdr); break; case 4: GC_push_marked4(h, hhdr); break; default: GC_mark_stack_top_reg = GC_mark_stack_top; for (p = (word *)h + HDR_WORDS, word_no = HDR_WORDS; p <= lim; p += sz, word_no += sz) { /* This needs manual optimization: */ if (mark_bit_from_hdr(hhdr, word_no)) { /* Mark from fields inside the object */ GC_mark_stack_top_reg = (* (hhdr -> hb_mark_proc))((word *)p, hhdr, GC_mark_stack_top_reg, mark_stack_limit); # ifdef GATHERSTATS /* Subtract this object from total, since it was */ /* added in twice. */ GC_composite_in_use -= sz; # endif } } GC_mark_stack_top = GC_mark_stack_top_reg; } } /* Test whether any page in the given block is dirty */ bool GC_block_was_dirty(h, hhdr) struct hblk *h; register hdr * hhdr; { register int sz = hhdr -> hb_sz; if (sz < MAXOBJSZ) { return(GC_page_was_dirty(h)); } else { register ptr_t p = (ptr_t)h; sz += HDR_WORDS; sz = WORDS_TO_BYTES(sz); while (p < (ptr_t)h + sz) { if (GC_page_was_dirty((struct hblk *)p)) return(TRUE); p += HBLKSIZE; } return(FALSE); } } /* Identical to above, but return address of next block */ struct hblk * GC_push_next_marked(h) struct hblk *h; { register hdr * hhdr; h = GC_next_block(h); if (h == 0) return(0); hhdr = HDR(h); GC_push_marked(h, hhdr); return(h + OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz)); } /* Identical to above, but mark only from dirty pages */ struct hblk * GC_push_next_marked_dirty(h) struct hblk *h; { register hdr * hhdr = HDR(h); if (!GC_incremental) { ABORT("dirty bits not set up"); } for (;;) { h = GC_next_block(h); if (h == 0) return(0); hhdr = HDR(h); # ifdef STUBBORN_ALLOC if (hhdr -> hb_obj_kind == STUBBORN) { if (GC_page_was_changed(h) && GC_block_was_dirty(h, hhdr)) { break; } } else { if (GC_block_was_dirty(h, hhdr)) break; } # else if (GC_block_was_dirty(h, hhdr)) break; # endif h += OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz); } GC_push_marked(h, hhdr); return(h + OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz)); } /* Similar to above, but for uncollectable pages. Needed since we */ /* do not clear marks for such pages, even for full collections. */ struct hblk * GC_push_next_marked_uncollectable(h) struct hblk *h; { register hdr * hhdr = HDR(h); for (;;) { h = GC_next_block(h); if (h == 0) return(0); hhdr = HDR(h); if (hhdr -> hb_obj_kind == UNCOLLECTABLE) break; h += OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz); } GC_push_marked(h, hhdr); return(h + OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz)); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.