#include #include #include "mlvalues.h" #include "debugger.h" #include "fail.h" #include "freelist.h" #include "gc.h" #include "gc_ctrl.h" #include "major_gc.h" #include "memory.h" #include "minor_gc.h" #include "misc.h" value *c_roots_head; /* Allocate more memory from malloc for the heap. Return a block of at least the requested size (in words). Return NULL when out of memory. */ static char *expand_heap (mlsize_t request) { char *mem; char *new_page_table = NULL; asize_t new_page_table_size = 0; asize_t malloc_request; asize_t i, more_pages; malloc_request = round_heap_chunk_size (Bhsize_wosize (request)); gc_message ("Growing heap to %ldk\n", (stat_heap_size + malloc_request) / 1024); mem = aligned_malloc (malloc_request + sizeof (heap_chunk_head), sizeof (heap_chunk_head)); if (mem == NULL){ gc_message ("No room for growing heap\n", 0); return NULL; } mem += sizeof (heap_chunk_head); (((heap_chunk_head *) mem) [-1]).size = malloc_request; Assert (Wosize_bhsize (malloc_request) >= request); Hd_hp (mem) = Make_header (Wosize_bhsize (malloc_request), 0, Blue); #ifndef SIXTEEN if (mem < heap_start){ more_pages = -Page (mem); }else if (Page (mem + malloc_request) > page_table_size){ Assert (mem >= heap_end); more_pages = Page (mem + malloc_request) - page_table_size; }else{ more_pages = 0; } if (more_pages != 0){ new_page_table_size = page_table_size + more_pages; new_page_table = (char *) malloc (new_page_table_size); if (new_page_table == NULL){ gc_message ("No room for growing page table\n", 0); free (mem); return NULL; } } if (mem < heap_start){ Assert (more_pages != 0); for (i = 0; i < more_pages; i++){ new_page_table [i] = Not_in_heap; } bcopy (page_table, new_page_table + more_pages, page_table_size); (((heap_chunk_head *) mem) [-1]).next = heap_start; heap_start = mem; }else{ char **last; char *cur; if (mem >= heap_end) heap_end = mem + malloc_request; if (more_pages != 0){ for (i = page_table_size; i < new_page_table_size; i++){ new_page_table [i] = Not_in_heap; } bcopy (page_table, new_page_table, page_table_size); } last = &heap_start; cur = *last; while (cur != NULL && cur < mem){ last = &((((heap_chunk_head *) cur) [-1]).next); cur = *last; } (((heap_chunk_head *) mem) [-1]).next = cur; *last = mem; } if (more_pages != 0){ free (page_table); page_table = new_page_table; page_table_size = new_page_table_size; } #else /* Simplified version for the 8086 */ { char **last; char *cur; last = &heap_start; cur = *last; while (cur != NULL && (char huge *) cur < (char huge *) mem){ last = &((((heap_chunk_head *) cur) [-1]).next); cur = *last; } (((heap_chunk_head *) mem) [-1]).next = cur; *last = mem; } #endif for (i = Page (mem); i < Page (mem + malloc_request); i++){ page_table [i] = In_heap; } stat_heap_size += malloc_request; return Bp_hp (mem); } EXTERN value alloc_shr (mlsize_t wosize, tag_t tag) { char *hp, *new_block; hp = fl_allocate (wosize); if (hp == NULL){ new_block = expand_heap (wosize); if (new_block == NULL) raise_out_of_memory (); fl_add_block (new_block); hp = fl_allocate (wosize); if (hp == NULL) fatal_error ("alloc_shr: expand heap failed\n"); } Assert (Is_in_heap (Val_hp (hp))); if (gc_phase == Phase_mark || (addr)hp >= (addr)gc_sweep_hp){ Hd_hp (hp) = Make_header (wosize, tag, Black); }else{ Hd_hp (hp) = Make_header (wosize, tag, White); } allocated_words += Whsize_wosize (wosize); if (allocated_words > Wsize_bsize (minor_heap_size)) force_minor_gc (); return Val_hp (hp); } /* Use this function to tell the major GC to speed up when you use finalized objects to automatically deallocate extra-heap objects. The GC will do at least one cycle every [max] allocated words; [mem] is the number of words allocated this time. Note that only [mem/max] is relevant. You can use numbers of bytes (or kilobytes, ...) instead of words. You can change units between calls to [adjust_collector_speed]. */ void adjust_gc_speed (mlsize_t mem, mlsize_t max) { if (max == 0) max = 1; if (mem > max) mem = max; extra_heap_memory += ((float) mem / max) * stat_heap_size; if (extra_heap_memory > stat_heap_size){ extra_heap_memory = stat_heap_size; } if (extra_heap_memory > Wsize_bsize (minor_heap_size) / 2) force_minor_gc (); } /* You must use [initialize] to store the initial value in a field of a shared block, unless you are sure the value is not a young block. A block value [v] is a shared block if and only if [Is_in_heap (v)] is true. */ /* [initialize] never calls the GC, so you may call it while an object is unfinished (i.e. just after a call to [alloc_shr].) */ void initialize (value * fp, value val) { *fp = val; Assert (Is_in_heap (fp)); if (Is_block (val) && Is_young (val)){ *ref_table_ptr++ = fp; if (ref_table_ptr >= ref_table_limit){ realloc_ref_table (); } } } /* You must use [modify] to change a field of an existing shared block, unless you are sure the value being overwritten is not a shared block and the value being written is not a young block. */ /* Never calls the GC. */ EXTERN void modify (value * fp, value val) { Modify (fp, val); } char *stat_alloc(asize_t sz) { char *result = (char *) malloc (sz); if (result == NULL) raise_out_of_memory (); return result; } void stat_free(char * blk) { free (blk); } char *stat_resize (char * blk, asize_t sz) { char *result = (char *) realloc (blk, sz); if (result == NULL) raise_out_of_memory (); return result; } void init_c_roots (void) { c_roots_head = NULL; }