diff options
author | Takano Akio <aljee@hyper.cx> | 2013-03-11 18:51:05 +0900 |
---|---|---|
committer | Ian Lynagh <ian@well-typed.com> | 2013-06-15 16:41:02 +0100 |
commit | fe652a8b56c864167ecf1fac899bb3d99363dfcf (patch) | |
tree | 888acad55c4cc1a9eee790f0c5404feed34c5fec /rts/sm/MarkWeak.c | |
parent | 6770663f764db76dbb7138ccb3aea0527d194151 (diff) | |
download | haskell-fe652a8b56c864167ecf1fac899bb3d99363dfcf.tar.gz |
Maintain per-generation lists of weak pointers (#7847)
Diffstat (limited to 'rts/sm/MarkWeak.c')
-rw-r--r-- | rts/sm/MarkWeak.c | 229 |
1 files changed, 135 insertions, 94 deletions
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); + } + } + } } |