[project @ 2002-09-05 16:26:33 by simonmar]
[ghc-hetmet.git] / ghc / rts / GC.c
index 8dbe589..88a265d 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: GC.c,v 1.133 2002/04/13 05:16:25 sof Exp $
+ * $Id: GC.c,v 1.139 2002/09/05 16:26:33 simonmar Exp $
  *
  * (c) The GHC Team 1998-1999
  *
@@ -45,6 +45,8 @@
 #include "RetainerProfile.h"
 #include "LdvProfile.h"
 
+#include <string.h>
+
 /* STATIC OBJECT LIST.
  *
  * During GC:
@@ -79,8 +81,8 @@
  * We build up a static object list while collecting generations 0..N,
  * which is then appended to the static object list of generation N+1.
  */
-StgClosure* static_objects;          // live static objects
-StgClosure* scavenged_static_objects; // static objects scavenged so far
+static StgClosure* static_objects;      // live static objects
+StgClosure* scavenged_static_objects;   // static objects scavenged so far
 
 /* N is the oldest generation being collected, where the generations
  * are numbered starting at 0.  A major GC (indicated by the major_gc
@@ -118,16 +120,16 @@ static rtsBool failed_to_evac;
 
 /* Old to-space (used for two-space collector only)
  */
-bdescr *old_to_blocks;
+static bdescr *old_to_blocks;
 
 /* Data used for allocation area sizing.
  */
-lnat new_blocks;               // blocks allocated during this GC 
-lnat g0s0_pcnt_kept = 30;      // percentage of g0s0 live at last minor GC 
+static lnat new_blocks;                 // blocks allocated during this GC 
+static lnat g0s0_pcnt_kept = 30; // percentage of g0s0 live at last minor GC 
 
 /* Used to avoid long recursion due to selector thunks
  */
-lnat thunk_selector_depth = 0;
+static lnat thunk_selector_depth = 0;
 #define MAX_THUNK_SELECTOR_DEPTH 256
 
 /* -----------------------------------------------------------------------------
@@ -990,11 +992,11 @@ GarbageCollect ( void (*get_roots)(evac_fn), rtsBool force_major_gc )
   // start any pending finalizers 
   scheduleFinalizers(old_weak_ptr_list);
   
-  ACQUIRE_LOCK(&sched_mutex);
-
   // send exceptions to any threads which were about to die 
   resurrectThreads(resurrected_threads);
   
+  ACQUIRE_LOCK(&sched_mutex);
+
   // Update the stable pointer hash table.
   updateStablePtrTable(major_gc);
 
@@ -1104,31 +1106,41 @@ traverse_weak_ptr_list(void)
              continue;
          }
          
-         ASSERT(get_itbl(w)->type == WEAK);
-         
-         /* Now, check whether the key is reachable.
-          */
-         new = isAlive(w->key);
-         if (new != NULL) {
-             w->key = new;
-             // evacuate the value and finalizer 
-             w->value = evacuate(w->value);
-             w->finalizer = evacuate(w->finalizer);
-             // remove this weak ptr from the old_weak_ptr list 
-             *last_w = w->link;
-             // and put it on the new weak ptr list 
-             next_w  = w->link;
-             w->link = weak_ptr_list;
-             weak_ptr_list = w;
-             flag = rtsTrue;
-             IF_DEBUG(weak, belch("Weak pointer still alive at %p -> %p", 
-                                  w, w->key));
-             continue;
-         }
-         else {
-             last_w = &(w->link);
-             next_w = w->link;
+         switch (get_itbl(w)->type) {
+
+         case EVACUATED:
+             next_w = (StgWeak *)((StgEvacuated *)w)->evacuee;
+             *last_w = next_w;
              continue;
+
+         case WEAK:
+             /* Now, check whether the key is reachable.
+              */
+             new = isAlive(w->key);
+             if (new != NULL) {
+                 w->key = new;
+                 // evacuate the value and finalizer 
+                 w->value = evacuate(w->value);
+                 w->finalizer = evacuate(w->finalizer);
+                 // remove this weak ptr from the old_weak_ptr list 
+                 *last_w = w->link;
+                 // and put it on the new weak ptr list 
+                 next_w  = w->link;
+                 w->link = weak_ptr_list;
+                 weak_ptr_list = w;
+                 flag = rtsTrue;
+                 IF_DEBUG(weak, belch("Weak pointer still alive at %p -> %p", 
+                                      w, w->key));
+                 continue;
+             }
+             else {
+                 last_w = &(w->link);
+                 next_w = w->link;
+                 continue;
+             }
+
+         default:
+             barf("traverse_weak_ptr_list: not WEAK");
          }
       }
       
@@ -1241,6 +1253,9 @@ mark_weak_ptr_list ( StgWeak **list )
 
   last_w = list;
   for (w = *list; w; w = w->link) {
+      // w might be WEAK, EVACUATED, or DEAD_WEAK (actually CON_STATIC) here
+      ASSERT(w->header.info == &stg_DEAD_WEAK_info 
+            || get_itbl(w)->type == WEAK || get_itbl(w)->type == EVACUATED);
       (StgClosure *)w = evacuate((StgClosure *)w);
       *last_w = w;
       last_w = &(w->link);
@@ -1273,6 +1288,7 @@ isAlive(StgClosure *p)
 
   loop:
     bd = Bdescr((P_)p);
+
     // ignore closures in generations that we're not collecting. 
     if (LOOKS_LIKE_STATIC(p) || bd->gen_no > N) {
        return p;
@@ -1453,8 +1469,8 @@ copyPart(StgClosure *src, nat size_to_reserve, nat size_to_copy, step *stp)
    Evacuate a large object
 
    This just consists of removing the object from the (doubly-linked)
-   large_alloc_list, and linking it on to the (singly-linked)
-   new_large_objects list, from where it will be scavenged later.
+   step->large_objects list, and linking it on to the (singly-linked)
+   step->new_large_objects list, from where it will be scavenged later.
 
    Convention: bd->flags has BF_EVACUATED set for a large object
    that has been evacuated, or unset otherwise.
@@ -1583,9 +1599,6 @@ loop:
   if (HEAP_ALLOCED(q)) {
     bd = Bdescr((P_)q);
 
-    // not a group head: find the group head
-    if (bd->blocks == 0) { bd = bd->link; }
-
     if (bd->gen_no > N) {
        /* Can't evacuate this object, because it's in a generation
         * older than the ones we're collecting.  Let's hope that it's
@@ -1714,6 +1727,15 @@ loop:
       const StgInfoTable* selectee_info;
       StgClosure* selectee = ((StgSelector*)q)->selectee;
 
+      // We only recurse a certain depth through selector thunks.
+      // NOTE: the depth is maintained manually, and we must be very
+      // careful to always decrement it before returning.
+      //
+      thunk_selector_depth++;
+      if (thunk_selector_depth > MAX_THUNK_SELECTOR_DEPTH) {
+         goto selector_abandon;
+      }
+
     selector_loop:
       selectee_info = get_itbl(selectee);
       switch (selectee_info->type) {
@@ -1733,29 +1755,30 @@ loop:
                 (StgWord32)(selectee_info->layout.payload.ptrs + 
                            selectee_info->layout.payload.nptrs));
 
+         // The thunk is now under evaluation, so we overwrite it
+         // with a BLACKHOLE.  This has a beneficial effect if the
+         // selector thunk eventually refers to itself: we won't
+         // recurse indefinitely, and the object which eventually
+         // gets evacuated will be a BLACKHOLE (as it should be: a
+         // selector thunk which refers to itself can only have value
+         // _|_).
+         SET_INFO(q,&stg_BLACKHOLE_info);
+
          // perform the selection! 
-         q = selectee->payload[offset];
+         selectee = selectee->payload[offset];
           if (major_gc==rtsTrue) {TICK_GC_SEL_MAJOR();} else {TICK_GC_SEL_MINOR();}
-
-         /* if we're already in to-space, there's no need to continue
-          * with the evacuation, just update the source address with
-          * a pointer to the (evacuated) constructor field.
-          */
-         if (HEAP_ALLOCED(q)) {
-           bdescr *bd = Bdescr((P_)q);
-           if (bd->flags & BF_EVACUATED) {
-             if (bd->gen_no < evac_gen) {
-               failed_to_evac = rtsTrue;
-               TICK_GC_FAILED_PROMOTION();
-             }
-             return q;
-           }
-         }
-
-         /* otherwise, carry on and evacuate this constructor field,
-          * (but not the constructor itself)
-          */
-         goto loop;
+         // Carry on and evacuate this constructor field,
+         // (but not the constructor itself)
+         //
+         // It is tempting to just 'goto loop;' at this point, but
+         // that doesn't give us a way to decrement
+         // thunk_selector_depth later.  So we recurse (boundedly)
+         // into evacuate().
+         // 
+         selectee = evacuate(selectee);
+         upd_evacuee(q,selectee);
+         thunk_selector_depth--;
+         return selectee;
        }
 
       case IND:
@@ -1767,34 +1790,28 @@ loop:
        goto selector_loop;
 
       case EVACUATED:
-       selectee = ((StgEvacuated *)selectee)->evacuee;
-       goto selector_loop;
+         // We could follow forwarding pointers here too, but we don't
+         // for two reasons:
+         //    * If the constructor has already been evacuated, then
+         //      we're only doing the evaluation early, not fixing a
+         //      space leak.
+         //    * When we finally reach the destination, we have to
+         //      figure out whether we are in to-space or not, and this
+         //      is somewhat awkward.
+         //
+         //   selectee = ((StgEvacuated *)selectee)->evacuee;
+         //   goto selector_loop;
+         break;
 
       case THUNK_SELECTOR:
-#         if 0
-          /* Disabled 03 April 2001 by JRS; it seems to cause the GC (or
-             something) to go into an infinite loop when the nightly
-             stage2 compiles PrelTup.lhs. */
-
          /* we can't recurse indefinitely in evacuate(), so set a
           * limit on the number of times we can go around this
           * loop.
           */
-         if (thunk_selector_depth < MAX_THUNK_SELECTOR_DEPTH) {
-             bdescr *bd;
-             bd = Bdescr((P_)selectee);
-             if (!bd->flags & BF_EVACUATED) {
-                 thunk_selector_depth++;
-                 selectee = evacuate(selectee);
-                 thunk_selector_depth--;
-                 goto selector_loop;
-             }
-         } else {
-              TICK_GC_SEL_ABANDONED();
-              // and fall through...
-          }
-#         endif
-
+         q = evacuate(selectee);
+         thunk_selector_depth--;
+         return q;
+         
       case AP_UPD:
       case THUNK:
       case THUNK_1_0:
@@ -1808,8 +1825,8 @@ loop:
       case SE_BLACKHOLE:
       case BLACKHOLE:
       case BLACKHOLE_BQ:
-       // not evaluated yet 
-       break;
+         // not evaluated yet 
+         break;
 
 #if defined(PAR)
        // a copy of the top-level cases below 
@@ -1820,12 +1837,14 @@ loop:
          //ToDo: derive size etc from reverted IP
          //to = copy(q,size,stp);
          // recordMutable((StgMutClosure *)to);
+         thunk_selector_depth--;
          return to;
        }
     
       case BLOCKED_FETCH:
        ASSERT(sizeofW(StgBlockedFetch) >= MIN_NONUPD_SIZE);
        to = copy(q,sizeofW(StgBlockedFetch),stp);
+       thunk_selector_depth--;
        return to;
 
 # ifdef DIST    
@@ -1834,11 +1853,13 @@ loop:
       case FETCH_ME:
        ASSERT(sizeofW(StgBlockedFetch) >= MIN_UPD_SIZE);
        to = copy(q,sizeofW(StgFetchMe),stp);
+       thunk_selector_depth--;
        return to;
     
       case FETCH_ME_BQ:
        ASSERT(sizeofW(StgBlockedFetch) >= MIN_UPD_SIZE);
        to = copy(q,sizeofW(StgFetchMeBlockingQueue),stp);
+       thunk_selector_depth--;
        return to;
 #endif
 
@@ -1847,6 +1868,8 @@ loop:
             (int)(selectee_info->type));
       }
     }
+  selector_abandon:
+    thunk_selector_depth--;
     return copy(q,THUNK_SELECTOR_sizeW(),stp);
 
   case IND:
@@ -1929,7 +1952,7 @@ loop:
      */
     if (evac_gen > 0) {                // optimisation 
       StgClosure *p = ((StgEvacuated*)q)->evacuee;
-      if (Bdescr((P_)p)->gen_no < evac_gen) {
+      if (HEAP_ALLOCED(p) && Bdescr((P_)p)->gen_no < evac_gen) {
        failed_to_evac = rtsTrue;
        TICK_GC_FAILED_PROMOTION();
       }
@@ -2186,6 +2209,8 @@ scavenge(step *stp)
     info = get_itbl((StgClosure *)p);
     ASSERT(p && (LOOKS_LIKE_GHC_INFO(info) || IS_HUGS_CONSTR_INFO(info)));
     
+    ASSERT(thunk_selector_depth == 0);
+
     q = p;
     switch (info->type) {