*
* ---------------------------------------------------------------------------*/
-#include "PosixSource.h"
+// #include "PosixSource.h"
#include "Rts.h"
#include "RtsFlags.h"
#include "RtsUtils.h"
#include "Sparks.h"
#include <string.h> // for memset()
+#include <unistd.h>
/* -----------------------------------------------------------------------------
Global variables
* We build up a static object list while collecting generations 0..N,
* which is then appended to the static object list of generation N+1.
*/
-StgClosure* static_objects; // live static objects
-StgClosure* scavenged_static_objects; // static objects scavenged so far
-#ifdef THREADED_RTS
-SpinLock static_objects_sync;
-#endif
/* N is the oldest generation being collected, where the generations
* are numbered starting at 0. A major GC (indicated by the major_gc
/* Thread-local data for each GC thread
*/
-gc_thread *gc_threads = NULL;
+gc_thread **gc_threads = NULL;
// gc_thread *gct = NULL; // this thread's gct TODO: make thread-local
// Number of threads running in *this* GC. Affects how many
static void mark_root (StgClosure **root);
static void zero_static_object_list (StgClosure* first_static);
-static void initialise_N (rtsBool force_major_gc);
+static nat initialise_N (rtsBool force_major_gc);
static void alloc_gc_threads (void);
static void init_collected_gen (nat g, nat threads);
static void init_uncollected_gen (nat g, nat threads);
static nat inc_running (void);
static nat dec_running (void);
static void wakeup_gc_threads (nat n_threads);
+static void shutdown_gc_threads (nat n_threads);
#if 0 && defined(DEBUG)
static void gcCAFs (void);
lnat live, allocated;
lnat oldgen_saved_blocks = 0;
gc_thread *saved_gct;
- nat g, s, t;
+ nat g, s, t, n;
// necessary if we stole a callee-saves register for gct:
saved_gct = gct;
ACQUIRE_SM_LOCK;
- debugTrace(DEBUG_gc, "starting GC");
-
#if defined(RTS_USER_SIGNALS)
if (RtsFlags.MiscFlags.install_signal_handlers) {
// block signals
}
#endif
- // tell the STM to discard any cached closures it's hoping to re-use
- stmPreGCHook();
-
// tell the stats department that we've started a GC
stat_startGC();
-#ifdef DEBUG
- // check for memory leaks if DEBUG is on
- memInventory();
-#endif
+ // tell the STM to discard any cached closures it's hoping to re-use
+ stmPreGCHook();
#ifdef DEBUG
mutlist_MUTVARS = 0;
/* Figure out which generation to collect
*/
- initialise_N(force_major_gc);
+ n = initialise_N(force_major_gc);
/* Allocate + initialise the gc_thread structures.
*/
* We don't try to parallelise minor GC.
*/
#if defined(THREADED_RTS)
- if (N == 0) {
+ if (n < (4*1024*1024 / BLOCK_SIZE)) {
n_gc_threads = 1;
} else {
n_gc_threads = RtsFlags.ParFlags.gcThreads;
#else
n_gc_threads = 1;
#endif
+ trace(TRACE_gc|DEBUG_gc, "GC (gen %d): %dKB to collect, using %d thread(s)",
+ N, n * (BLOCK_SIZE / 1024), n_gc_threads);
#ifdef RTS_GTK_FRONTPANEL
if (RtsFlags.GcFlags.frontpanel) {
}
#endif
+#ifdef DEBUG
+ // check for memory leaks if DEBUG is on
+ memInventory(traceClass(DEBUG_gc));
+#endif
+
// check stack sanity *before* GC (ToDo: check all threads)
IF_DEBUG(sanity, checkFreeListSanity());
- /* Initialise the static object lists
- */
- static_objects = END_OF_STATIC_LIST;
- scavenged_static_objects = END_OF_STATIC_LIST;
-
-#ifdef THREADED_RTS
- initSpinLock(&static_objects_sync);
- initSpinLock(&recordMutableGen_sync);
- initSpinLock(&gc_alloc_block_sync);
-#endif
+ // Initialise all our gc_thread structures
+ for (t = 0; t < n_gc_threads; t++) {
+ init_gc_thread(gc_threads[t]);
+ }
// Initialise all the generations/steps that we're collecting.
for (g = 0; g <= N; g++) {
mark_stack_bdescr = NULL;
}
- // Initialise all our gc_thread structures
- for (t = 0; t < n_gc_threads; t++) {
- init_gc_thread(&gc_threads[t]);
- }
-
- // the main thread is running: this prevents any other threads from
- // exiting prematurely, so we can start them now.
- inc_running();
- wakeup_gc_threads(n_gc_threads);
-
- // Initialise stats
- copied = 0;
-
// this is the main thread
- gct = &gc_threads[0];
+ gct = gc_threads[0];
/* -----------------------------------------------------------------------
* follow all the roots that we know about:
* Also do them in reverse generation order, for the usual reason:
* namely to reduce the likelihood of spurious old->new pointers.
*/
- {
- for (g = RtsFlags.GcFlags.generations-1; g > N; g--) {
+ for (g = RtsFlags.GcFlags.generations-1; g > N; g--) {
generations[g].saved_mut_list = generations[g].mut_list;
generations[g].mut_list = allocBlock();
- // mut_list always has at least one block.
- }
- for (g = RtsFlags.GcFlags.generations-1; g > N; g--) {
+ // mut_list always has at least one block.
+ }
+
+ // the main thread is running: this prevents any other threads from
+ // exiting prematurely, so we can start them now.
+ // NB. do this after the mutable lists have been saved above, otherwise
+ // the other GC threads will be writing into the old mutable lists.
+ inc_running();
+ wakeup_gc_threads(n_gc_threads);
+
+ for (g = RtsFlags.GcFlags.generations-1; g > N; g--) {
scavenge_mutable_list(&generations[g]);
- }
}
// follow roots from the CAF list (used by GHCi)
gct->evac_step = 0;
GetRoots(mark_root);
+#if defined(RTS_USER_SIGNALS)
+ // mark the signal handlers (signals should be already blocked)
+ markSignalHandlers(mark_root);
+#endif
+
// Mark the weak pointer list, and prepare to detect dead weak pointers.
markWeakPtrList();
initWeakForGC();
break;
}
+ shutdown_gc_threads(n_gc_threads);
+
// Update pointers from the Task list
update_task_list();
bdescr *prev;
for (t = 0; t < n_gc_threads; t++) {
- thr = &gc_threads[t];
-
- for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
- for (s = 0; s < generations[g].n_steps; s++) {
- ws = &thr->steps[g][s];
- if (g==0 && s==0) continue;
-
- // Not true?
- // ASSERT( ws->scan_bd == ws->todo_bd );
- ASSERT( ws->scan_bd ? ws->scan == ws->scan_bd->free : 1 );
-
- // Push the final block
- if (ws->scan_bd) { push_scan_block(ws->scan_bd, ws); }
-
- ASSERT(countBlocks(ws->scavd_list) == ws->n_scavd_blocks);
-
- prev = ws->scavd_list;
- for (bd = ws->scavd_list; bd != NULL; bd = bd->link) {
- bd->flags &= ~BF_EVACUATED; // now from-space
- prev = bd;
- }
- prev->link = ws->stp->blocks;
- ws->stp->blocks = ws->scavd_list;
- ws->stp->n_blocks += ws->n_scavd_blocks;
- ASSERT(countBlocks(ws->stp->blocks) == ws->stp->n_blocks);
- }
+ thr = gc_threads[t];
+
+ // not step 0
+ for (s = 1; s < total_steps; s++) {
+ ws = &thr->steps[s];
+ // Not true?
+ // ASSERT( ws->scan_bd == ws->todo_bd );
+ ASSERT( ws->scan_bd ? ws->scan_bd->u.scan == ws->scan_bd->free : 1 );
+
+ // Push the final block
+ if (ws->scan_bd) { push_scanned_block(ws->scan_bd, ws); }
+
+ ASSERT(countBlocks(ws->scavd_list) == ws->n_scavd_blocks);
+
+ prev = ws->part_list;
+ for (bd = ws->part_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;
+ if (ws->part_list != NULL) {
+ 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);
}
}
}
/* run through all the generations/steps and tidy up
*/
+ copied = 0;
+ {
+ nat i;
+ for (i=0; i < n_gc_threads; i++) {
+ if (n_gc_threads > 1) {
+ trace(TRACE_gc,"thread %d:", i);
+ trace(TRACE_gc," copied %ld", gc_threads[i]->copied * sizeof(W_));
+ trace(TRACE_gc," any_work %ld", gc_threads[i]->any_work);
+ trace(TRACE_gc," no_work %ld", gc_threads[i]->no_work);
+ trace(TRACE_gc," scav_find_work %ld", gc_threads[i]->scav_find_work);
+ }
+ copied += gc_threads[i]->copied;
+ }
+ }
+
for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
if (g <= N) {
// onto the front of the now-compacted existing blocks.
for (bd = stp->blocks; bd != NULL; bd = bd->link) {
bd->flags &= ~BF_EVACUATED; // now from-space
+ stp->n_words += bd->free - bd->start;
}
// tack the new blocks on the end of the existing blocks
if (stp->old_blocks != NULL) {
// add the new blocks to the block tally
stp->n_blocks += stp->n_old_blocks;
ASSERT(countBlocks(stp->blocks) == stp->n_blocks);
+ ASSERT(countOccupied(stp->blocks) == stp->n_words);
}
else // not copacted
{
// update the max size of older generations after a major GC
resize_generations();
- // Guess the amount of live data for stats.
- live = calcLive();
+ // Calculate the amount of live data for stats.
+ live = calcLiveWords();
// Free the small objects allocated via allocate(), since this will
// all have been copied into G0S1 now.
g0s0->blocks = NULL;
}
g0s0->n_blocks = 0;
+ g0s0->n_words = 0;
}
alloc_blocks = 0;
alloc_blocks_lim = RtsFlags.GcFlags.minAllocAreaSize;
#ifdef PROFILING
// resetStaticObjectForRetainerProfiling() must be called before
// zeroing below.
- resetStaticObjectForRetainerProfiling();
+ if (n_gc_threads > 1) {
+ barf("profiling is currently broken with multi-threaded GC");
+ // ToDo: fix the gct->scavenged_static_objects below
+ }
+ resetStaticObjectForRetainerProfiling(gct->scavenged_static_objects);
#endif
// zero the scavenged static object list
if (major_gc) {
- zero_static_object_list(scavenged_static_objects);
+ nat i;
+ for (i = 0; i < n_gc_threads; i++) {
+ zero_static_object_list(gc_threads[i]->scavenged_static_objects);
+ }
}
// Reset the nursery
IF_DEBUG(sanity, checkSanity());
// extra GC trace info
- IF_DEBUG(gc, statDescribeGens());
+ if (traceClass(TRACE_gc|DEBUG_gc)) statDescribeGens();
#ifdef DEBUG
// symbol-table based profiling
#ifdef DEBUG
// check for memory leaks if DEBUG is on
- memInventory();
+ memInventory(traceClass(DEBUG_gc));
#endif
#ifdef RTS_GTK_FRONTPANEL
gct = saved_gct;
}
+/* -----------------------------------------------------------------------------
+ * 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?
------------------------------------------------------------------------ */
-/* This has to be protected either by the scheduler monitor, or by the
- garbage collection monitor (probably the latter).
- KH @ 25/10/99
-*/
-
void
GetRoots( evac_fn evac )
{
Capability *cap;
Task *task;
- for (i = 0; i < n_capabilities; i++) {
+ // 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);
evac((StgClosure **)(void *)&task->suspended_tso);
}
+#if defined(THREADED_RTS)
+ markSparkQueue(evac,cap);
+#endif
}
#if !defined(THREADED_RTS)
evac((StgClosure **)(void *)&blocked_queue_tl);
evac((StgClosure **)(void *)&sleeping_queue);
#endif
-
- // evac((StgClosure **)&blackhole_queue);
-
-#if defined(THREADED_RTS)
- markSparkQueue(evac);
-#endif
-
-#if defined(RTS_USER_SIGNALS)
- // mark the signal handlers (signals should be already blocked)
- markSignalHandlers(evac);
-#endif
}
/* -----------------------------------------------------------------------------
/* -----------------------------------------------------------------------------
Figure out which generation to collect, initialise N and major_gc.
+
+ Also returns the total number of blocks in generations that will be
+ collected.
-------------------------------------------------------------------------- */
-static void
+static nat
initialise_N (rtsBool force_major_gc)
{
- nat g;
+ int g;
+ nat s, blocks, blocks_total;
+
+ blocks = 0;
+ blocks_total = 0;
if (force_major_gc) {
- N = RtsFlags.GcFlags.generations - 1;
- major_gc = rtsTrue;
+ N = RtsFlags.GcFlags.generations - 1;
} else {
- N = 0;
- for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
- if (generations[g].steps[0].n_blocks +
- generations[g].steps[0].n_large_blocks
- >= generations[g].max_blocks) {
- N = g;
- }
- }
- major_gc = (N == RtsFlags.GcFlags.generations-1);
+ N = 0;
+ }
+
+ for (g = RtsFlags.GcFlags.generations - 1; g >= 0; g--) {
+ blocks = 0;
+ for (s = 0; s < generations[g].n_steps; s++) {
+ blocks += generations[g].steps[s].n_words / BLOCK_SIZE_W;
+ blocks += generations[g].steps[s].n_large_blocks;
+ }
+ if (blocks >= generations[g].max_blocks) {
+ N = stg_max(N,g);
+ }
+ if ((nat)g <= N) {
+ blocks_total += blocks;
+ }
}
+
+ blocks_total += countNurseryBlocks();
+
+ major_gc = (N == RtsFlags.GcFlags.generations-1);
+ return blocks_total;
}
/* -----------------------------------------------------------------------------
Initialise the gc_thread structures.
-------------------------------------------------------------------------- */
-static void
-alloc_gc_thread (gc_thread *t, int n)
+static gc_thread *
+alloc_gc_thread (int n)
{
- nat g, s;
+ nat s;
step_workspace *ws;
+ gc_thread *t;
+
+ t = stgMallocBytes(sizeof(gc_thread) + total_steps * sizeof(step_workspace),
+ "alloc_gc_thread");
#ifdef THREADED_RTS
t->id = 0;
initCondition(&t->wake_cond);
initMutex(&t->wake_mutex);
- t->wakeup = rtsFalse;
+ t->wakeup = rtsTrue; // starts true, so we can wait for the
+ // thread to start up, see wakeup_gc_threads
t->exit = rtsFalse;
#endif
t->papi_events = -1;
#endif
- t->steps = stgMallocBytes(RtsFlags.GcFlags.generations *
- sizeof(step_workspace *),
- "initialise_gc_thread");
-
- for (g = 0; g < RtsFlags.GcFlags.generations; g++)
+ for (s = 0; s < total_steps; s++)
{
- t->steps[g] = stgMallocBytes(generations[g].n_steps *
- sizeof(step_workspace),
- "initialise_gc_thread/2");
-
- for (s = 0; s < generations[g].n_steps; s++)
- {
- ws = &t->steps[g][s];
- ws->stp = &generations[g].steps[s];
- ws->gct = t;
-
- ws->scan_bd = NULL;
- ws->scan = NULL;
-
- ws->todo_bd = NULL;
- ws->buffer_todo_bd = NULL;
-
- ws->scavd_list = NULL;
- ws->n_scavd_blocks = 0;
- }
+ ws = &t->steps[s];
+ ws->step = &all_steps[s];
+ ASSERT(s == ws->step->abs_no);
+ ws->gct = t;
+
+ ws->scan_bd = NULL;
+
+ ws->todo_bd = NULL;
+ ws->buffer_todo_bd = NULL;
+
+ ws->part_list = NULL;
+ ws->n_part_blocks = 0;
+
+ ws->scavd_list = NULL;
+ ws->n_scavd_blocks = 0;
}
+
+ return t;
}
#if defined(THREADED_RTS)
nat i;
gc_threads = stgMallocBytes (RtsFlags.ParFlags.gcThreads *
- sizeof(gc_thread),
+ sizeof(gc_thread*),
"alloc_gc_threads");
for (i = 0; i < RtsFlags.ParFlags.gcThreads; i++) {
- alloc_gc_thread(&gc_threads[i], i);
+ gc_threads[i] = alloc_gc_thread(i);
}
#else
- gc_threads = stgMallocBytes (sizeof(gc_thread),
+ gc_threads = stgMallocBytes (sizeof(gc_thread*),
"alloc_gc_threads");
- alloc_gc_thread(gc_threads, 0);
+ gc_threads[0] = alloc_gc_thread(0);
#endif
}
}
ACQUIRE_LOCK(&gc_running_mutex);
n_running = ++gc_running_threads;
RELEASE_LOCK(&gc_running_mutex);
+ ASSERT(n_running <= n_gc_threads);
return n_running;
}
{
nat n_running;
ACQUIRE_LOCK(&gc_running_mutex);
+ ASSERT(n_gc_threads != 0);
n_running = --gc_running_threads;
RELEASE_LOCK(&gc_running_mutex);
return n_running;
// GarbageCollect(), or this is a worker thread and the main
// thread bumped gc_running_threads before waking us up.
+ // Every thread evacuates some roots.
+ gct->evac_step = 0;
+ GetRoots(mark_root);
+
loop:
scavenge_loop();
// scavenge_loop() only exits when there's no work to do
gct->thread_index, r);
while (gc_running_threads != 0) {
+ usleep(1);
if (any_work()) {
inc_running();
goto loop;
// Wait until we're told to wake up
ACQUIRE_LOCK(&gct->wake_mutex);
+ gct->wakeup = rtsFalse;
while (!gct->wakeup) {
debugTrace(DEBUG_gc, "GC thread %d standing by...",
gct->thread_index);
waitCondition(&gct->wake_cond, &gct->wake_mutex);
}
RELEASE_LOCK(&gct->wake_mutex);
- gct->wakeup = rtsFalse;
if (gct->exit) break;
#ifdef USE_PAPI
// Start from 1: the main thread is 0
for (i = 1; i < RtsFlags.ParFlags.gcThreads; i++) {
createOSThread(&id, (OSThreadProc*)&gc_thread_entry,
- &gc_threads[i]);
+ gc_threads[i]);
}
done = rtsTrue;
}
nat i;
for (i=1; i < n_threads; i++) {
inc_running();
- ACQUIRE_LOCK(&gc_threads[i].wake_mutex);
- gc_threads[i].wakeup = rtsTrue;
- signalCondition(&gc_threads[i].wake_cond);
- RELEASE_LOCK(&gc_threads[i].wake_mutex);
+ debugTrace(DEBUG_gc, "waking up gc thread %d", i);
+ do {
+ ACQUIRE_LOCK(&gc_threads[i]->wake_mutex);
+ if (gc_threads[i]->wakeup) {
+ RELEASE_LOCK(&gc_threads[i]->wake_mutex);
+ continue;
+ } else {
+ break;
+ }
+ } while (1);
+ gc_threads[i]->wakeup = rtsTrue;
+ signalCondition(&gc_threads[i]->wake_cond);
+ RELEASE_LOCK(&gc_threads[i]->wake_mutex);
+ }
+#endif
+}
+
+// After GC is complete, we must wait for all GC threads to enter the
+// standby state, otherwise they may still be executing inside
+// any_work(), and may even remain awake until the next GC starts.
+static void
+shutdown_gc_threads (nat n_threads USED_IF_THREADS)
+{
+#if defined(THREADED_RTS)
+ nat i;
+ rtsBool wakeup;
+ for (i=1; i < n_threads; i++) {
+ do {
+ ACQUIRE_LOCK(&gc_threads[i]->wake_mutex);
+ wakeup = gc_threads[i]->wakeup;
+ // wakeup is false while the thread is waiting
+ RELEASE_LOCK(&gc_threads[i]->wake_mutex);
+ } while (wakeup);
}
#endif
}
stp->n_old_blocks = stp->n_blocks;
stp->blocks = NULL;
stp->n_blocks = 0;
+ stp->n_words = 0;
// we don't have any to-be-scavenged blocks yet
stp->todos = NULL;
+ stp->todos_last = NULL;
stp->n_todos = 0;
// initialise the large object queues.
// we don't copy objects into g0s0, unless -G0
if (g==0 && s==0 && RtsFlags.GcFlags.generations > 1) continue;
- ws = &gc_threads[t].steps[g][s];
+ ws = &gc_threads[t]->steps[g * RtsFlags.GcFlags.steps + s];
ws->scan_bd = NULL;
- ws->scan = NULL;
ws->todo_large_objects = NULL;
+ ws->part_list = NULL;
+ ws->n_part_blocks = 0;
+
// allocate the first to-space block; extra blocks will be
// chained on as necessary.
ws->todo_bd = NULL;
ws->buffer_todo_bd = NULL;
- gc_alloc_todo_block(ws);
+ alloc_todo_block(ws,0);
ws->scavd_list = NULL;
ws->n_scavd_blocks = 0;
for (t = 0; t < threads; t++) {
for (s = 0; s < generations[g].n_steps; s++) {
- ws = &gc_threads[t].steps[g][s];
- stp = ws->stp;
+ ws = &gc_threads[t]->steps[g * RtsFlags.GcFlags.steps + s];
+ stp = ws->step;
ws->buffer_todo_bd = NULL;
ws->todo_large_objects = NULL;
+ ws->part_list = NULL;
+ ws->n_part_blocks = 0;
+
ws->scavd_list = NULL;
ws->n_scavd_blocks = 0;
// If the block at the head of the list in this generation
// is less than 3/4 full, then use it as a todo block.
- if (isPartiallyFull(stp->blocks))
+ if (stp->blocks && isPartiallyFull(stp->blocks))
{
ws->todo_bd = stp->blocks;
+ ws->todo_free = ws->todo_bd->free;
+ ws->todo_lim = ws->todo_bd->start + BLOCK_SIZE_W;
stp->blocks = stp->blocks->link;
stp->n_blocks -= 1;
+ stp->n_words -= ws->todo_bd->free - ws->todo_bd->start;
ws->todo_bd->link = NULL;
// this block is also the scan block; we must scan
// from the current end point.
ws->scan_bd = ws->todo_bd;
- ws->scan = ws->scan_bd->free;
+ ws->scan_bd->u.scan = ws->scan_bd->free;
// subtract the contents of this block from the stats,
// because we'll count the whole block later.
else
{
ws->scan_bd = NULL;
- ws->scan = NULL;
ws->todo_bd = NULL;
- gc_alloc_todo_block(ws);
+ alloc_todo_block(ws,0);
}
}
}
static void
init_gc_thread (gc_thread *t)
{
+ t->static_objects = END_OF_STATIC_LIST;
+ t->scavenged_static_objects = END_OF_STATIC_LIST;
t->evac_step = 0;
t->failed_to_evac = rtsFalse;
t->eager_promotion = rtsTrue;
t->thunk_selector_depth = 0;
+ t->copied = 0;
+ t->any_work = 0;
+ t->no_work = 0;
+ t->scav_find_work = 0;
+
}
/* -----------------------------------------------------------------------------
nat gens = RtsFlags.GcFlags.generations;
// live in the oldest generations
- live = oldest_gen->steps[0].n_blocks +
+ live = (oldest_gen->steps[0].n_words + BLOCK_SIZE_W - 1) / BLOCK_SIZE_W+
oldest_gen->steps[0].n_large_blocks;
// default max size for all generations except zero