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;
/* 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;
}
+ trace(TRACE_gc|DEBUG_gc, "GC: %dk to collect, using %d thread(s)",
+ n * (BLOCK_SIZE / 1024), n_gc_threads);
#else
n_gc_threads = 1;
#endif
// check stack sanity *before* GC (ToDo: check all threads)
IF_DEBUG(sanity, checkFreeListSanity());
+ // 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++) {
init_collected_gen(g,n_gc_threads);
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);
-
// this is the main thread
gct = gc_threads[0];
* 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)
break;
}
+ shutdown_gc_threads(n_gc_threads);
+
// Update pointers from the Task list
update_task_list();
/* -----------------------------------------------------------------------------
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_blocks;
+ 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;
}
/* -----------------------------------------------------------------------------
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;
// 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
#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
+}
+
/* ----------------------------------------------------------------------------
Initialise a generation that is to be collected
------------------------------------------------------------------------- */