diff options
-rw-r--r-- | includes/rts/storage/GC.h | 3 | ||||
-rw-r--r-- | includes/stg/MiscClosures.h | 1 | ||||
-rw-r--r-- | rts/PrimOps.cmm | 4 | ||||
-rw-r--r-- | rts/RetainerProfile.c | 8 | ||||
-rw-r--r-- | rts/RtsStartup.c | 6 | ||||
-rw-r--r-- | rts/Weak.c | 2 | ||||
-rw-r--r-- | rts/Weak.h | 2 | ||||
-rw-r--r-- | rts/sm/Compact.c | 10 | ||||
-rw-r--r-- | rts/sm/GC.c | 2 | ||||
-rw-r--r-- | rts/sm/MarkWeak.c | 229 | ||||
-rw-r--r-- | rts/sm/Storage.c | 3 | ||||
-rw-r--r-- | utils/deriveConstants/DeriveConstants.hs | 1 |
12 files changed, 161 insertions, 110 deletions
diff --git a/includes/rts/storage/GC.h b/includes/rts/storage/GC.h index 6ddd301c41..fb5e21e832 100644 --- a/includes/rts/storage/GC.h +++ b/includes/rts/storage/GC.h @@ -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; diff --git a/includes/stg/MiscClosures.h b/includes/stg/MiscClosures.h index db0a32eb67..ca5280f188 100644 --- a/includes/stg/MiscClosures.h +++ b/includes/stg/MiscClosures.h @@ -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 diff --git a/rts/PrimOps.cmm b/rts/PrimOps.cmm index b44dfe7f5c..c12c6d8638 100644 --- a/rts/PrimOps.cmm +++ b/rts/PrimOps.cmm @@ -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)); diff --git a/rts/RetainerProfile.c b/rts/RetainerProfile.c index 4e7ed3e222..10a947dda8 100644 --- a/rts/RetainerProfile.c +++ b/rts/RetainerProfile.c @@ -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); diff --git a/rts/RtsStartup.c b/rts/RtsStartup.c index d8c2058526..39c5ef1f94 100644 --- a/rts/RtsStartup.c +++ b/rts/RtsStartup.c @@ -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) { diff --git a/rts/Weak.c b/rts/Weak.c index e7a1257562..98ac7603b7 100644 --- a/rts/Weak.c +++ b/rts/Weak.c @@ -16,8 +16,6 @@ #include "Prelude.h" #include "Trace.h" -StgWeak *weak_ptr_list; - void runCFinalizers(StgCFinalizerList *list) { diff --git a/rts/Weak.h b/rts/Weak.h index 7892277b11..fbdf18a861 100644 --- a/rts/Weak.h +++ b/rts/Weak.h @@ -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); diff --git a/rts/sm/Compact.c b/rts/sm/Compact.c index ffa355ae9c..91ad8abb5a 100644 --- a/rts/sm/Compact.c +++ b/rts/sm/Compact.c @@ -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 diff --git a/rts/sm/GC.c b/rts/sm/GC.c index ddb538472f..28593f5c71 100644 --- a/rts/sm/GC.c +++ b/rts/sm/GC.c @@ -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 diff --git a/rts/sm/MarkWeak.c b/rts/sm/MarkWeak.c index f8ccaad7ea..5313eecb9b 100644 --- a/rts/sm/MarkWeak.c +++ b/rts/sm/MarkWeak.c @@ -75,34 +75,37 @@ 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); + } + } + } } diff --git a/rts/sm/Storage.c b/rts/sm/Storage.c index 5d5470b025..a5337bc5b2 100644 --- a/rts/sm/Storage.c +++ b/rts/sm/Storage.c @@ -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; diff --git a/utils/deriveConstants/DeriveConstants.hs b/utils/deriveConstants/DeriveConstants.hs index c731b9ea72..3173c27cec 100644 --- a/utils/deriveConstants/DeriveConstants.hs +++ b/utils/deriveConstants/DeriveConstants.hs @@ -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" |