X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=rts%2Fsm%2FMarkWeak.c;h=4f0a7a451ba30dd4383dbb3287361dc137a602a9;hb=6cf8982ac30be6836a0cdd8be5a6ac1a1a144213;hp=eca5c54b138fc4b7f658936182b6b9f36a0739c5;hpb=c357244371ca5eeaa0d56a6bd349e26a3b16b7dc;p=ghc-hetmet.git diff --git a/rts/sm/MarkWeak.c b/rts/sm/MarkWeak.c index eca5c54..4f0a7a4 100644 --- a/rts/sm/MarkWeak.c +++ b/rts/sm/MarkWeak.c @@ -1,6 +1,6 @@ /* ----------------------------------------------------------------------------- * - * (c) The GHC Team 1998-2006 + * (c) The GHC Team 1998-2008 * * Weak pointers and weak-like things in the GC * @@ -11,13 +11,16 @@ * * ---------------------------------------------------------------------------*/ +#include "PosixSource.h" #include "Rts.h" -#include "Storage.h" + #include "MarkWeak.h" #include "GC.h" +#include "GCThread.h" #include "Evac.h" #include "Trace.h" #include "Schedule.h" +#include "Weak.h" /* ----------------------------------------------------------------------------- Weak Pointers @@ -73,10 +76,11 @@ static WeakStage weak_stage; */ StgWeak *old_weak_ptr_list; // also pending finaliser list -/* List of all threads during GC - */ +// List of threads found to be unreachable StgTSO *resurrected_threads; -static StgTSO *old_all_threads; + +// List of blocked threads found to have pending throwTos +StgTSO *exception_threads; void initWeakForGC(void) @@ -84,13 +88,8 @@ initWeakForGC(void) old_weak_ptr_list = weak_ptr_list; weak_ptr_list = NULL; weak_stage = WeakPtrs; - - /* The all_threads list is like the weak_ptr_list. - * See traverseWeakPtrList() for the details. - */ - old_all_threads = all_threads; - all_threads = END_TSO_QUEUE; resurrected_threads = END_TSO_QUEUE; + exception_threads = END_TSO_QUEUE; } rtsBool @@ -99,6 +98,7 @@ traverseWeakPtrList(void) StgWeak *w, **last_w, *next_w; StgClosure *new; rtsBool flag = rtsFalse; + const StgInfoTable *info; switch (weak_stage) { @@ -123,12 +123,14 @@ traverseWeakPtrList(void) continue; } - switch (get_itbl(w)->type) { - - case EVACUATED: - next_w = (StgWeak *)((StgEvacuated *)w)->evacuee; + info = w->header.info; + if (IS_FORWARDING_PTR(info)) { + next_w = (StgWeak *)UN_FORWARDING_PTR(info); *last_w = next_w; continue; + } + + switch (INFO_PTR_TO_STRUCT(info)->type) { case WEAK: /* Now, check whether the key is reachable. @@ -184,53 +186,85 @@ traverseWeakPtrList(void) * the weak ptr list. If we discover any threads that are about to * become garbage, we wake them up and administer an exception. */ - { - StgTSO *t, *tmp, *next, **prev; + { + StgTSO *t, *tmp, *next, **prev; + nat g, s; + step *stp; - prev = &old_all_threads; - for (t = old_all_threads; t != END_TSO_QUEUE; t = next) { - - tmp = (StgTSO *)isAlive((StgClosure *)t); + // Traverse thread lists for generations we collected... + for (g = 0; g <= N; g++) { + for (s = 0; s < generations[g].n_steps; s++) { + stp = &generations[g].steps[s]; + + prev = &stp->old_threads; + + for (t = stp->old_threads; t != END_TSO_QUEUE; t = next) { - if (tmp != NULL) { - t = tmp; - } + tmp = (StgTSO *)isAlive((StgClosure *)t); - ASSERT(get_itbl(t)->type == TSO); - switch (t->what_next) { - case ThreadRelocated: - next = t->link; - *prev = next; - continue; - case ThreadKilled: - case ThreadComplete: - // finshed or died. The thread might still be alive, but we - // don't keep it on the all_threads list. Don't forget to - // stub out its global_link field. - next = t->global_link; - t->global_link = END_TSO_QUEUE; - *prev = next; - continue; - default: - ; - } - - if (tmp == NULL) { - // not alive (yet): leave this thread on the - // old_all_threads list. - prev = &(t->global_link); - next = t->global_link; - } - else { - // alive: move this thread onto the all_threads list. - next = t->global_link; - t->global_link = all_threads; - all_threads = t; - *prev = next; - } - } + if (tmp != NULL) { + t = tmp; + } + + ASSERT(get_itbl(t)->type == TSO); + if (t->what_next == ThreadRelocated) { + next = t->_link; + *prev = next; + continue; + } + + next = t->global_link; + + // This is a good place to check for blocked + // exceptions. It might be the case that a thread is + // blocked on delivering an exception to a thread that + // is also blocked - we try to ensure that this + // doesn't happen in throwTo(), but it's too hard (or + // impossible) to close all the race holes, so we + // accept that some might get through and deal with + // them here. A GC will always happen at some point, + // even if the system is otherwise deadlocked. + // + // If an unreachable thread has blocked + // exceptions, we really want to perform the + // blocked exceptions rather than throwing + // BlockedIndefinitely exceptions. This is the + // only place we can discover such threads. + // The target thread might even be + // ThreadFinished or ThreadKilled. Bugs here + // will only be seen when running on a + // multiprocessor. + if (t->blocked_exceptions != END_TSO_QUEUE) { + if (tmp == NULL) { + evacuate((StgClosure **)&t); + flag = rtsTrue; + } + t->global_link = exception_threads; + exception_threads = t; + *prev = next; + continue; + } + + if (tmp == NULL) { + // not alive (yet): leave this thread on the + // old_all_threads list. + prev = &(t->global_link); + } + else { + // alive + *prev = next; + + // move this thread onto the correct threads list. + step *new_step; + new_step = Bdescr((P_)t)->step; + t->global_link = new_step->threads; + new_step->threads = t; + } + } + } + } } - + /* If we evacuated any threads, we need to go back to the scavenger. */ if (flag) return rtsTrue; @@ -238,14 +272,34 @@ traverseWeakPtrList(void) /* And resurrect any threads which were about to become garbage. */ { + nat g, s; + step *stp; StgTSO *t, *tmp, *next; - for (t = old_all_threads; t != END_TSO_QUEUE; t = next) { - next = t->global_link; - tmp = t; - evacuate((StgClosure **)&tmp); - tmp->global_link = resurrected_threads; - resurrected_threads = tmp; - } + + for (g = 0; g <= N; g++) { + for (s = 0; s < generations[g].n_steps; s++) { + stp = &generations[g].steps[s]; + + for (t = stp->old_threads; t != END_TSO_QUEUE; t = next) { + next = t->global_link; + + // ThreadFinished and ThreadComplete: we have to keep + // these on the all_threads list until they + // become garbage, because they might get + // pending exceptions. + switch (t->what_next) { + case ThreadKilled: + case ThreadComplete: + continue; + default: + tmp = t; + evacuate((StgClosure **)&tmp); + tmp->global_link = resurrected_threads; + resurrected_threads = tmp; + } + } + } + } } /* Finally, we can update the blackhole_queue. This queue @@ -257,7 +311,7 @@ traverseWeakPtrList(void) */ { StgTSO **pt; - for (pt = &blackhole_queue; *pt != END_TSO_QUEUE; pt = &((*pt)->link)) { + for (pt = &blackhole_queue; *pt != END_TSO_QUEUE; pt = &((*pt)->_link)) { *pt = (StgTSO *)isAlive((StgClosure *)*pt); ASSERT(*pt != NULL); } @@ -290,7 +344,7 @@ traverseBlackholeQueue (void) flag = rtsFalse; prev = NULL; - for (t = blackhole_queue; t != END_TSO_QUEUE; prev=t, t = t->link) { + for (t = blackhole_queue; t != END_TSO_QUEUE; prev=t, t = t->_link) { // if the thread is not yet alive... if (! (tmp = (StgTSO *)isAlive((StgClosure*)t))) { // if the closure it is blocked on is either (a) a @@ -302,9 +356,14 @@ traverseBlackholeQueue (void) continue; } } - tmp = t; - evacuate((StgClosure **)&tmp); - if (prev) prev->link = t; + evacuate((StgClosure **)&t); + if (prev) { + prev->_link = t; + } else { + blackhole_queue = t; + } + // no write barrier when on the blackhole queue, + // because we traverse the whole queue on every GC. flag = rtsTrue; } } @@ -331,8 +390,9 @@ markWeakPtrList ( void ) 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 - ASSERT(w->header.info == &stg_DEAD_WEAK_info - || get_itbl(w)->type == WEAK || get_itbl(w)->type == EVACUATED); + ASSERT(IS_FORWARDING_PTR(w->header.info) + || w->header.info == &stg_DEAD_WEAK_info + || get_itbl(w)->type == WEAK); tmp = w; evacuate((StgClosure **)&tmp); *last_w = w;