Maintain per-generation lists of weak pointers (#7847)
authorTakano Akio <aljee@hyper.cx>
Mon, 11 Mar 2013 09:51:05 +0000 (18:51 +0900)
committerIan Lynagh <ian@well-typed.com>
Sat, 15 Jun 2013 15:41:02 +0000 (16:41 +0100)
12 files changed:
includes/rts/storage/GC.h
includes/stg/MiscClosures.h
rts/PrimOps.cmm
rts/RetainerProfile.c
rts/RtsStartup.c
rts/Weak.c
rts/Weak.h
rts/sm/Compact.c
rts/sm/GC.c
rts/sm/MarkWeak.c
rts/sm/Storage.c
utils/deriveConstants/DeriveConstants.hs

index 6ddd301..fb5e21e 100644 (file)
@@ -83,6 +83,8 @@ typedef struct generation_ {
 
     StgTSO *       threads;             // threads in this gen
                                         // linked via global_link
+    StgWeak *      weak_ptr_list;       // weak pointers in this gen
+
     struct generation_ *to;            // destination gen for live objects
 
     // stats information
@@ -116,6 +118,7 @@ typedef struct generation_ {
     bdescr *     bitmap;               // bitmap for compacting collection
 
     StgTSO *     old_threads;
+    StgWeak *    old_weak_ptr_list;
 } generation;
 
 extern generation * generations;
index db0a32e..ca5280f 100644 (file)
@@ -465,7 +465,6 @@ extern StgWord stg_stack_save_entries[];
 // Storage.c
 extern unsigned int RTS_VAR(g0);
 extern unsigned int RTS_VAR(large_alloc_lim);
-extern StgWord RTS_VAR(weak_ptr_list);
 extern StgWord RTS_VAR(atomic_modify_mutvar_mutex);
 
 // RtsFlags
index b44dfe7..c12c6d8 100644 (file)
@@ -380,8 +380,8 @@ stg_mkWeakzh ( gcptr key,
   StgWeak_cfinalizers(w) = stg_NO_FINALIZER_closure;
 
   ACQUIRE_LOCK(sm_mutex);
-  StgWeak_link(w)       = W_[weak_ptr_list];
-  W_[weak_ptr_list]     = w;
+  StgWeak_link(w) = generation_weak_ptr_list(W_[g0]);
+  generation_weak_ptr_list(W_[g0]) = w;
   RELEASE_LOCK(sm_mutex);
 
   IF_DEBUG(weak, ccall debugBelch(stg_weak_msg,w));
index 4e7ed3e..10a947d 100644 (file)
@@ -1767,9 +1767,11 @@ computeRetainerSet( void )
     //
     // The following code assumes that WEAK objects are considered to be roots
     // for retainer profilng.
-    for (weak = weak_ptr_list; weak != NULL; weak = weak->link)
-       // retainRoot((StgClosure *)weak);
-       retainRoot(NULL, (StgClosure **)&weak);
+    for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
+        for (weak = generations[g].weak_ptr_list; weak != NULL; weak = weak->link)
+            // retainRoot((StgClosure *)weak);
+            retainRoot(NULL, (StgClosure **)&weak);
+    }
 
     // Consider roots from the stable ptr table.
     markStableTables(retainRoot, NULL);
index d8c2058..39c5ef1 100644 (file)
@@ -295,6 +295,8 @@ hs_add_root(void (*init_root)(void) STG_UNUSED)
 static void
 hs_exit_(rtsBool wait_foreign)
 {
+    nat g;
+
     if (hs_init_count <= 0) {
        errorBelch("warning: too many hs_exit()s");
        return;
@@ -325,7 +327,9 @@ hs_exit_(rtsBool wait_foreign)
     exitScheduler(wait_foreign);
 
     /* run C finalizers for all active weak pointers */
-    runAllCFinalizers(weak_ptr_list);
+    for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
+        runAllCFinalizers(generations[g].weak_ptr_list);
+    }
 
 #if defined(RTS_USER_SIGNALS)
     if (RtsFlags.MiscFlags.install_signal_handlers) {
index e7a1257..98ac760 100644 (file)
@@ -16,8 +16,6 @@
 #include "Prelude.h"
 #include "Trace.h"
 
-StgWeak *weak_ptr_list;
-
 void
 runCFinalizers(StgCFinalizerList *list)
 {
index 7892277..fbdf18a 100644 (file)
@@ -14,7 +14,7 @@
 #include "BeginPrivate.h"
 
 extern rtsBool running_finalizers;
-extern StgWeak * weak_ptr_list;
+extern StgWeak * dead_weak_ptr_list;
 
 void runCFinalizers(StgCFinalizerList *list);
 void runAllCFinalizers(StgWeak *w);
index ffa355a..91ad8ab 100644 (file)
@@ -917,11 +917,13 @@ compact(StgClosure *static_objects)
     markScheduler((evac_fn)thread_root, NULL);
 
     // the weak pointer lists...
-    if (weak_ptr_list != NULL) {
-       thread((void *)&weak_ptr_list);
+    for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
+        if (generations[g].weak_ptr_list != NULL)
+            thread((void *)&generations[g].weak_ptr_list);
     }
-    if (old_weak_ptr_list != NULL) {
-       thread((void *)&old_weak_ptr_list); // tmp
+
+    if (dead_weak_ptr_list != NULL) {
+       thread((void *)&dead_weak_ptr_list); // tmp
     }
 
     // mutable lists
index ddb5384..28593f5 100644 (file)
@@ -699,7 +699,7 @@ GarbageCollect (nat collect_gen,
   // Start any pending finalizers.  Must be after
   // updateStableTables() and stableUnlock() (see #4221).
   RELEASE_SM_LOCK;
-  scheduleFinalizers(cap, old_weak_ptr_list);
+  scheduleFinalizers(cap, dead_weak_ptr_list);
   ACQUIRE_SM_LOCK;
 
   // check sanity after GC
index f8ccaad..5313eec 100644 (file)
 typedef enum { WeakPtrs, WeakThreads, WeakDone } WeakStage;
 static WeakStage weak_stage;
 
-/* Weak pointers
- */
-StgWeak *old_weak_ptr_list; // also pending finaliser list
-StgWeak *weak_ptr_list_tail;
+// List of weak pointers whose key is dead
+StgWeak *dead_weak_ptr_list;
 
 // List of threads found to be unreachable
 StgTSO *resurrected_threads;
 
+static void collectDeadWeakPtrs (generation *gen);
+static rtsBool tidyWeakList (generation *gen);
 static void resurrectUnreachableThreads (generation *gen);
 static rtsBool tidyThreadList (generation *gen);
 
 void
 initWeakForGC(void)
 {
-    old_weak_ptr_list = weak_ptr_list;
-    weak_ptr_list = NULL;
-    weak_ptr_list_tail = NULL;
+    nat g;
+
+    for (g = 0; g <= N; g++) {
+        generation *gen = &generations[g];
+        gen->old_weak_ptr_list = gen->weak_ptr_list;
+        gen->weak_ptr_list = NULL;
+    }
+
     weak_stage = WeakPtrs;
+    dead_weak_ptr_list = NULL;
     resurrected_threads = END_TSO_QUEUE;
 }
 
 rtsBool 
 traverseWeakPtrList(void)
 {
-  StgWeak *w, **last_w, *next_w;
-  StgClosure *new;
   rtsBool flag = rtsFalse;
-  const StgInfoTable *info;
 
   switch (weak_stage) {
 
@@ -110,73 +113,23 @@ traverseWeakPtrList(void)
       return rtsFalse;
 
   case WeakPtrs:
-      /* doesn't matter where we evacuate values/finalizers to, since
-       * these pointers are treated as roots (iff the keys are alive).
-       */
-      gct->evac_gen_no = 0;
-      
-      last_w = &old_weak_ptr_list;
-      for (w = old_weak_ptr_list; w != NULL; w = next_w) {
-         
-         /* There might be a DEAD_WEAK on the list if finalizeWeak# was
-          * called on a live weak pointer object.  Just remove it.
-          */
-         if (w->header.info == &stg_DEAD_WEAK_info) {
-             next_w = w->link;
-             *last_w = next_w;
-             continue;
-         }
-         
-          info = get_itbl((StgClosure *)w);
-         switch (info->type) {
-
-         case WEAK:
-             /* Now, check whether the key is reachable.
-              */
-             new = isAlive(w->key);
-             if (new != NULL) {
-                 w->key = new;
-                 // evacuate the value and finalizer 
-                 evacuate(&w->value);
-                 evacuate(&w->finalizer);
-                 // remove this weak ptr from the old_weak_ptr list 
-                 *last_w = w->link;
-                  next_w  = w->link;
-
-                  // and put it on the new weak ptr list.
-                  if (weak_ptr_list == NULL) {
-                      weak_ptr_list = w;
-                  } else {
-                      weak_ptr_list_tail->link = w;
-                  }
-                  weak_ptr_list_tail = w;
-                  w->link = NULL;
-                  flag = rtsTrue;
-
-                 debugTrace(DEBUG_weak, 
-                            "weak pointer still alive at %p -> %p",
-                            w, w->key);
-                 continue;
-             }
-             else {
-                 last_w = &(w->link);
-                 next_w = w->link;
-                 continue;
-             }
-
-         default:
-             barf("traverseWeakPtrList: not WEAK");
-         }
+  {
+      nat g;
+
+      for (g = 0; g <= N; g++) {
+          if (tidyWeakList(&generations[g])) {
+              flag = rtsTrue;
+          }
       }
       
       /* If we didn't make any changes, then we can go round and kill all
-       * the dead weak pointers.  The old_weak_ptr list is used as a list
+       * the dead weak pointers.  The dead_weak_ptr list is used as a list
        * of pending finalizers later on.
        */
       if (flag == rtsFalse) {
-         for (w = old_weak_ptr_list; w; w = w->link) {
-             evacuate(&w->finalizer);
-         }
+          for (g = 0; g <= N; g++) {
+              collectDeadWeakPtrs(&generations[g]);
+          }
 
          // Next, move to the WeakThreads stage after fully
          // scavenging the finalizers we've just evacuated.
@@ -184,6 +137,7 @@ traverseWeakPtrList(void)
       }
 
       return rtsTrue;
+  }
 
   case WeakThreads:
       /* Now deal with the step->threads lists, which behave somewhat like
@@ -229,6 +183,17 @@ traverseWeakPtrList(void)
   }
 }
   
+static void collectDeadWeakPtrs (generation *gen)
+{
+    StgWeak *w, *next_w;
+    for (w = gen->old_weak_ptr_list; w != NULL; w = next_w) {
+        evacuate(&w->finalizer);
+        next_w = w->link;
+        w->link = dead_weak_ptr_list;
+        dead_weak_ptr_list = w;
+    }
+}
+
   static void resurrectUnreachableThreads (generation *gen)
 {
     StgTSO *t, *tmp, *next;
@@ -253,6 +218,80 @@ traverseWeakPtrList(void)
     }
 }
 
+static rtsBool tidyWeakList(generation *gen)
+{
+    StgWeak *w, **last_w, *next_w;
+    const StgInfoTable *info;
+    StgClosure *new;
+    rtsBool flag = rtsFalse;
+    last_w = &gen->old_weak_ptr_list;
+    for (w = gen->old_weak_ptr_list; w != NULL; w = next_w) {
+
+        /* There might be a DEAD_WEAK on the list if finalizeWeak# was
+         * called on a live weak pointer object.  Just remove it.
+         */
+        if (w->header.info == &stg_DEAD_WEAK_info) {
+            next_w = w->link;
+            *last_w = next_w;
+            continue;
+        }
+
+        info = get_itbl((StgClosure *)w);
+        switch (info->type) {
+
+        case WEAK:
+            /* Now, check whether the key is reachable.
+             */
+            new = isAlive(w->key);
+            if (new != NULL) {
+                generation *new_gen;
+
+                w->key = new;
+
+                // Find out which generation this weak ptr is in, and
+                // move it onto the weak ptr list of that generation.
+
+                new_gen = Bdescr((P_)w)->gen;
+                gct->evac_gen_no = new_gen->no;
+
+                // evacuate the value and finalizer
+                evacuate(&w->value);
+                evacuate(&w->finalizer);
+                // remove this weak ptr from the old_weak_ptr list
+                *last_w = w->link;
+                next_w  = w->link;
+
+                // and put it on the correct weak ptr list.
+                w->link = new_gen->weak_ptr_list;
+                new_gen->weak_ptr_list = w;
+                flag = rtsTrue;
+
+                if (gen->no != new_gen->no) {
+                    debugTrace(DEBUG_weak,
+                      "moving weak pointer %p from %d to %d",
+                      w, gen->no, new_gen->no);
+                }
+
+
+                debugTrace(DEBUG_weak,
+                           "weak pointer still alive at %p -> %p",
+                           w, w->key);
+                continue;
+            }
+            else {
+                last_w = &(w->link);
+                next_w = w->link;
+                continue;
+            }
+
+        default:
+            barf("tidyWeakList: not WEAK: %d, %p", info->type, w);
+        }
+    }
+
+    return flag;
+}
+
 static rtsBool tidyThreadList (generation *gen)
 {
     StgTSO *t, *tmp, *next, **prev;
@@ -303,38 +342,40 @@ static rtsBool tidyThreadList (generation *gen)
 /* -----------------------------------------------------------------------------
    Evacuate every weak pointer object on the weak_ptr_list, and update
    the link fields.
-
-   ToDo: with a lot of weak pointers, this will be expensive.  We
-   should have a per-GC weak pointer list, just like threads.
    -------------------------------------------------------------------------- */
 
 void
 markWeakPtrList ( void )
 {
-  StgWeak *w, **last_w;
+    nat g;
+
+    for (g = 0; g <= N; g++) {
+        generation *gen = &generations[g];
+        StgWeak *w, **last_w;
 
-  last_w = &weak_ptr_list;
-  for (w = weak_ptr_list; w; w = w->link) {
-      // w might be WEAK, EVACUATED, or DEAD_WEAK (actually CON_STATIC) here
+        last_w = &gen->weak_ptr_list;
+        for (w = gen->weak_ptr_list; w != NULL; w = w->link) {
+            // w might be WEAK, EVACUATED, or DEAD_WEAK (actually CON_STATIC) here
 
 #ifdef DEBUG
-      {   // careful to do this assertion only reading the info ptr
-          // once, because during parallel GC it might change under our feet.
-          const StgInfoTable *info;
-          info = w->header.info;
-          ASSERT(IS_FORWARDING_PTR(info)
-                 || info == &stg_DEAD_WEAK_info 
-                 || INFO_PTR_TO_STRUCT(info)->type == WEAK);
-      }
+            {   // careful to do this assertion only reading the info ptr
+                // once, because during parallel GC it might change under our feet.
+                const StgInfoTable *info;
+                info = w->header.info;
+                ASSERT(IS_FORWARDING_PTR(info)
+                       || info == &stg_DEAD_WEAK_info
+                       || INFO_PTR_TO_STRUCT(info)->type == WEAK);
+            }
 #endif
 
-      evacuate((StgClosure **)last_w);
-      w = *last_w;
-      if (w->header.info == &stg_DEAD_WEAK_info) {
-          last_w = &(w->link);
-      } else {
-          last_w = &(w->link);
-      }
-  }
+            evacuate((StgClosure **)last_w);
+            w = *last_w;
+            if (w->header.info == &stg_DEAD_WEAK_info) {
+                last_w = &(w->link);
+            } else {
+                last_w = &(w->link);
+            }
+        }
+    }
 }
 
index 5d5470b..a5337bc 100644 (file)
@@ -93,6 +93,8 @@ initGeneration (generation *gen, int g)
 #endif
     gen->threads = END_TSO_QUEUE;
     gen->old_threads = END_TSO_QUEUE;
+    gen->weak_ptr_list = NULL;
+    gen->old_weak_ptr_list = NULL;
 }
 
 void
@@ -169,7 +171,6 @@ initStorage (void)
 
   generations[0].max_blocks = 0;
 
-  weak_ptr_list = NULL;
   caf_list = END_OF_STATIC_LIST;
   revertible_caf_list = END_OF_STATIC_LIST;
    
index c731b9e..3173c27 100644 (file)
@@ -346,6 +346,7 @@ wanteds = concat
 
           ,structSize C  "generation"
           ,structField C "generation" "n_new_large_words"
+          ,structField C "generation" "weak_ptr_list"
 
           ,structSize Both   "CostCentreStack"
           ,structField C     "CostCentreStack" "ccsID"