#include "RetainerProfile.h" // for counting memory blocks (memInventory)
#include "OSMem.h"
#include "Trace.h"
+#include "GC.h"
+#include "GCUtils.h"
#include <stdlib.h>
#include <string.h>
stp->n_old_blocks = 0;
stp->gen = &generations[g];
stp->gen_no = g;
- stp->hp = NULL;
- stp->hpLim = NULL;
- stp->hp_bd = NULL;
- stp->scavd_hp = NULL;
- stp->scavd_hpLim = NULL;
- stp->scan = NULL;
- stp->scan_bd = NULL;
stp->large_objects = NULL;
stp->n_large_blocks = 0;
- stp->new_large_objects = NULL;
stp->scavenged_large_objects = NULL;
stp->n_scavenged_large_blocks = 0;
stp->is_compacted = 0;
stp->bitmap = NULL;
+#ifdef THREADED_RTS
+ initSpinLock(&stp->sync_todo);
+ initSpinLock(&stp->sync_large_objects);
+#endif
}
void
{
nat g, s;
generation *gen;
+ step *step_arr;
if (generations != NULL) {
// multi-init protection
/* Sanity check to make sure the LOOKS_LIKE_ macros appear to be
* doing something reasonable.
*/
- ASSERT(LOOKS_LIKE_INFO_PTR(&stg_BLACKHOLE_info));
+ /* We use the NOT_NULL variant or gcc warns that the test is always true */
+ ASSERT(LOOKS_LIKE_INFO_PTR_NOT_NULL(&stg_BLACKHOLE_info));
ASSERT(LOOKS_LIKE_CLOSURE_PTR(&stg_dummy_ret_closure));
ASSERT(!HEAP_ALLOCED(&stg_dummy_ret_closure));
* sizeof(struct generation_),
"initStorage: gens");
+ /* allocate all the steps into an array. It is important that we do
+ it this way, because we need the invariant that two step pointers
+ can be directly compared to see which is the oldest.
+ Remember that the last generation has only one step. */
+ step_arr = stgMallocBytes(sizeof(struct step_)
+ * (1 + ((RtsFlags.GcFlags.generations - 1)
+ * RtsFlags.GcFlags.steps)),
+ "initStorage: steps");
+
/* Initialise all generations */
for(g = 0; g < RtsFlags.GcFlags.generations; g++) {
gen = &generations[g];
/* Oldest generation: one step */
oldest_gen->n_steps = 1;
- oldest_gen->steps =
- stgMallocBytes(1 * sizeof(struct step_), "initStorage: last step");
+ oldest_gen->steps = step_arr + (RtsFlags.GcFlags.generations - 1)
+ * RtsFlags.GcFlags.steps;
/* set up all except the oldest generation with 2 steps */
for(g = 0; g < RtsFlags.GcFlags.generations-1; g++) {
generations[g].n_steps = RtsFlags.GcFlags.steps;
- generations[g].steps =
- stgMallocBytes (RtsFlags.GcFlags.steps * sizeof(struct step_),
- "initStorage: steps");
+ generations[g].steps = step_arr + g * RtsFlags.GcFlags.steps;
}
} else {
/* single generation, i.e. a two-space collector */
g0->n_steps = 1;
- g0->steps = stgMallocBytes (sizeof(struct step_), "initStorage: steps");
+ g0->steps = step_arr;
}
#ifdef THREADED_RTS
/* Tell GNU multi-precision pkg about our custom alloc functions */
mp_set_memory_functions(stgAllocForGMP, stgReallocForGMP, stgDeallocForGMP);
+#ifdef THREADED_RTS
+ initSpinLock(&gc_alloc_block_sync);
+#endif
+
IF_DEBUG(gc, statDescribeGens());
RELEASE_SM_LOCK;
void
freeStorage (void)
{
- nat g;
-
- for(g = 0; g < RtsFlags.GcFlags.generations; g++)
- stgFree(generations[g].steps);
+ stgFree(g0s0); // frees all the steps
stgFree(generations);
freeAllMBlocks();
#if defined(THREADED_RTS)
/* Approximate the amount of live data in the heap. To be called just
* after garbage collection (see GarbageCollect()).
*/
-extern lnat
-calcLive(void)
+lnat
+calcLiveBlocks(void)
{
nat g, s;
lnat live = 0;
step *stp;
if (RtsFlags.GcFlags.generations == 1) {
- return (g0s0->n_large_blocks + g0s0->n_blocks) * BLOCK_SIZE_W;
+ return g0s0->n_large_blocks + g0s0->n_blocks;
}
for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
continue;
}
stp = &generations[g].steps[s];
- live += (stp->n_large_blocks + stp->n_blocks) * BLOCK_SIZE_W;
+ live += stp->n_large_blocks + stp->n_blocks;
}
}
return live;
}
+lnat
+countOccupied(bdescr *bd)
+{
+ lnat words;
+
+ words = 0;
+ for (; bd != NULL; bd = bd->link) {
+ words += bd->free - bd->start;
+ }
+ return words;
+}
+
+lnat
+calcLiveWords(void)
+{
+ nat g, s;
+ lnat live;
+ step *stp;
+
+ if (RtsFlags.GcFlags.generations == 1) {
+ return countOccupied(g0s0->blocks) + countOccupied(g0s0->large_objects);
+ }
+
+ live = 0;
+ for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
+ for (s = 0; s < generations[g].n_steps; s++) {
+ /* approximate amount of live data (doesn't take into account slop
+ * at end of each block).
+ */
+ if (g == 0 && s == 0) continue;
+ stp = &generations[g].steps[s];
+ live += countOccupied(stp->blocks) +
+ countOccupied(stp->large_objects);
+ }
+ }
+ return live;
+}
+
/* Approximate the number of blocks that will be needed at the next
* garbage collection.
*
#ifdef DEBUG
+// Useful for finding partially full blocks in gdb
+void findSlop(bdescr *bd);
+void findSlop(bdescr *bd)
+{
+ lnat slop;
+
+ for (; bd != NULL; bd = bd->link) {
+ slop = (bd->blocks * BLOCK_SIZE_W) - (bd->free - bd->start);
+ if (slop > (1024/sizeof(W_))) {
+ debugBelch("block at %p (bdescr %p) has %ldKB slop\n",
+ bd->start, bd, slop / (1024/sizeof(W_)));
+ }
+ }
+}
+
nat
countBlocks(bdescr *bd)
{