[project @ 2005-04-10 21:44:10 by simonmar]
[ghc-hetmet.git] / ghc / rts / Storage.c
index 367530f..14e4444 100644 (file)
@@ -1,7 +1,6 @@
 /* -----------------------------------------------------------------------------
- * $Id: Storage.c,v 1.84 2004/08/13 13:11:01 simonmar Exp $
  *
- * (c) The GHC Team, 1998-1999
+ * (c) The GHC Team, 1998-2004
  *
  * Storage manager front end
  *
 #include "Weak.h"
 #include "Sanity.h"
 #include "Arena.h"
-
+#include "OSThreads.h"
+#include "Capability.h"
 #include "Storage.h"
 #include "Schedule.h"
-#include "OSThreads.h"
-
 #include "RetainerProfile.h"   // for counting memory blocks (memInventory)
 
 #include <stdlib.h>
 #include <string.h>
 
 StgClosure    *caf_list         = NULL;
+StgClosure    *revertible_caf_list = NULL;
+rtsBool       keepCAFs;
 
 bdescr *small_alloc_list;      /* allocate()d small objects */
 bdescr *pinned_object_block;    /* allocate pinned objects into this block */
@@ -60,6 +60,18 @@ static void *stgAllocForGMP   (size_t size_in_bytes);
 static void *stgReallocForGMP (void *ptr, size_t old_size, size_t new_size);
 static void  stgDeallocForGMP (void *ptr, size_t size);
 
+/*
+ * Storage manager mutex
+ */
+#if defined(SMP)
+extern Mutex sm_mutex;
+#define ACQUIRE_SM_LOCK   ACQUIRE_LOCK(&sm_mutex)
+#define RELEASE_SM_LOCK   RELEASE_LOCK(&sm_mutex)
+#else
+#define ACQUIRE_SM_LOCK
+#define RELEASE_SM_LOCK
+#endif
+
 void
 initStorage( void )
 {
@@ -88,7 +100,7 @@ initStorage( void )
   if (RtsFlags.GcFlags.maxHeapSize != 0 &&
       RtsFlags.GcFlags.minAllocAreaSize > 
       RtsFlags.GcFlags.maxHeapSize) {
-      prog_belch("maximum heap size (-M) is smaller than minimum alloc area size (-A)");
+      errorBelch("maximum heap size (-M) is smaller than minimum alloc area size (-A)");
       exit(1);
   }
 
@@ -107,8 +119,7 @@ initStorage( void )
   for(g = 0; g < RtsFlags.GcFlags.generations; g++) {
     gen = &generations[g];
     gen->no = g;
-    gen->mut_list = END_MUT_LIST;
-    gen->mut_once_list = END_MUT_LIST;
+    gen->mut_list = allocBlock();
     gen->collections = 0;
     gen->failed_promotions = 0;
     gen->max_blocks = 0;
@@ -177,7 +188,7 @@ initStorage( void )
   /* The oldest generation has one step and it is compacted. */
   if (RtsFlags.GcFlags.compact) {
       if (RtsFlags.GcFlags.generations == 1) {
-         belch("WARNING: compaction is incompatible with -G1; disabled");
+         errorBelch("WARNING: compaction is incompatible with -G1; disabled");
       } else {
          oldest_gen->steps[0].is_compacted = 1;
       }
@@ -199,6 +210,7 @@ initStorage( void )
 
   weak_ptr_list = NULL;
   caf_list = NULL;
+  revertible_caf_list = NULL;
    
   /* initialise the allocate() interface */
   small_alloc_list = NULL;
@@ -261,25 +273,43 @@ exitStorage (void)
 void
 newCAF(StgClosure* caf)
 {
-  /* Put this CAF on the mutable list for the old generation.
-   * This is a HACK - the IND_STATIC closure doesn't really have
-   * a mut_link field, but we pretend it has - in fact we re-use
-   * the STATIC_LINK field for the time being, because when we
-   * come to do a major GC we won't need the mut_link field
-   * any more and can use it as a STATIC_LINK.
-   */
   ACQUIRE_SM_LOCK;
 
-  ((StgIndStatic *)caf)->saved_info = NULL;
-  ((StgMutClosure *)caf)->mut_link = oldest_gen->mut_once_list;
-  oldest_gen->mut_once_list = (StgMutClosure *)caf;
-
+  if(keepCAFs)
+  {
+    // HACK:
+    // If we are in GHCi _and_ we are using dynamic libraries,
+    // then we can't redirect newCAF calls to newDynCAF (see below),
+    // so we make newCAF behave almost like newDynCAF.
+    // The dynamic libraries might be used by both the interpreted
+    // program and GHCi itself, so they must not be reverted.
+    // This also means that in GHCi with dynamic libraries, CAFs are not
+    // garbage collected. If this turns out to be a problem, we could
+    // do another hack here and do an address range test on caf to figure
+    // out whether it is from a dynamic library.
+    ((StgIndStatic *)caf)->saved_info  = (StgInfoTable *)caf->header.info;
+    ((StgIndStatic *)caf)->static_link = caf_list;
+    caf_list = caf;
+  }
+  else
+  {
+    /* Put this CAF on the mutable list for the old generation.
+    * This is a HACK - the IND_STATIC closure doesn't really have
+    * a mut_link field, but we pretend it has - in fact we re-use
+    * the STATIC_LINK field for the time being, because when we
+    * come to do a major GC we won't need the mut_link field
+    * any more and can use it as a STATIC_LINK.
+    */
+    ((StgIndStatic *)caf)->saved_info = NULL;
+    recordMutableGen(caf, oldest_gen);
+  }
+  
   RELEASE_SM_LOCK;
 
 #ifdef PAR
   /* If we are PAR or DIST then  we never forget a CAF */
   { globalAddr *newGA;
-    //belch("<##> Globalising CAF %08x %s",caf,info_type(caf));
+    //debugBelch("<##> Globalising CAF %08x %s",caf,info_type(caf));
     newGA=makeGlobal(caf,rtsTrue); /*given full weight*/
     ASSERT(newGA);
   } 
@@ -290,6 +320,8 @@ newCAF(StgClosure* caf)
 // object code in GHCi.  In this case we want to retain *all* CAFs in
 // the object code, because they might be demanded at any time from an
 // expression evaluated on the command line.
+// Also, GHCi might want to revert CAFs, so we add these to the
+// revertible_caf_list.
 //
 // The linker hackily arranges that references to newCaf from dynamic
 // code end up pointing to newDynCAF.
@@ -299,8 +331,8 @@ newDynCAF(StgClosure *caf)
     ACQUIRE_SM_LOCK;
 
     ((StgIndStatic *)caf)->saved_info  = (StgInfoTable *)caf->header.info;
-    ((StgIndStatic *)caf)->static_link = caf_list;
-    caf_list = caf;
+    ((StgIndStatic *)caf)->static_link = revertible_caf_list;
+    revertible_caf_list = caf;
 
     RELEASE_SM_LOCK;
 }
@@ -314,19 +346,12 @@ allocNurseries( void )
 { 
 #ifdef SMP
   Capability *cap;
-  bdescr *bd;
 
   g0s0->blocks = NULL;
   g0s0->n_blocks = 0;
   for (cap = free_capabilities; cap != NULL; cap = cap->link) {
     cap->r.rNursery = allocNursery(NULL, RtsFlags.GcFlags.minAllocAreaSize);
     cap->r.rCurrentNursery = cap->r.rNursery;
-    /* Set the back links to be equal to the Capability,
-     * so we can do slightly better informed locking.
-     */
-    for (bd = cap->r.rNursery; bd != NULL; bd = bd->link) {
-      bd->u.back = (bdescr *)cap;
-    }
   }
 #else /* SMP */
   g0s0->blocks      = allocNursery(NULL, RtsFlags.GcFlags.minAllocAreaSize);
@@ -343,13 +368,17 @@ void
 resetNurseries( void )
 {
   bdescr *bd;
-#ifdef SMP
   Capability *cap;
-  
-  /* All tasks must be stopped */
-  ASSERT(n_free_capabilities == RtsFlags.ParFlags.nNodes);
 
-  for (cap = free_capabilities; cap != NULL; cap = cap->link) {
+#ifdef SMP
+  /* All tasks must be stopped */
+  ASSERT(rts_n_free_capabilities == RtsFlags.ParFlags.nNodes);
+  for (cap = free_capabilities; cap != NULL; cap = cap->link)
+#else
+  cap = &MainCapability;
+  ASSERT(cap->r.rNursery == g0s0->blocks);
+#endif
+  {
     for (bd = cap->r.rNursery; bd; bd = bd->link) {
       bd->free = bd->start;
       ASSERT(bd->gen_no == 0);
@@ -358,16 +387,6 @@ resetNurseries( void )
     }
     cap->r.rCurrentNursery = cap->r.rNursery;
   }
-#else
-  for (bd = g0s0->blocks; bd; bd = bd->link) {
-    bd->free = bd->start;
-    ASSERT(bd->gen_no == 0);
-    ASSERT(bd->step == g0s0);
-    IF_DEBUG(sanity,memset(bd->start, 0xaa, BLOCK_SIZE));
-  }
-  MainCapability.r.rNursery = g0s0->blocks;
-  MainCapability.r.rCurrentNursery = g0s0->blocks;
-#endif
 }
 
 bdescr *
@@ -418,7 +437,7 @@ resizeNursery ( nat blocks )
   }
 
   else if (nursery_blocks < blocks) {
-    IF_DEBUG(gc, fprintf(stderr, "Increasing size of nursery to %d blocks\n", 
+    IF_DEBUG(gc, debugBelch("Increasing size of nursery to %d blocks\n", 
                         blocks));
     g0s0->blocks = allocNursery(g0s0->blocks, blocks-nursery_blocks);
   } 
@@ -426,7 +445,7 @@ resizeNursery ( nat blocks )
   else {
     bdescr *next_bd;
     
-    IF_DEBUG(gc, fprintf(stderr, "Decreasing size of nursery to %d blocks\n", 
+    IF_DEBUG(gc, debugBelch("Decreasing size of nursery to %d blocks\n", 
                         blocks));
 
     bd = g0s0->blocks;
@@ -447,6 +466,8 @@ resizeNursery ( nat blocks )
   
   g0s0->n_blocks = blocks;
   ASSERT(countBlocks(g0s0->blocks) == g0s0->n_blocks);
+
+  MainCapability.r.rNursery = g0s0->blocks;
 }
 
 /* -----------------------------------------------------------------------------
@@ -674,7 +695,7 @@ calcAllocated( void )
   /*  ASSERT(n_free_capabilities == RtsFlags.ParFlags.nNodes); */
 
   allocated = 
-    n_free_capabilities * RtsFlags.GcFlags.minAllocAreaSize * BLOCK_SIZE_W
+    rts_n_free_capabilities * RtsFlags.GcFlags.minAllocAreaSize * BLOCK_SIZE_W
     + allocated_bytes();
 
   for (cap = free_capabilities; cap != NULL; cap = cap->link) {
@@ -792,25 +813,28 @@ memInventory(void)
   /* count the blocks we current have */
 
   for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
-    for (s = 0; s < generations[g].n_steps; s++) {
-      stp = &generations[g].steps[s];
-      total_blocks += stp->n_blocks;
-      if (RtsFlags.GcFlags.generations == 1) {
-       /* two-space collector has a to-space too :-) */
-       total_blocks += g0s0->n_to_blocks;
+      for (bd = generations[g].mut_list; bd != NULL; bd = bd->link) {
+         total_blocks += bd->blocks;
       }
-      for (bd = stp->large_objects; bd; bd = bd->link) {
-       total_blocks += bd->blocks;
-       /* hack for megablock groups: they have an extra block or two in
-          the second and subsequent megablocks where the block
-          descriptors would normally go.
-       */
-       if (bd->blocks > BLOCKS_PER_MBLOCK) {
-         total_blocks -= (MBLOCK_SIZE / BLOCK_SIZE - BLOCKS_PER_MBLOCK)
-                         * (bd->blocks/(MBLOCK_SIZE/BLOCK_SIZE));
-       }
+      for (s = 0; s < generations[g].n_steps; s++) {
+         stp = &generations[g].steps[s];
+         total_blocks += stp->n_blocks;
+         if (RtsFlags.GcFlags.generations == 1) {
+             /* two-space collector has a to-space too :-) */
+             total_blocks += g0s0->n_to_blocks;
+         }
+         for (bd = stp->large_objects; bd; bd = bd->link) {
+             total_blocks += bd->blocks;
+             /* hack for megablock groups: they have an extra block or two in
+                the second and subsequent megablocks where the block
+                descriptors would normally go.
+             */
+             if (bd->blocks > BLOCKS_PER_MBLOCK) {
+                 total_blocks -= (MBLOCK_SIZE / BLOCK_SIZE - BLOCKS_PER_MBLOCK)
+                     * (bd->blocks/(MBLOCK_SIZE/BLOCK_SIZE));
+             }
+         }
       }
-    }
   }
 
   /* any blocks held by allocate() */
@@ -832,7 +856,7 @@ memInventory(void)
 
   if (total_blocks + free_blocks != mblocks_allocated *
       BLOCKS_PER_MBLOCK) {
-    fprintf(stderr, "Blocks: %ld live + %ld free  = %ld total (%ld around)\n",
+    debugBelch("Blocks: %ld live + %ld free  = %ld total (%ld around)\n",
            total_blocks, free_blocks, total_blocks + free_blocks,
            mblocks_allocated * BLOCKS_PER_MBLOCK);
   }
@@ -873,7 +897,6 @@ checkSanity( void )
                checkChain(generations[g].steps[s].large_objects);
                if (g > 0) {
                    checkMutableList(generations[g].mut_list, g);
-                   checkMutOnceList(generations[g].mut_once_list, g);
                }
            }
        }