static void init_collected_gen (nat g, nat threads);
static void init_uncollected_gen (nat g, nat threads);
static void init_gc_thread (gc_thread *t);
-static void update_task_list (void);
static void resize_generations (void);
static void resize_nursery (void);
static void start_gc_threads (void);
/* Approximate how much we allocated.
* Todo: only when generating stats?
*/
- allocated = calcAllocated();
+ allocated = calcAllocated(rtsFalse/* don't count the nursery yet */);
/* Figure out which generation to collect
*/
// The other threads are now stopped. We might recurse back to
// here, but from now on this is the only thread.
- // if any blackholes are alive, make the threads that wait on
- // them alive too.
- if (traverseBlackholeQueue()) {
- inc_running();
- continue;
- }
-
// must be last... invariant is that everything is fully
// scavenged at this point.
if (traverseWeakPtrList()) { // returns rtsTrue if evaced something
shutdown_gc_threads(n_gc_threads, gct->thread_index);
- // Update pointers from the Task list
- update_task_list();
-
// Now see which stable names are still alive.
gcStablePtrTable();
+#ifdef THREADED_RTS
+ if (n_gc_threads == 1) {
+ for (n = 0; n < n_capabilities; n++) {
+ pruneSparkQueue(&capabilities[n]);
+ }
+ } else {
+ pruneSparkQueue(&capabilities[gct->thread_index]);
+ }
+#endif
+
#ifdef PROFILING
// We call processHeapClosureForDead() on every closure destroyed during
// the current garbage collection, so we invoke LdvCensusForDead().
freeChain(gen->large_objects);
gen->large_objects = gen->scavenged_large_objects;
gen->n_large_blocks = gen->n_scavenged_large_blocks;
- gen->n_new_large_blocks = 0;
+ gen->n_new_large_words = 0;
ASSERT(countBlocks(gen->large_objects) == gen->n_large_blocks);
}
else // for generations > N
// 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.
- alloc_blocks_lim = RtsFlags.GcFlags.minAllocAreaSize;
-
// Start a new pinned_object_block
for (n = 0; n < n_capabilities; n++) {
capabilities[n].pinned_object_block = NULL;
}
}
+ // Reset the nursery: make the blocks empty
+ allocated += clearNurseries();
+
resize_nursery();
- // mark the garbage collected CAFs as dead
+ resetNurseries();
+
+ // mark the garbage collected CAFs as dead
#if 0 && defined(DEBUG) // doesn't work at the moment
if (major_gc) { gcCAFs(); }
#endif
}
}
- // Reset the nursery
- resetNurseries();
-
- // start any pending finalizers
- RELEASE_SM_LOCK;
- scheduleFinalizers(cap, old_weak_ptr_list);
- ACQUIRE_SM_LOCK;
-
- // send exceptions to any threads which were about to die
+ // send exceptions to any threads which were about to die
RELEASE_SM_LOCK;
resurrectThreads(resurrected_threads);
- performPendingThrowTos(exception_threads);
ACQUIRE_SM_LOCK;
// Update the stable pointer hash table.
updateStablePtrTable(major_gc);
+ // unlock the StablePtr table. Must be before scheduleFinalizers(),
+ // because a finalizer may call hs_free_fun_ptr() or
+ // hs_free_stable_ptr(), both of which access the StablePtr table.
+ stablePtrPostGC();
+
+ // Start any pending finalizers. Must be after
+ // updateStablePtrTable() and stablePtrPostGC() (see #4221).
+ RELEASE_SM_LOCK;
+ scheduleFinalizers(cap, old_weak_ptr_list);
+ ACQUIRE_SM_LOCK;
+
+ if (major_gc) {
+ nat need, got;
+ need = BLOCKS_TO_MBLOCKS(n_alloc_blocks);
+ got = mblocks_allocated;
+ /* If the amount of data remains constant, next major GC we'll
+ require (F+1)*need. We leave (F+2)*need in order to reduce
+ repeated deallocation and reallocation. */
+ need = (RtsFlags.GcFlags.oldGenFactor + 2) * need;
+ if (got > need) {
+ returnMemoryToOS(got - need);
+ }
+ }
+
// check sanity after GC
IF_DEBUG(sanity, checkSanity(rtsTrue));
slop = calcLiveBlocks() * BLOCK_SIZE_W - live;
stat_endGC(allocated, live, copied, N, max_copied, avg_copied, slop);
- // unlock the StablePtr table
- stablePtrPostGC();
-
// Guess which generation we'll collect *next* time
initialise_N(force_major_gc);
#endif
gct->no_work++;
+#if defined(THREADED_RTS)
+ yieldThread();
+#endif
return rtsFalse;
}
loop:
- traceEvent(&capabilities[gct->thread_index], EVENT_GC_WORK);
+ traceEventGcWork(&capabilities[gct->thread_index]);
#if defined(THREADED_RTS)
if (n_gc_threads > 1) {
// scavenge_loop() only exits when there's no work to do
r = dec_running();
- traceEvent(&capabilities[gct->thread_index], EVENT_GC_IDLE);
+ traceEventGcIdle(&capabilities[gct->thread_index]);
debugTrace(DEBUG_gc, "%d GC threads still running", r);
// scavenge_loop() to perform any pending work.
}
- traceEvent(&capabilities[gct->thread_index], EVENT_GC_DONE);
+ traceEventGcDone(&capabilities[gct->thread_index]);
}
#if defined(THREADED_RTS)
scavenge_until_all_done();
+#ifdef THREADED_RTS
+ // Now that the whole heap is marked, we discard any sparks that
+ // were found to be unreachable. The main GC thread is currently
+ // marking heap reachable via weak pointers, so it is
+ // non-deterministic whether a spark will be retained if it is
+ // only reachable via weak pointers. To fix this problem would
+ // require another GC barrier, which is too high a price.
+ pruneSparkQueue(cap);
+#endif
+
#ifdef USE_PAPI
// count events in this thread towards the GC totals
papi_thread_stop_gc1_count(gct->papi_events);
void
waitForGcThreads (Capability *cap USED_IF_THREADS)
{
- nat n_threads = RtsFlags.ParFlags.nNodes;
- nat me = cap->no;
+ const nat n_threads = RtsFlags.ParFlags.nNodes;
+ const nat me = cap->no;
nat i, j;
rtsBool retry = rtsTrue;
void
releaseGCThreads (Capability *cap USED_IF_THREADS)
{
- nat n_threads = RtsFlags.ParFlags.nNodes;
- nat me = cap->no;
+ const nat n_threads = RtsFlags.ParFlags.nNodes;
+ const nat me = cap->no;
nat i;
for (i=0; i < n_threads; i++) {
if (i == me) continue;
if (!(bd->flags & BF_FRAGMENTED)) {
bd->flags |= BF_MARKED;
}
+
+ // BF_SWEPT should be marked only for blocks that are being
+ // collected in sweep()
+ bd->flags &= ~BF_SWEPT;
}
}
}
}
/* ----------------------------------------------------------------------------
- Update the pointers from the task list
-
- These are treated as weak pointers because we want to allow a main
- thread to get a BlockedOnDeadMVar exception in the same way as any
- other thread. Note that the threads should all have been retained
- by GC by virtue of being on the all_threads list, we're just
- updating pointers here.
- ------------------------------------------------------------------------- */
-
-static void
-update_task_list (void)
-{
- Task *task;
- StgTSO *tso;
- for (task = all_tasks; task != NULL; task = task->all_link) {
- if (!task->stopped && task->tso) {
- ASSERT(task->tso->bound == task);
- tso = (StgTSO *) isAlive((StgClosure *)task->tso);
- if (tso == NULL) {
- barf("task %p: main thread %d has been GC'd",
-#ifdef THREADED_RTS
- (void *)task->id,
-#else
- (void *)task,
-#endif
- task->tso->id);
- }
- task->tso = tso;
- }
- }
-}
-
-/* ----------------------------------------------------------------------------
Reset the sizes of the older generations when we do a major
collection.
if (major_gc && RtsFlags.GcFlags.generations > 1) {
nat live, size, min_alloc, words;
- nat max = RtsFlags.GcFlags.maxHeapSize;
- nat gens = RtsFlags.GcFlags.generations;
+ const nat max = RtsFlags.GcFlags.maxHeapSize;
+ const nat gens = RtsFlags.GcFlags.generations;
// live in the oldest generations
if (oldest_gen->live_estimate != 0) {
// Auto-enable compaction when the residency reaches a
// certain percentage of the maximum heap size (default: 30%).
- if (RtsFlags.GcFlags.generations > 1 &&
- (RtsFlags.GcFlags.compact ||
- (max > 0 &&
- oldest_gen->n_blocks >
- (RtsFlags.GcFlags.compactThreshold * max) / 100))) {
+ if (RtsFlags.GcFlags.compact ||
+ (max > 0 &&
+ oldest_gen->n_blocks >
+ (RtsFlags.GcFlags.compactThreshold * max) / 100)) {
oldest_gen->mark = 1;
oldest_gen->compact = 1;
// debugBelch("compaction: on\n", live);
static void
resize_nursery (void)
{
- lnat min_nursery = RtsFlags.GcFlags.minAllocAreaSize * n_capabilities;
+ const lnat min_nursery = RtsFlags.GcFlags.minAllocAreaSize * n_capabilities;
if (RtsFlags.GcFlags.generations == 1)
{ // Two-space collector:
if (RtsFlags.GcFlags.heapSizeSuggestion)
{
long blocks;
- nat needed = calcNeeded(); // approx blocks needed at next GC
+ const nat needed = calcNeeded(); // approx blocks needed at next GC
/* Guess how much will be live in generation 0 step 0 next time.
* A good approximation is obtained by finding the