/* -----------------------------------------------------------------------------
*
- * (c) The GHC Team 1998-2006
+ * (c) The GHC Team 1998-2008
*
* Generational garbage collector
*
#include "Papi.h"
#include "GC.h"
+#include "GCThread.h"
#include "Compact.h"
#include "Evac.h"
#include "Scav.h"
Static function declarations
-------------------------------------------------------------------------- */
-static void mark_root (StgClosure **root);
+static void mark_root (void *user, StgClosure **root);
static void zero_static_object_list (StgClosure* first_static);
static nat initialise_N (rtsBool force_major_gc);
static void alloc_gc_threads (void);
{
bdescr *bd;
step *stp;
- lnat live, allocated, max_copied, avg_copied;
+ lnat live, allocated, max_copied, avg_copied, slop;
lnat oldgen_saved_blocks = 0;
gc_thread *saved_gct;
nat g, s, t, n;
}
#endif
+ ASSERT(sizeof(step_workspace) == 16 * sizeof(StgWord));
+ // otherwise adjust the padding in step_workspace.
+
// tell the stats department that we've started a GC
stat_startGC();
// follow roots from the CAF list (used by GHCi)
gct->evac_step = 0;
- markCAFs(mark_root);
+ markCAFs(mark_root, gct);
// follow all the roots that the application knows about.
gct->evac_step = 0;
- GetRoots(mark_root);
+ markSomeCapabilities(mark_root, gct, gct->thread_index, n_gc_threads);
#if defined(RTS_USER_SIGNALS)
// mark the signal handlers (signals should be already blocked)
- markSignalHandlers(mark_root);
+ markSignalHandlers(mark_root, gct);
#endif
// Mark the weak pointer list, and prepare to detect dead weak pointers.
initWeakForGC();
// Mark the stable pointer table.
- markStablePtrTable(mark_root);
+ markStablePtrTable(mark_root, gct);
/* -------------------------------------------------------------------------
* Repeatedly scavenge all the areas we know about until there's no
if (major_gc && oldest_gen->steps[0].is_compacted) {
// save number of blocks for stats
oldgen_saved_blocks = oldest_gen->steps[0].n_old_blocks;
- compact();
+ compact(gct->scavenged_static_objects);
}
IF_DEBUG(sanity, checkGlobalTSOList(rtsFalse));
{
gc_thread *thr;
step_workspace *ws;
- bdescr *prev;
+ bdescr *prev, *next;
for (t = 0; t < n_gc_threads; t++) {
thr = gc_threads[t];
// not step 0
- for (s = 1; s < total_steps; s++) {
+ if (RtsFlags.GcFlags.generations == 1) {
+ s = 0;
+ } else {
+ s = 1;
+ }
+ for (; s < total_steps; s++) {
ws = &thr->steps[s];
// Push the final block
ASSERT(gct->scan_bd == NULL);
ASSERT(countBlocks(ws->scavd_list) == ws->n_scavd_blocks);
- prev = ws->part_list;
- for (bd = ws->part_list; bd != NULL; bd = bd->link) {
+ prev = NULL;
+ for (bd = ws->scavd_list; bd != NULL; bd = bd->link) {
bd->flags &= ~BF_EVACUATED; // now from-space
ws->step->n_words += bd->free - bd->start;
prev = bd;
}
if (prev != NULL) {
- prev->link = ws->scavd_list;
- }
- for (bd = ws->scavd_list; bd != NULL; bd = bd->link) {
- bd->flags &= ~BF_EVACUATED; // now from-space
- ws->step->n_words += bd->free - bd->start;
- prev = bd;
+ prev->link = ws->step->blocks;
+ ws->step->blocks = ws->scavd_list;
+ }
+ ws->step->n_blocks += ws->n_scavd_blocks;
+
+ prev = NULL;
+ for (bd = ws->part_list; bd != NULL; bd = next) {
+ next = bd->link;
+ if (bd->free == bd->start) {
+ if (prev == NULL) {
+ ws->part_list = next;
+ } else {
+ prev->link = next;
+ }
+ freeGroup(bd);
+ ws->n_part_blocks--;
+ } else {
+ bd->flags &= ~BF_EVACUATED; // now from-space
+ ws->step->n_words += bd->free - bd->start;
+ prev = bd;
+ }
}
- prev->link = ws->step->blocks;
- if (ws->part_list != NULL) {
+ if (prev != NULL) {
+ prev->link = ws->step->blocks;
ws->step->blocks = ws->part_list;
- } else {
- ws->step->blocks = ws->scavd_list;
}
ws->step->n_blocks += ws->n_part_blocks;
- ws->step->n_blocks += ws->n_scavd_blocks;
+
ASSERT(countBlocks(ws->step->blocks) == ws->step->n_blocks);
ASSERT(countOccupied(ws->step->blocks) == ws->step->n_words);
}
}
}
- // Two-space collector: swap the semi-spaces around.
- // Currently: g0s0->old_blocks is the old nursery
- // g0s0->blocks is to-space from this GC
- // We want these the other way around.
- if (RtsFlags.GcFlags.generations == 1) {
- bdescr *nursery_blocks = g0s0->old_blocks;
- nat n_nursery_blocks = g0s0->n_old_blocks;
- g0s0->old_blocks = g0s0->blocks;
- g0s0->n_old_blocks = g0s0->n_blocks;
- g0s0->blocks = nursery_blocks;
- g0s0->n_blocks = n_nursery_blocks;
- }
-
/* run through all the generations/steps and tidy up
*/
copied = 0;
#endif
// ok, GC over: tell the stats department what happened.
- stat_endGC(allocated, live, copied, N, max_copied, avg_copied);
+ slop = calcLiveBlocks() * BLOCK_SIZE_W - live;
+ stat_endGC(allocated, live, copied, N, max_copied, avg_copied, slop);
#if defined(RTS_USER_SIGNALS)
if (RtsFlags.MiscFlags.install_signal_handlers) {
}
/* -----------------------------------------------------------------------------
- * Mark all nodes pointed to by sparks in the spark queues (for GC) Does an
- * implicit slide i.e. after marking all sparks are at the beginning of the
- * spark pool and the spark pool only contains sparkable closures
- * -------------------------------------------------------------------------- */
-
-#ifdef THREADED_RTS
-static void
-markSparkQueue (evac_fn evac, Capability *cap)
-{
- StgClosure **sparkp, **to_sparkp;
- nat n, pruned_sparks; // stats only
- StgSparkPool *pool;
-
- PAR_TICKY_MARK_SPARK_QUEUE_START();
-
- n = 0;
- pruned_sparks = 0;
-
- pool = &(cap->r.rSparks);
-
- ASSERT_SPARK_POOL_INVARIANTS(pool);
-
-#if defined(PARALLEL_HASKELL)
- // stats only
- n = 0;
- pruned_sparks = 0;
-#endif
-
- sparkp = pool->hd;
- to_sparkp = pool->hd;
- while (sparkp != pool->tl) {
- ASSERT(*sparkp!=NULL);
- ASSERT(LOOKS_LIKE_CLOSURE_PTR(((StgClosure *)*sparkp)));
- // ToDo?: statistics gathering here (also for GUM!)
- if (closure_SHOULD_SPARK(*sparkp)) {
- evac(sparkp);
- *to_sparkp++ = *sparkp;
- if (to_sparkp == pool->lim) {
- to_sparkp = pool->base;
- }
- n++;
- } else {
- pruned_sparks++;
- }
- sparkp++;
- if (sparkp == pool->lim) {
- sparkp = pool->base;
- }
- }
- pool->tl = to_sparkp;
-
- PAR_TICKY_MARK_SPARK_QUEUE_END(n);
-
-#if defined(PARALLEL_HASKELL)
- debugTrace(DEBUG_sched,
- "marked %d sparks and pruned %d sparks on [%x]",
- n, pruned_sparks, mytid);
-#else
- debugTrace(DEBUG_sched,
- "marked %d sparks and pruned %d sparks",
- n, pruned_sparks);
-#endif
-
- debugTrace(DEBUG_sched,
- "new spark queue len=%d; (hd=%p; tl=%p)\n",
- sparkPoolSize(pool), pool->hd, pool->tl);
-}
-#endif
-
-/* ---------------------------------------------------------------------------
- Where are the roots that we know about?
-
- - all the threads on the runnable queue
- - all the threads on the blocked queue
- - all the threads on the sleeping queue
- - all the thread currently executing a _ccall_GC
- - all the "main threads"
-
- ------------------------------------------------------------------------ */
-
-void
-GetRoots( evac_fn evac )
-{
- nat i;
- Capability *cap;
- Task *task;
-
- // Each GC thread is responsible for following roots from the
- // Capability of the same number. There will usually be the same
- // or fewer Capabilities as GC threads, but just in case there
- // are more, we mark every Capability whose number is the GC
- // thread's index plus a multiple of the number of GC threads.
- for (i = gct->thread_index; i < n_capabilities; i += n_gc_threads) {
- cap = &capabilities[i];
- evac((StgClosure **)(void *)&cap->run_queue_hd);
- evac((StgClosure **)(void *)&cap->run_queue_tl);
-#if defined(THREADED_RTS)
- evac((StgClosure **)(void *)&cap->wakeup_queue_hd);
- evac((StgClosure **)(void *)&cap->wakeup_queue_tl);
-#endif
- for (task = cap->suspended_ccalling_tasks; task != NULL;
- task=task->next) {
- debugTrace(DEBUG_sched,
- "evac'ing suspended TSO %lu", (unsigned long)task->suspended_tso->id);
- evac((StgClosure **)(void *)&task->suspended_tso);
- }
-
-#if defined(THREADED_RTS)
- markSparkQueue(evac,cap);
-#endif
- }
-
-#if !defined(THREADED_RTS)
- evac((StgClosure **)(void *)&blocked_queue_hd);
- evac((StgClosure **)(void *)&blocked_queue_tl);
- evac((StgClosure **)(void *)&sleeping_queue);
-#endif
-}
-
-/* -----------------------------------------------------------------------------
- isAlive determines whether the given closure is still alive (after
- a garbage collection) or not. It returns the new address of the
- closure if it is alive, or NULL otherwise.
-
- NOTE: Use it before compaction only!
- It untags and (if needed) retags pointers to closures.
- -------------------------------------------------------------------------- */
-
-
-StgClosure *
-isAlive(StgClosure *p)
-{
- const StgInfoTable *info;
- bdescr *bd;
- StgWord tag;
- StgClosure *q;
-
- while (1) {
- /* The tag and the pointer are split, to be merged later when needed. */
- tag = GET_CLOSURE_TAG(p);
- q = UNTAG_CLOSURE(p);
-
- ASSERT(LOOKS_LIKE_CLOSURE_PTR(q));
- info = get_itbl(q);
-
- // ignore static closures
- //
- // ToDo: for static closures, check the static link field.
- // Problem here is that we sometimes don't set the link field, eg.
- // for static closures with an empty SRT or CONSTR_STATIC_NOCAFs.
- //
- if (!HEAP_ALLOCED(q)) {
- return p;
- }
-
- // ignore closures in generations that we're not collecting.
- bd = Bdescr((P_)q);
- if (bd->gen_no > N) {
- return p;
- }
-
- // if it's a pointer into to-space, then we're done
- if (bd->flags & BF_EVACUATED) {
- return p;
- }
-
- // large objects use the evacuated flag
- if (bd->flags & BF_LARGE) {
- return NULL;
- }
-
- // check the mark bit for compacted steps
- if ((bd->flags & BF_COMPACTED) && is_marked((P_)q,bd)) {
- return p;
- }
-
- switch (info->type) {
-
- case IND:
- case IND_STATIC:
- case IND_PERM:
- case IND_OLDGEN: // rely on compatible layout with StgInd
- case IND_OLDGEN_PERM:
- // follow indirections
- p = ((StgInd *)q)->indirectee;
- continue;
-
- case EVACUATED:
- // alive!
- return ((StgEvacuated *)q)->evacuee;
-
- case TSO:
- if (((StgTSO *)q)->what_next == ThreadRelocated) {
- p = (StgClosure *)((StgTSO *)q)->link;
- continue;
- }
- return NULL;
-
- default:
- // dead.
- return NULL;
- }
- }
-}
-
-/* -----------------------------------------------------------------------------
Figure out which generation to collect, initialise N and major_gc.
Also returns the total number of blocks in generations that will be
// Every thread evacuates some roots.
gct->evac_step = 0;
- GetRoots(mark_root);
+ markSomeCapabilities(mark_root, gct, gct->thread_index, n_gc_threads);
loop:
scavenge_loop();
}
/* -----------------------------------------------------------------------------
- Function we pass to GetRoots to evacuate roots.
+ Function we pass to evacuate roots.
-------------------------------------------------------------------------- */
static void
-mark_root(StgClosure **root)
+mark_root(void *user, StgClosure **root)
{
- evacuate(root);
+ // we stole a register for gct, but this function is called from
+ // *outside* the GC where the register variable is not in effect,
+ // so we need to save and restore it here. NB. only call
+ // mark_root() from the main GC thread, otherwise gct will be
+ // incorrect.
+ gc_thread *saved_gct;
+ saved_gct = gct;
+ gct = user;
+
+ evacuate(root);
+
+ gct = saved_gct;
}
/* -----------------------------------------------------------------------------
}
}
-/* -----------------------------------------------------------------------------
- Reverting CAFs
- -------------------------------------------------------------------------- */
-
-void
-revertCAFs( void )
-{
- StgIndStatic *c;
-
- for (c = (StgIndStatic *)revertible_caf_list; c != NULL;
- c = (StgIndStatic *)c->static_link)
- {
- SET_INFO(c, c->saved_info);
- c->saved_info = NULL;
- // could, but not necessary: c->static_link = NULL;
- }
- revertible_caf_list = NULL;
-}
-
-void
-markCAFs( evac_fn evac )
-{
- StgIndStatic *c;
-
- for (c = (StgIndStatic *)caf_list; c != NULL;
- c = (StgIndStatic *)c->static_link)
- {
- evac(&c->indirectee);
- }
- for (c = (StgIndStatic *)revertible_caf_list; c != NULL;
- c = (StgIndStatic *)c->static_link)
- {
- evac(&c->indirectee);
- }
-}
-
/* ----------------------------------------------------------------------------
Update the pointers from the task list
* performance we get from 3L bytes, reducing to the same
* performance at 2L bytes.
*/
- blocks = g0s0->n_old_blocks;
+ blocks = g0s0->n_blocks;
if ( RtsFlags.GcFlags.maxHeapSize != 0 &&
blocks * RtsFlags.GcFlags.oldGenFactor * 2 >