1.0.32.33: GENCGC tuning parameters
[sbcl.git] / src / runtime / gencgc.c
index 5d687f4..5bd67c8 100644 (file)
@@ -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,7 +503,7 @@ 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 = %u\n", dynamic_space_size);
@@ -1167,11 +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.
          */
-        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");
@@ -1681,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
@@ -4093,7 +4093,7 @@ 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,
@@ -4349,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. */
@@ -4360,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) {
@@ -4387,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++;
@@ -4397,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.
@@ -4533,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;
@@ -4602,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;
     }
 
@@ -4629,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;
@@ -4639,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;
         }
@@ -4720,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);
@@ -4731,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),
@@ -4796,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
@@ -4812,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);
@@ -4829,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;
     }