1 /* -----------------------------------------------------------------------------
2 * $Id: Storage.c,v 1.20 1999/11/02 15:06:04 simonmar Exp $
4 * (c) The GHC Team, 1998-1999
6 * Storage manager front end
8 * ---------------------------------------------------------------------------*/
15 #include "BlockAlloc.h"
23 #include "StoragePriv.h"
26 nat nursery_blocks; /* number of blocks in the nursery */
29 StgClosure *caf_list = NULL;
31 bdescr *small_alloc_list; /* allocate()d small objects */
32 bdescr *large_alloc_list; /* allocate()d large objects */
33 nat alloc_blocks; /* number of allocate()d blocks since GC */
34 nat alloc_blocks_lim; /* approximate limit on alloc_blocks */
36 StgPtr alloc_Hp = NULL; /* next free byte in small_alloc_list */
37 StgPtr alloc_HpLim = NULL; /* end of block at small_alloc_list */
39 generation *generations; /* all the generations */
40 generation *g0; /* generation 0, for convenience */
41 generation *oldest_gen; /* oldest generation, for convenience */
42 step *g0s0; /* generation 0, step 0, for convenience */
45 * Storage manager mutex: protects all the above state from
46 * simultaneous access by two STG threads.
49 pthread_mutex_t sm_mutex = PTHREAD_MUTEX_INITIALIZER;
55 static void *stgAllocForGMP (size_t size_in_bytes);
56 static void *stgReallocForGMP (void *ptr, size_t old_size, size_t new_size);
57 static void stgDeallocForGMP (void *ptr, size_t size);
66 /* If we're doing heap profiling, we want a two-space heap with a
67 * fixed-size allocation area so that we get roughly even-spaced
70 #if defined(PROFILING) || defined(DEBUG)
71 if (RtsFlags.ProfFlags.doHeapProfile) {
72 RtsFlags.GcFlags.generations = 1;
73 RtsFlags.GcFlags.steps = 1;
74 RtsFlags.GcFlags.oldGenFactor = 0;
75 RtsFlags.GcFlags.heapSizeSuggestion = 0;
79 if (RtsFlags.GcFlags.heapSizeSuggestion >
80 RtsFlags.GcFlags.maxHeapSize) {
81 RtsFlags.GcFlags.maxHeapSize = RtsFlags.GcFlags.heapSizeSuggestion;
86 /* allocate generation info array */
87 generations = (generation *)stgMallocBytes(RtsFlags.GcFlags.generations
88 * sizeof(struct _generation),
91 /* Initialise all generations */
92 for(g = 0; g < RtsFlags.GcFlags.generations; g++) {
93 gen = &generations[g];
95 gen->mut_list = END_MUT_LIST;
96 gen->mut_once_list = END_MUT_LIST;
98 gen->failed_promotions = 0;
102 /* A couple of convenience pointers */
103 g0 = &generations[0];
104 oldest_gen = &generations[RtsFlags.GcFlags.generations-1];
106 /* Allocate step structures in each generation */
107 if (RtsFlags.GcFlags.generations > 1) {
108 /* Only for multiple-generations */
110 /* Oldest generation: one step */
111 oldest_gen->n_steps = 1;
113 stgMallocBytes(1 * sizeof(struct _step), "initStorage: last step");
115 /* set up all except the oldest generation with 2 steps */
116 for(g = 0; g < RtsFlags.GcFlags.generations-1; g++) {
117 generations[g].n_steps = RtsFlags.GcFlags.steps;
118 generations[g].steps =
119 stgMallocBytes (RtsFlags.GcFlags.steps * sizeof(struct _step),
120 "initStorage: steps");
124 /* single generation, i.e. a two-space collector */
126 g0->steps = stgMallocBytes (sizeof(struct _step), "initStorage: steps");
129 /* Initialise all steps */
130 for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
131 for (s = 0; s < generations[g].n_steps; s++) {
132 step = &generations[g].steps[s];
136 step->gen = &generations[g];
141 step->scan_bd = NULL;
142 step->large_objects = NULL;
143 step->new_large_objects = NULL;
144 step->scavenged_large_objects = NULL;
148 /* Set up the destination pointers in each younger gen. step */
149 for (g = 0; g < RtsFlags.GcFlags.generations-1; g++) {
150 for (s = 0; s < generations[g].n_steps-1; s++) {
151 generations[g].steps[s].to = &generations[g].steps[s+1];
153 generations[g].steps[s].to = &generations[g+1].steps[0];
156 /* The oldest generation has one step and its destination is the
158 oldest_gen->steps[0].to = &oldest_gen->steps[0];
160 /* generation 0 is special: that's the nursery */
161 generations[0].max_blocks = 0;
163 /* G0S0: the allocation area. Policy: keep the allocation area
164 * small to begin with, even if we have a large suggested heap
165 * size. Reason: we're going to do a major collection first, and we
166 * don't want it to be a big one. This vague idea is borne out by
167 * rigorous experimental evidence.
169 g0s0 = &generations[0].steps[0];
173 weak_ptr_list = NULL;
176 /* initialise the allocate() interface */
177 small_alloc_list = NULL;
178 large_alloc_list = NULL;
180 alloc_blocks_lim = RtsFlags.GcFlags.minAllocAreaSize;
183 /* Tell GNU multi-precision pkg about our custom alloc functions */
184 mp_set_memory_functions(stgAllocForGMP, stgReallocForGMP, stgDeallocForGMP);
188 pthread_mutex_init(&sm_mutex, NULL);
191 IF_DEBUG(gc, stat_describe_gens());
197 stat_exit(calcAllocated());
201 newCAF(StgClosure* caf)
203 /* Put this CAF on the mutable list for the old generation.
204 * This is a HACK - the IND_STATIC closure doesn't really have
205 * a mut_link field, but we pretend it has - in fact we re-use
206 * the STATIC_LINK field for the time being, because when we
207 * come to do a major GC we won't need the mut_link field
208 * any more and can use it as a STATIC_LINK.
210 ACQUIRE_LOCK(&sm_mutex);
211 ((StgMutClosure *)caf)->mut_link = oldest_gen->mut_once_list;
212 oldest_gen->mut_once_list = (StgMutClosure *)caf;
216 const StgInfoTable *info;
218 info = get_itbl(caf);
219 ASSERT(info->type == IND_STATIC);
221 STATIC_LINK2(info,caf) = caf_list;
226 RELEASE_LOCK(&sm_mutex);
229 /* -----------------------------------------------------------------------------
231 -------------------------------------------------------------------------- */
234 allocNurseries( void )
242 for (cap = free_capabilities; cap != NULL; cap = cap->link) {
243 cap->rNursery = allocNursery(NULL, RtsFlags.GcFlags.minAllocAreaSize);
244 cap->rCurrentNursery = cap->rNursery;
248 nursery_blocks = RtsFlags.GcFlags.minAllocAreaSize;
249 g0s0->blocks = allocNursery(NULL, nursery_blocks);
250 g0s0->n_blocks = nursery_blocks;
251 g0s0->to_space = NULL;
252 MainRegTable.rNursery = g0s0->blocks;
253 MainRegTable.rCurrentNursery = g0s0->blocks;
254 /* hp, hpLim, hp_bd, to_space etc. aren't used in G0S0 */
259 resetNurseries( void )
265 /* All tasks must be stopped */
266 ASSERT(n_free_capabilities == RtsFlags.ConcFlags.nNodes);
268 for (cap = free_capabilities; cap != NULL; cap = cap->link) {
269 for (bd = cap->rNursery; bd; bd = bd->link) {
270 bd->free = bd->start;
271 ASSERT(bd->gen == g0);
272 ASSERT(bd->step == g0s0);
273 IF_DEBUG(sanity,memset(bd->start, 0xaa, BLOCK_SIZE));
275 cap->rCurrentNursery = cap->rNursery;
278 for (bd = g0s0->blocks; bd; bd = bd->link) {
279 bd->free = bd->start;
280 ASSERT(bd->gen == g0);
281 ASSERT(bd->step == g0s0);
282 IF_DEBUG(sanity,memset(bd->start, 0xaa, BLOCK_SIZE));
284 MainRegTable.rNursery = g0s0->blocks;
285 MainRegTable.rCurrentNursery = g0s0->blocks;
290 allocNursery (bdescr *last_bd, nat blocks)
295 /* Allocate a nursery */
296 for (i=0; i < blocks; i++) {
302 bd->free = bd->start;
309 resizeNursery ( nat blocks )
314 barf("resizeNursery: can't resize in SMP mode");
317 if (nursery_blocks == blocks) {
318 ASSERT(g0s0->n_blocks == blocks);
322 else if (nursery_blocks < blocks) {
323 IF_DEBUG(gc, fprintf(stderr, "Increasing size of nursery to %d blocks\n",
325 g0s0->blocks = allocNursery(g0s0->blocks, blocks-nursery_blocks);
331 IF_DEBUG(gc, fprintf(stderr, "Decreasing size of nursery to %d blocks\n",
333 for (bd = g0s0->blocks; nursery_blocks > blocks; nursery_blocks--) {
341 g0s0->n_blocks = nursery_blocks = blocks;
344 /* -----------------------------------------------------------------------------
345 The allocate() interface
347 allocate(n) always succeeds, and returns a chunk of memory n words
348 long. n can be larger than the size of a block if necessary, in
349 which case a contiguous block group will be allocated.
350 -------------------------------------------------------------------------- */
358 ACQUIRE_LOCK(&sm_mutex);
360 TICK_ALLOC_HEAP_NOCTR(n);
363 /* big allocation (>LARGE_OBJECT_THRESHOLD) */
364 /* ToDo: allocate directly into generation 1 */
365 if (n >= LARGE_OBJECT_THRESHOLD/sizeof(W_)) {
366 nat req_blocks = (lnat)BLOCK_ROUND_UP(n*sizeof(W_)) / BLOCK_SIZE;
367 bd = allocGroup(req_blocks);
368 dbl_link_onto(bd, &g0s0->large_objects);
372 bd->free = bd->start;
373 /* don't add these blocks to alloc_blocks, since we're assuming
374 * that large objects are likely to remain live for quite a while
375 * (eg. running threads), so garbage collecting early won't make
378 RELEASE_LOCK(&sm_mutex);
381 /* small allocation (<LARGE_OBJECT_THRESHOLD) */
382 } else if (small_alloc_list == NULL || alloc_Hp + n > alloc_HpLim) {
383 if (small_alloc_list) {
384 small_alloc_list->free = alloc_Hp;
387 bd->link = small_alloc_list;
388 small_alloc_list = bd;
392 alloc_Hp = bd->start;
393 alloc_HpLim = bd->start + BLOCK_SIZE_W;
399 RELEASE_LOCK(&sm_mutex);
403 lnat allocated_bytes(void)
405 return (alloc_blocks * BLOCK_SIZE_W - (alloc_HpLim - alloc_Hp));
408 /* -----------------------------------------------------------------------------
409 Allocation functions for GMP.
411 These all use the allocate() interface - we can't have any garbage
412 collection going on during a gmp operation, so we use allocate()
413 which always succeeds. The gmp operations which might need to
414 allocate will ask the storage manager (via doYouWantToGC()) whether
415 a garbage collection is required, in case we get into a loop doing
416 only allocate() style allocation.
417 -------------------------------------------------------------------------- */
420 stgAllocForGMP (size_t size_in_bytes)
423 nat data_size_in_words, total_size_in_words;
425 /* should be a multiple of sizeof(StgWord) (whole no. of limbs) */
426 ASSERT(size_in_bytes % sizeof(W_) == 0);
428 data_size_in_words = size_in_bytes / sizeof(W_);
429 total_size_in_words = sizeofW(StgArrWords) + data_size_in_words;
431 /* allocate and fill it in. */
432 arr = (StgArrWords *)allocate(total_size_in_words);
433 SET_ARR_HDR(arr, &ARR_WORDS_info, CCCS, data_size_in_words);
435 /* and return a ptr to the goods inside the array */
436 return(BYTE_ARR_CTS(arr));
440 stgReallocForGMP (void *ptr, size_t old_size, size_t new_size)
442 void *new_stuff_ptr = stgAllocForGMP(new_size);
444 char *p = (char *) ptr;
445 char *q = (char *) new_stuff_ptr;
447 for (; i < old_size; i++, p++, q++) {
451 return(new_stuff_ptr);
455 stgDeallocForGMP (void *ptr STG_UNUSED,
456 size_t size STG_UNUSED)
458 /* easy for us: the garbage collector does the dealloc'n */
461 /* -----------------------------------------------------------------------------
463 * -------------------------------------------------------------------------- */
465 /* -----------------------------------------------------------------------------
468 * Approximate how much we've allocated: number of blocks in the
469 * nursery + blocks allocated via allocate() - unused nusery blocks.
470 * This leaves a little slop at the end of each block, and doesn't
471 * take into account large objects (ToDo).
472 * -------------------------------------------------------------------------- */
475 calcAllocated( void )
483 /* All tasks must be stopped */
484 ASSERT(n_free_capabilities == RtsFlags.ConcFlags.nNodes);
487 n_free_capabilities * RtsFlags.GcFlags.minAllocAreaSize * BLOCK_SIZE_W
490 for (cap = free_capabilities; cap != NULL; cap = cap->link) {
491 for ( bd = cap->rCurrentNursery->link; bd != NULL; bd = bd->link ) {
492 allocated -= BLOCK_SIZE_W;
494 if (cap->rCurrentNursery->free < cap->rCurrentNursery->start
496 allocated -= (cap->rCurrentNursery->start + BLOCK_SIZE_W)
497 - cap->rCurrentNursery->free;
502 bdescr *current_nursery = MainRegTable.rCurrentNursery;
504 allocated = (nursery_blocks * BLOCK_SIZE_W) + allocated_bytes();
505 for ( bd = current_nursery->link; bd != NULL; bd = bd->link ) {
506 allocated -= BLOCK_SIZE_W;
508 if (current_nursery->free < current_nursery->start + BLOCK_SIZE_W) {
509 allocated -= (current_nursery->start + BLOCK_SIZE_W)
510 - current_nursery->free;
517 /* Approximate the amount of live data in the heap. To be called just
518 * after garbage collection (see GarbageCollect()).
527 if (RtsFlags.GcFlags.generations == 1) {
528 live = (g0s0->to_blocks - 1) * BLOCK_SIZE_W +
529 ((lnat)g0s0->hp_bd->free - (lnat)g0s0->hp_bd->start) / sizeof(W_);
533 for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
534 for (s = 0; s < generations[g].n_steps; s++) {
535 /* approximate amount of live data (doesn't take into account slop
536 * at end of each block).
538 if (g == 0 && s == 0) {
541 step = &generations[g].steps[s];
542 live += (step->n_blocks - 1) * BLOCK_SIZE_W +
543 ((lnat)step->hp_bd->free - (lnat)step->hp_bd->start) / sizeof(W_);
549 /* Approximate the number of blocks that will be needed at the next
550 * garbage collection.
552 * Assume: all data currently live will remain live. Steps that will
553 * be collected next time will therefore need twice as many blocks
554 * since all the data will be copied.
563 for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
564 for (s = 0; s < generations[g].n_steps; s++) {
565 if (g == 0 && s == 0) { continue; }
566 step = &generations[g].steps[s];
567 if (generations[g].steps[0].n_blocks > generations[g].max_blocks) {
568 needed += 2 * step->n_blocks;
570 needed += step->n_blocks;
577 /* -----------------------------------------------------------------------------
580 memInventory() checks for memory leaks by counting up all the
581 blocks we know about and comparing that to the number of blocks
582 allegedly floating around in the system.
583 -------------------------------------------------------------------------- */
593 lnat total_blocks = 0, free_blocks = 0;
595 /* count the blocks we current have */
597 for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
598 for (s = 0; s < generations[g].n_steps; s++) {
599 step = &generations[g].steps[s];
600 total_blocks += step->n_blocks;
601 if (RtsFlags.GcFlags.generations == 1) {
602 /* two-space collector has a to-space too :-) */
603 total_blocks += g0s0->to_blocks;
605 for (bd = step->large_objects; bd; bd = bd->link) {
606 total_blocks += bd->blocks;
607 /* hack for megablock groups: they have an extra block or two in
608 the second and subsequent megablocks where the block
609 descriptors would normally go.
611 if (bd->blocks > BLOCKS_PER_MBLOCK) {
612 total_blocks -= (MBLOCK_SIZE / BLOCK_SIZE - BLOCKS_PER_MBLOCK)
613 * (bd->blocks/(MBLOCK_SIZE/BLOCK_SIZE));
619 /* any blocks held by allocate() */
620 for (bd = small_alloc_list; bd; bd = bd->link) {
621 total_blocks += bd->blocks;
623 for (bd = large_alloc_list; bd; bd = bd->link) {
624 total_blocks += bd->blocks;
627 /* count the blocks on the free list */
628 free_blocks = countFreeList();
630 ASSERT(total_blocks + free_blocks == mblocks_allocated * BLOCKS_PER_MBLOCK);
633 if (total_blocks + free_blocks != mblocks_allocated *
635 fprintf(stderr, "Blocks: %ld live + %ld free = %ld total (%ld around)\n",
636 total_blocks, free_blocks, total_blocks + free_blocks,
637 mblocks_allocated * BLOCKS_PER_MBLOCK);
642 /* Full heap sanity check. */
649 if (RtsFlags.GcFlags.generations == 1) {
650 checkHeap(g0s0->to_space, NULL);
651 checkChain(g0s0->large_objects);
654 for (g = 0; g <= N; g++) {
655 for (s = 0; s < generations[g].n_steps; s++) {
656 if (g == 0 && s == 0) { continue; }
657 checkHeap(generations[g].steps[s].blocks, NULL);
660 for (g = N+1; g < RtsFlags.GcFlags.generations; g++) {
661 for (s = 0; s < generations[g].n_steps; s++) {
662 checkHeap(generations[g].steps[s].blocks,
663 generations[g].steps[s].blocks->start);
664 checkChain(generations[g].steps[s].large_objects);
667 checkFreeListSanity();