#include "Schedule.h"
#include "Weak.h"
#include "Storage.h"
+#include "Threads.h"
/* -----------------------------------------------------------------------------
Weak Pointers
// List of threads found to be unreachable
StgTSO *resurrected_threads;
-// List of blocked threads found to have pending throwTos
-StgTSO *exception_threads;
-
-static void resurrectUnreachableThreads (step *stp);
-static rtsBool tidyThreadList (step *stp);
+static void resurrectUnreachableThreads (generation *gen);
+static rtsBool tidyThreadList (generation *gen);
void
initWeakForGC(void)
weak_ptr_list = NULL;
weak_stage = WeakPtrs;
resurrected_threads = END_TSO_QUEUE;
- exception_threads = END_TSO_QUEUE;
}
rtsBool
/* doesn't matter where we evacuate values/finalizers to, since
* these pointers are treated as roots (iff the keys are alive).
*/
- gct->evac_step = 0;
+ gct->evac_gen = 0;
last_w = &old_weak_ptr_list;
for (w = old_weak_ptr_list; w != NULL; w = next_w) {
continue;
}
- 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) {
+ info = get_itbl(w);
+ switch (info->type) {
case WEAK:
/* Now, check whether the key is reachable.
* become garbage, we wake them up and administer an exception.
*/
{
- nat g, s, n;
+ nat g;
// Traverse thread lists for generations we collected...
- for (n = 0; n < n_capabilities; n++) {
- if (tidyThreadList(&nurseries[n])) {
- flag = rtsTrue;
- }
- }
+// ToDo when we have one gen per capability:
+// for (n = 0; n < n_capabilities; n++) {
+// if (tidyThreadList(&nurseries[n])) {
+// flag = rtsTrue;
+// }
+// }
for (g = 0; g <= N; g++) {
- for (s = 0; s < generations[g].n_steps; s++) {
- if (tidyThreadList(&generations[g].steps[s])) {
- flag = rtsTrue;
- }
+ if (tidyThreadList(&generations[g])) {
+ flag = rtsTrue;
}
}
/* And resurrect any threads which were about to become garbage.
*/
{
- nat g, s, n;
-
- for (n = 0; n < n_capabilities; n++) {
- resurrectUnreachableThreads(&nurseries[n]);
- }
+ nat g;
for (g = 0; g <= N; g++) {
- for (s = 0; s < generations[g].n_steps; s++) {
- resurrectUnreachableThreads(&generations[g].steps[s]);
- }
+ resurrectUnreachableThreads(&generations[g]);
}
}
}
}
- static void resurrectUnreachableThreads (step *stp)
+ static void resurrectUnreachableThreads (generation *gen)
{
StgTSO *t, *tmp, *next;
- for (t = stp->old_threads; t != END_TSO_QUEUE; t = next) {
+ for (t = gen->old_threads; t != END_TSO_QUEUE; t = next) {
next = t->global_link;
// ThreadFinished and ThreadComplete: we have to keep
}
}
-static rtsBool tidyThreadList (step *stp)
+static rtsBool tidyThreadList (generation *gen)
{
StgTSO *t, *tmp, *next, **prev;
rtsBool flag = rtsFalse;
- prev = &stp->old_threads;
+ prev = &gen->old_threads;
- for (t = stp->old_threads; t != END_TSO_QUEUE; t = next) {
+ for (t = gen->old_threads; t != END_TSO_QUEUE; t = next) {
tmp = (StgTSO *)isAlive((StgClosure *)t);
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 the thread is not masking exceptions but there are
+ // pending exceptions on its queue, then something has gone
+ // wrong:
+ ASSERT(t->blocked_exceptions == END_BLOCKED_EXCEPTIONS_QUEUE
+ || (t->flags & TSO_BLOCKEX));
if (tmp == NULL) {
// not alive (yet): leave this thread on the
*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;
+ generation *new_gen;
+ new_gen = Bdescr((P_)t)->gen;
+ t->global_link = new_gen->threads;
+ new_gen->threads = t;
}
}
}
/* -----------------------------------------------------------------------------
- After GC, the live weak pointer list may have forwarding pointers
- on it, because a weak pointer object was evacuated after being
- moved to the live weak pointer list. We remove those forwarding
- pointers here.
-
- Also, we don't consider weak pointer objects to be reachable, but
- we must nevertheless consider them to be "live" and retain them.
- Therefore any weak pointer objects which haven't as yet been
- evacuated need to be evacuated now.
+ 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, *tmp;
+ StgWeak *w, **last_w;
last_w = &weak_ptr_list;
for (w = weak_ptr_list; w; w = w->link) {
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;
- last_w = &(w->link);
+ evacuate((StgClosure **)last_w);
+ w = *last_w;
+ if (w->header.info == &stg_DEAD_WEAK_info) {
+ last_w = &(((StgDeadWeak*)w)->link);
+ } else {
+ last_w = &(w->link);
+ }
}
}