X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fruntime%2Fgencgc.c;h=5bd67c80c2df5f05268bb663e445be6cb8bf46a0;hb=4ec0d70e08ea4b512d45ddbd6c82e8f6a91a914f;hp=5236a6395bc305f4aaeb5aec80afe3adc0d6b289;hpb=b5b36522d5398715e53a3c6ca75cf16001ce46ac;p=sbcl.git diff --git a/src/runtime/gencgc.c b/src/runtime/gencgc.c index 5236a63..5bd67c8 100644 --- a/src/runtime/gencgc.c +++ b/src/runtime/gencgc.c @@ -41,6 +41,7 @@ #include "gc.h" #include "gc-internal.h" #include "thread.h" +#include "pseudo-atomic.h" #include "alloc.h" #include "genesis/vector.h" #include "genesis/weak-pointer.h" @@ -68,9 +69,7 @@ page_index_t gc_find_freeish_pages(long *restart_page_ptr, long nbytes, * scratch space by the collector, and should never get collected. */ enum { - HIGHEST_NORMAL_GENERATION = 5, - PSEUDO_STATIC_GENERATION, - SCRATCH_GENERATION, + SCRATCH_GENERATION = PSEUDO_STATIC_GENERATION+1, NUM_GENERATIONS }; @@ -88,7 +87,7 @@ long large_object_size = 4 * PAGE_BYTES; /* the verbosity level. All non-error messages are disabled at level 0; * and only a few rare messages are printed at level 1. */ -#ifdef QSHOW +#if QSHOW boolean gencgc_verbose = 1; #else boolean gencgc_verbose = 0; @@ -256,7 +255,12 @@ size_t void_diff(void *x, void *y) return (pointer_sized_uint_t)x - (pointer_sized_uint_t)y; } -/* a structure to hold the state of a generation */ +/* a structure to hold the state of a generation + * + * CAUTION: If you modify this, make sure to touch up the alien + * definition in src/code/gc.lisp accordingly. ...or better yes, + * deal with the FIXME there... + */ struct generation { /* the first page that gc_alloc() checks on its next call */ @@ -286,9 +290,9 @@ struct generation { /* the number of GCs since the last raise */ int num_gc; - /* the average age after which a GC will raise objects to the + /* the number of GCs to run on the generations before raising objects to the * next generation */ - int trigger_age; + int number_of_gcs_before_promotion; /* the cumulative sum of the bytes allocated to this generation. It is * cleared after a GC on this generations, and update before new @@ -300,7 +304,7 @@ struct generation { /* a minimum average memory age before a GC will occur helps * prevent a GC when a large number of new live objects have been * added, in which case a GC could be a waste of time */ - double min_av_mem_age; + double minimum_age_before_gc; /* A linked list of lutex structures in this generation, used for * implementing lutex finalization. */ @@ -383,7 +387,7 @@ count_generation_pages(generation_index_t generation) return count; } -#ifdef QSHOW +#if QSHOW static long count_dont_move_pages(void) { @@ -415,8 +419,8 @@ count_generation_bytes_allocated (generation_index_t gen) } /* Return the average age of the memory in a generation. */ -static double -gen_av_mem_age(generation_index_t gen) +extern double +generation_average_age(generation_index_t gen) { if (generations[gen].bytes_allocated == 0) return 0.0; @@ -428,10 +432,10 @@ gen_av_mem_age(generation_index_t gen) /* The verbose argument controls how much to print: 0 for normal * level of detail; 1 for debugging. */ -static void -print_generation_stats(int verbose) /* FIXME: should take FILE argument */ +extern void +print_generation_stats() /* FIXME: should take FILE argument, or construct a string */ { - generation_index_t i, gens; + generation_index_t i; #if defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64) #define FPU_STATE_SIZE 27 @@ -445,17 +449,11 @@ print_generation_stats(int verbose) /* FIXME: should take FILE argument */ * so they need to be saved and reset for C. */ fpu_save(fpu_state); - /* highest generation to print */ - if (verbose) - gens = SCRATCH_GENERATION; - else - gens = PSEUDO_STATIC_GENERATION; - /* Print the heap stats. */ fprintf(stderr, " Gen StaPg UbSta LaSta LUbSt Boxed Unboxed LB LUB !move Alloc Waste Trig WP GCs Mem-age\n"); - for (i = 0; i < gens; i++) { + for (i = 0; i < SCRATCH_GENERATION; i++) { page_index_t j; long boxed_cnt = 0; long unboxed_cnt = 0; @@ -505,10 +503,10 @@ print_generation_stats(int verbose) /* FIXME: should take FILE argument */ generations[i].gc_trigger, count_write_protect_generation_pages(i), generations[i].num_gc, - gen_av_mem_age(i)); + generation_average_age(i)); } fprintf(stderr," Total bytes allocated = %lu\n", bytes_allocated); - fprintf(stderr," Dynamic-space-size bytes = %lu\n", dynamic_space_size); + fprintf(stderr," Dynamic-space-size bytes = %u\n", dynamic_space_size); fpu_restore(fpu_state); } @@ -1046,7 +1044,7 @@ gc_alloc_large(long nbytes, int page_type_flag, struct alloc_region *alloc_regio int orig_first_page_bytes_used; long byte_cnt; int more; - long bytes_used; + unsigned long bytes_used; page_index_t next_page; int ret; @@ -1154,6 +1152,7 @@ static page_index_t gencgc_alloc_start_page = -1; void gc_heap_exhausted_error_or_lose (long available, long requested) { + struct thread *thread = arch_os_get_current_thread(); /* Write basic information before doing anything else: if we don't * call to lisp this is a must, and even if we do there is always * the danger that we bounce back here before the error has been @@ -1166,12 +1165,13 @@ gc_heap_exhausted_error_or_lose (long available, long requested) /* If we are in GC, or totally out of memory there is no way * to sanely transfer control to the lisp-side of things. */ - struct thread *thread = arch_os_get_current_thread(); - print_generation_stats(1); + print_generation_stats(); fprintf(stderr, "GC control variables:\n"); fprintf(stderr, " *GC-INHIBIT* = %s\n *GC-PENDING* = %s\n", SymbolValue(GC_INHIBIT,thread)==NIL ? "false" : "true", - SymbolValue(GC_PENDING,thread)==NIL ? "false" : "true"); + (SymbolValue(GC_PENDING, thread) == T) ? + "true" : ((SymbolValue(GC_PENDING, thread) == NIL) ? + "false" : "in progress")); #ifdef LISP_FEATURE_SB_THREAD fprintf(stderr, " *STOP-FOR-GC-PENDING* = %s\n", SymbolValue(STOP_FOR_GC_PENDING,thread)==NIL ? "false" : "true"); @@ -1181,6 +1181,18 @@ gc_heap_exhausted_error_or_lose (long available, long requested) else { /* FIXME: assert free_pages_lock held */ (void)thread_mutex_unlock(&free_pages_lock); + gc_assert(get_pseudo_atomic_atomic(thread)); + clear_pseudo_atomic_atomic(thread); + if (get_pseudo_atomic_interrupted(thread)) + do_pending_interrupt(); + /* Another issue is that signalling HEAP-EXHAUSTED error leads + * to running user code at arbitrary places, even in a + * WITHOUT-INTERRUPTS which may lead to a deadlock without + * running out of the heap. So at this point all bets are + * off. */ + if (SymbolValue(INTERRUPTS_ENABLED,thread) == NIL) + corruption_warning_and_maybe_lose + ("Signalling HEAP-EXHAUSTED in a WITHOUT-INTERRUPTS."); funcall2(StaticSymbolFunction(HEAP_EXHAUSTED_ERROR), alloc_number(available), alloc_number(requested)); lose("HEAP-EXHAUSTED-ERROR fell through"); @@ -1188,7 +1200,8 @@ gc_heap_exhausted_error_or_lose (long available, long requested) } page_index_t -gc_find_freeish_pages(page_index_t *restart_page_ptr, long nbytes, int page_type_flag) +gc_find_freeish_pages(page_index_t *restart_page_ptr, long nbytes, + int page_type_flag) { page_index_t first_page, last_page; page_index_t restart_page = *restart_page_ptr; @@ -1201,7 +1214,8 @@ gc_find_freeish_pages(page_index_t *restart_page_ptr, long nbytes, int page_type restart_page = gencgc_alloc_start_page; } - if (nbytes>=PAGE_BYTES) { + gc_assert(nbytes>=0); + if (((unsigned long)nbytes)>=PAGE_BYTES) { /* Search for a contiguous free space of at least nbytes, * aligned on a page boundary. The page-alignment is strictly * speaking needed only for objects at least large_object_size @@ -1667,7 +1681,7 @@ sniff_code_object(struct code *code, unsigned long displacement) unsigned d2 = *((unsigned char *)p - 2); unsigned d3 = *((unsigned char *)p - 3); unsigned d4 = *((unsigned char *)p - 4); -#ifdef QSHOW +#if QSHOW unsigned d5 = *((unsigned char *)p - 5); unsigned d6 = *((unsigned char *)p - 6); #endif @@ -2726,7 +2740,7 @@ preserve_pointer(void *addr) * probability that random garbage will be bogusly interpreted as * a pointer which prevents a page from moving. */ if (!(code_page_p(addr_page_index) - || (is_lisp_pointer(addr) && + || (is_lisp_pointer((lispobj)addr) && possibly_valid_dynamic_space_pointer(addr)))) return; @@ -4079,13 +4093,13 @@ garbage_collect_generation(generation_index_t generation, int raise) } #endif -#ifdef QSHOW +#if QSHOW if (gencgc_verbose > 1) { long num_dont_move_pages = count_dont_move_pages(); fprintf(stderr, "/non-movable pages due to conservative pointers = %d (%d bytes)\n", num_dont_move_pages, - npage_bytes(num_dont_move_pages); + npage_bytes(num_dont_move_pages)); } #endif @@ -4335,7 +4349,7 @@ collect_garbage(generation_index_t last_gen) } if (gencgc_verbose > 1) - print_generation_stats(0); + print_generation_stats(); do { /* Collect the generation. */ @@ -4346,7 +4360,7 @@ collect_garbage(generation_index_t last_gen) } else { raise = (gen < last_gen) - || (generations[gen].num_gc >= generations[gen].trigger_age); + || (generations[gen].num_gc >= generations[gen].number_of_gcs_before_promotion); } if (gencgc_verbose > 1) { @@ -4373,7 +4387,7 @@ collect_garbage(generation_index_t last_gen) if (gencgc_verbose > 1) { FSHOW((stderr, "GC of generation %d finished:\n", gen)); - print_generation_stats(0); + print_generation_stats(); } gen++; @@ -4383,8 +4397,8 @@ collect_garbage(generation_index_t last_gen) && raise && (generations[gen].bytes_allocated > generations[gen].gc_trigger) - && (gen_av_mem_age(gen) - > generations[gen].min_av_mem_age)))); + && (generation_average_age(gen) + > generations[gen].minimum_age_before_gc)))); /* Now if gen-1 was raised all generations before gen are empty. * If it wasn't raised then all generations before gen-1 are empty. @@ -4519,7 +4533,7 @@ gc_free_heap(void) } if (gencgc_verbose > 1) - print_generation_stats(0); + print_generation_stats(); /* Initialize gc_alloc(). */ gc_alloc_generation = 0; @@ -4588,8 +4602,8 @@ gc_init(void) generations[i].cum_sum_bytes_allocated = 0; /* the tune-able parameters */ generations[i].bytes_consed_between_gc = 2000000; - generations[i].trigger_age = 1; - generations[i].min_av_mem_age = 0.75; + generations[i].number_of_gcs_before_promotion = 1; + generations[i].minimum_age_before_gc = 0.75; generations[i].lutexes = NULL; } @@ -4615,7 +4629,6 @@ gencgc_pickup_dynamic(void) generation_index_t gen = PSEUDO_STATIC_GENERATION; do { lispobj *first,*ptr= (lispobj *)page_address(page); - page_table[page].allocated = BOXED_PAGE_FLAG; page_table[page].gen = gen; page_table[page].bytes_used = PAGE_BYTES; page_table[page].large_object = 0; @@ -4625,8 +4638,10 @@ gencgc_pickup_dynamic(void) page_table[page].need_to_zero = 1; if (!gencgc_partial_pickup) { + page_table[page].allocated = BOXED_PAGE_FLAG; first=gc_search_space(prev,(ptr+2)-prev,ptr); - if(ptr == first) prev=ptr; + if(ptr == first) + prev=ptr; page_table[page].region_start_offset = page_address(page) - (void *)prev; } @@ -4706,8 +4721,21 @@ general_alloc_internal(long nbytes, int page_type_flag, struct alloc_region *reg /* set things up so that GC happens when we finish the PA * section */ SetSymbolValue(GC_PENDING,T,thread); - if (SymbolValue(GC_INHIBIT,thread) == NIL) - set_pseudo_atomic_interrupted(thread); + if (SymbolValue(GC_INHIBIT,thread) == NIL) { + set_pseudo_atomic_interrupted(thread); +#ifdef LISP_FEATURE_PPC + /* PPC calls alloc() from a trap or from pa_alloc(), + * look up the most context if it's from a trap. */ + { + os_context_t *context = + thread->interrupt_data->allocation_trap_context; + maybe_save_gc_mask_and_block_deferrables + (context ? os_context_sigmask_addr(context) : NULL); + } +#else + maybe_save_gc_mask_and_block_deferrables(NULL); +#endif + } } } new_obj = gc_alloc_with_region(nbytes, page_type_flag, region, 0); @@ -4717,7 +4745,7 @@ general_alloc_internal(long nbytes, int page_type_flag, struct alloc_region *reg if ((alloc_signal & FIXNUM_TAG_MASK) == 0) { if ((signed long) alloc_signal <= 0) { SetSymbolValue(ALLOC_SIGNAL, T, thread); - thread_kill(thread->os_thread, SIGPROF); + raise(SIGPROF); } else { SetSymbolValue(ALLOC_SIGNAL, alloc_signal - (1 << N_FIXNUM_TAG_BITS), @@ -4757,6 +4785,7 @@ general_alloc(long nbytes, int page_type_flag) lispobj * alloc(long nbytes) { + gc_assert(get_pseudo_atomic_atomic(arch_os_get_current_thread())); return general_alloc(nbytes, BOXED_PAGE_FLAG); } @@ -4781,7 +4810,7 @@ gencgc_handle_wp_violation(void* fault_addr) { page_index_t page_index = find_page_index(fault_addr); -#ifdef QSHOW_SIGNALS +#if QSHOW_SIGNALS FSHOW((stderr, "heap WP violation? fault_addr=%x, page_index=%d\n", fault_addr, page_index)); #endif @@ -4797,6 +4826,9 @@ gencgc_handle_wp_violation(void* fault_addr) return 0; } else { + int ret; + ret = thread_mutex_lock(&free_pages_lock); + gc_assert(ret == 0); if (page_table[page_index].write_protected) { /* Unprotect the page. */ os_protect(page_address(page_index), PAGE_BYTES, OS_VM_PROT_ALL); @@ -4814,6 +4846,8 @@ gencgc_handle_wp_violation(void* fault_addr) page_index, boxed_region.first_page, boxed_region.last_page); } + ret = thread_mutex_unlock(&free_pages_lock); + gc_assert(ret == 0); /* Don't worry, we can handle it. */ return 1; }