X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=ghc%2Frts%2FStorage.c;h=382acd318a86443526a415718461db266cbe7c1f;hb=34a98f40dea6d31ced5213b7810dc39b4989c395;hp=1f080c58a75493d08b3d2293428d67e033fbf918;hpb=f939293c9e2b8eead06935196500734b1240aee8;p=ghc-hetmet.git diff --git a/ghc/rts/Storage.c b/ghc/rts/Storage.c index 1f080c5..382acd3 100644 --- a/ghc/rts/Storage.c +++ b/ghc/rts/Storage.c @@ -1,5 +1,7 @@ /* ----------------------------------------------------------------------------- - * $Id: Storage.c,v 1.8 1999/01/28 15:04:02 simonm Exp $ + * $Id: Storage.c,v 1.28 2000/11/13 14:40:37 simonmar Exp $ + * + * (c) The GHC Team, 1998-1999 * * Storage manager front end * @@ -12,15 +14,16 @@ #include "Hooks.h" #include "BlockAlloc.h" #include "MBlock.h" -#include "gmp.h" #include "Weak.h" #include "Sanity.h" #include "Storage.h" +#include "Schedule.h" #include "StoragePriv.h" -bdescr *current_nursery; /* next available nursery block, or NULL */ +#ifndef SMP nat nursery_blocks; /* number of blocks in the nursery */ +#endif StgClosure *caf_list = NULL; @@ -37,6 +40,16 @@ generation *g0; /* generation 0, for convenience */ generation *oldest_gen; /* oldest generation, for convenience */ step *g0s0; /* generation 0, step 0, for convenience */ +lnat total_allocated = 0; /* total memory allocated during run */ + +/* + * Storage manager mutex: protects all the above state from + * simultaneous access by two STG threads. + */ +#ifdef SMP +pthread_mutex_t sm_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif + /* * Forward references */ @@ -51,9 +64,22 @@ initStorage (void) step *step; generation *gen; + /* If we're doing heap profiling, we want a two-space heap with a + * fixed-size allocation area so that we get roughly even-spaced + * samples. + */ +#if defined(PROFILING) || defined(DEBUG) + if (RtsFlags.ProfFlags.doHeapProfile) { + RtsFlags.GcFlags.generations = 1; + RtsFlags.GcFlags.steps = 1; + RtsFlags.GcFlags.oldGenFactor = 0; + RtsFlags.GcFlags.heapSizeSuggestion = 0; + } +#endif + if (RtsFlags.GcFlags.heapSizeSuggestion > RtsFlags.GcFlags.maxHeapSize) { - barf("Suggested heap size (-H) is larger than max. heap size (-M)\n"); + RtsFlags.GcFlags.maxHeapSize = RtsFlags.GcFlags.heapSizeSuggestion; } initBlockAllocator(); @@ -68,9 +94,10 @@ initStorage (void) gen = &generations[g]; gen->no = g; gen->mut_list = END_MUT_LIST; + gen->mut_once_list = END_MUT_LIST; gen->collections = 0; gen->failed_promotions = 0; - gen->max_blocks = RtsFlags.GcFlags.minOldGenSize; + gen->max_blocks = 0; } /* A couple of convenience pointers */ @@ -111,6 +138,8 @@ initStorage (void) step->hp = NULL; step->hpLim = NULL; step->hp_bd = NULL; + step->scan = NULL; + step->scan_bd = NULL; step->large_objects = NULL; step->new_large_objects = NULL; step->scavenged_large_objects = NULL; @@ -132,14 +161,15 @@ initStorage (void) /* generation 0 is special: that's the nursery */ generations[0].max_blocks = 0; - /* G0S0: the allocation area */ - step = &generations[0].steps[0]; - g0s0 = step; - step->blocks = allocNursery(NULL, RtsFlags.GcFlags.minAllocAreaSize); - step->n_blocks = RtsFlags.GcFlags.minAllocAreaSize; - nursery_blocks = RtsFlags.GcFlags.minAllocAreaSize; - current_nursery = step->blocks; - /* hp, hpLim, hp_bd, to_space etc. aren't used in G0S0 */ + /* G0S0: the allocation area. Policy: keep the allocation area + * small to begin with, even if we have a large suggested heap + * size. Reason: we're going to do a major collection first, and we + * don't want it to be a big one. This vague idea is borne out by + * rigorous experimental evidence. + */ + g0s0 = &generations[0].steps[0]; + + allocNurseries(); weak_ptr_list = NULL; caf_list = NULL; @@ -150,15 +180,178 @@ initStorage (void) alloc_blocks = 0; alloc_blocks_lim = RtsFlags.GcFlags.minAllocAreaSize; -#ifdef COMPILER /* Tell GNU multi-precision pkg about our custom alloc functions */ mp_set_memory_functions(stgAllocForGMP, stgReallocForGMP, stgDeallocForGMP); + +#ifdef SMP + pthread_mutex_init(&sm_mutex, NULL); #endif IF_DEBUG(gc, stat_describe_gens()); } -extern bdescr * +void +exitStorage (void) +{ + stat_exit(calcAllocated()); +} + + +/* ----------------------------------------------------------------------------- + CAF management. + -------------------------------------------------------------------------- */ + +void +newCAF(StgClosure* caf) +{ + /* Put this CAF on the mutable list for the old generation. + * This is a HACK - the IND_STATIC closure doesn't really have + * a mut_link field, but we pretend it has - in fact we re-use + * the STATIC_LINK field for the time being, because when we + * come to do a major GC we won't need the mut_link field + * any more and can use it as a STATIC_LINK. + */ + ACQUIRE_LOCK(&sm_mutex); + + ASSERT( ((StgMutClosure*)caf)->mut_link == NULL ); + ((StgMutClosure *)caf)->mut_link = oldest_gen->mut_once_list; + oldest_gen->mut_once_list = (StgMutClosure *)caf; + +#ifdef INTERPRETER + /* If we're Hugs, we also have to put it in the CAF table, so that + the CAF can be reverted. When reverting, CAFs created by compiled + code are recorded in the CAF table, which lives outside the + heap, in mallocville. CAFs created by interpreted code are + chained together via the link fields in StgCAFs, and are not + recorded in the CAF table. + */ + ASSERT( get_itbl(caf)->type == THUNK_STATIC ); + addToECafTable ( caf, get_itbl(caf) ); +#endif + + RELEASE_LOCK(&sm_mutex); +} + +#ifdef INTERPRETER +void +newCAF_made_by_Hugs(StgCAF* caf) +{ + ACQUIRE_LOCK(&sm_mutex); + + ASSERT( get_itbl(caf)->type == CAF_ENTERED ); + recordOldToNewPtrs((StgMutClosure*)caf); + caf->link = ecafList; + ecafList = caf->link; + + RELEASE_LOCK(&sm_mutex); +} +#endif + +#ifdef INTERPRETER +/* These initialisations are critical for correct operation + on the first call of addToECafTable. +*/ +StgCAF* ecafList = END_ECAF_LIST; +StgCAFTabEntry* ecafTable = NULL; +StgInt usedECafTable = 0; +StgInt sizeECafTable = 0; + + +void clearECafTable ( void ) +{ + usedECafTable = 0; +} + +void addToECafTable ( StgClosure* closure, StgInfoTable* origItbl ) +{ + StgInt i; + StgCAFTabEntry* et2; + if (usedECafTable == sizeECafTable) { + /* Make the initial table size be 8 */ + sizeECafTable *= 2; + if (sizeECafTable == 0) sizeECafTable = 8; + et2 = stgMallocBytes ( + sizeECafTable * sizeof(StgCAFTabEntry), + "addToECafTable" ); + for (i = 0; i < usedECafTable; i++) + et2[i] = ecafTable[i]; + if (ecafTable) free(ecafTable); + ecafTable = et2; + } + ecafTable[usedECafTable].closure = closure; + ecafTable[usedECafTable].origItbl = origItbl; + usedECafTable++; +} +#endif + +/* ----------------------------------------------------------------------------- + Nursery management. + -------------------------------------------------------------------------- */ + +void +allocNurseries( void ) +{ +#ifdef SMP + { + Capability *cap; + bdescr *bd; + + g0s0->blocks = NULL; + g0s0->n_blocks = 0; + for (cap = free_capabilities; cap != NULL; cap = cap->link) { + cap->rNursery = allocNursery(NULL, RtsFlags.GcFlags.minAllocAreaSize); + cap->rCurrentNursery = cap->rNursery; + for (bd = cap->rNursery; bd != NULL; bd = bd->link) { + bd->back = (bdescr *)cap; + } + } + /* Set the back links to be equal to the Capability, + * so we can do slightly better informed locking. + */ + } +#else /* SMP */ + nursery_blocks = RtsFlags.GcFlags.minAllocAreaSize; + g0s0->blocks = allocNursery(NULL, nursery_blocks); + g0s0->n_blocks = nursery_blocks; + g0s0->to_space = NULL; + MainRegTable.rNursery = g0s0->blocks; + MainRegTable.rCurrentNursery = g0s0->blocks; + /* hp, hpLim, hp_bd, to_space etc. aren't used in G0S0 */ +#endif +} + +void +resetNurseries( void ) +{ + bdescr *bd; +#ifdef SMP + Capability *cap; + + /* All tasks must be stopped */ + ASSERT(n_free_capabilities == RtsFlags.ParFlags.nNodes); + + for (cap = free_capabilities; cap != NULL; cap = cap->link) { + for (bd = cap->rNursery; bd; bd = bd->link) { + bd->free = bd->start; + ASSERT(bd->gen == g0); + ASSERT(bd->step == g0s0); + IF_DEBUG(sanity,memset(bd->start, 0xaa, BLOCK_SIZE)); + } + cap->rCurrentNursery = cap->rNursery; + } +#else + for (bd = g0s0->blocks; bd; bd = bd->link) { + bd->free = bd->start; + ASSERT(bd->gen == g0); + ASSERT(bd->step == g0s0); + IF_DEBUG(sanity,memset(bd->start, 0xaa, BLOCK_SIZE)); + } + MainRegTable.rNursery = g0s0->blocks; + MainRegTable.rCurrentNursery = g0s0->blocks; +#endif +} + +bdescr * allocNursery (bdescr *last_bd, nat blocks) { bdescr *bd; @@ -177,11 +370,15 @@ allocNursery (bdescr *last_bd, nat blocks) return last_bd; } -extern void +void resizeNursery ( nat blocks ) { bdescr *bd; +#ifdef SMP + barf("resizeNursery: can't resize in SMP mode"); +#endif + if (nursery_blocks == blocks) { ASSERT(g0s0->n_blocks == blocks); return; @@ -209,66 +406,6 @@ resizeNursery ( nat blocks ) g0s0->n_blocks = nursery_blocks = blocks; } -void -exitStorage (void) -{ - lnat allocated; - bdescr *bd; - - /* Return code ignored for now */ - /* ToDo: allocation figure is slightly wrong (see also GarbageCollect()) */ - allocated = (nursery_blocks * BLOCK_SIZE_W) + allocated_bytes(); - for ( bd = current_nursery->link; bd != NULL; bd = bd->link ) { - allocated -= BLOCK_SIZE_W; - } - stat_exit(allocated); -} - -void -recordMutable(StgMutClosure *p) -{ - bdescr *bd; - - ASSERT(closure_MUTABLE(p)); - - bd = Bdescr((P_)p); - - /* no need to bother in generation 0 */ - if (bd->gen == g0) { - return; - } - - if (p->mut_link == NULL) { - p->mut_link = bd->gen->mut_list; - bd->gen->mut_list = p; - } -} - -void -newCAF(StgClosure* caf) -{ - /* Put this CAF on the mutable list for the old generation. - * This is a HACK - the IND_STATIC closure doesn't really have - * a mut_link field, but we pretend it has - in fact we re-use - * the STATIC_LINK field for the time being, because when we - * come to do a major GC we won't need the mut_link field - * any more and can use it as a STATIC_LINK. - */ - ((StgMutClosure *)caf)->mut_link = oldest_gen->mut_list; - oldest_gen->mut_list = (StgMutClosure *)caf; - -#ifdef DEBUG - { - const StgInfoTable *info; - - info = get_itbl(caf); - ASSERT(info->type == IND_STATIC); - STATIC_LINK2(info,caf) = caf_list; - caf_list = caf; - } -#endif -} - /* ----------------------------------------------------------------------------- The allocate() interface @@ -283,7 +420,9 @@ allocate(nat n) bdescr *bd; StgPtr p; - TICK_ALLOC_HEAP(n); + ACQUIRE_LOCK(&sm_mutex); + + TICK_ALLOC_HEAP_NOCTR(n); CCS_ALLOC(CCCS,n); /* big allocation (>LARGE_OBJECT_THRESHOLD) */ @@ -301,6 +440,8 @@ allocate(nat n) * (eg. running threads), so garbage collecting early won't make * much difference. */ + alloc_blocks += req_blocks; + RELEASE_LOCK(&sm_mutex); return bd->start; /* small allocation (link) { + for ( bd = cap->rCurrentNursery->link; bd != NULL; bd = bd->link ) { + allocated -= BLOCK_SIZE_W; + } + if (cap->rCurrentNursery->free < cap->rCurrentNursery->start + + BLOCK_SIZE_W) { + allocated -= (cap->rCurrentNursery->start + BLOCK_SIZE_W) + - cap->rCurrentNursery->free; + } + } + +#else /* !SMP */ + bdescr *current_nursery = MainRegTable.rCurrentNursery; + + allocated = (nursery_blocks * BLOCK_SIZE_W) + allocated_bytes(); + for ( bd = current_nursery->link; bd != NULL; bd = bd->link ) { + allocated -= BLOCK_SIZE_W; + } + if (current_nursery->free < current_nursery->start + BLOCK_SIZE_W) { + allocated -= (current_nursery->start + BLOCK_SIZE_W) + - current_nursery->free; + } +#endif + + total_allocated += allocated; + return allocated; +} /* Approximate the amount of live data in the heap. To be called just * after garbage collection (see GarbageCollect()). @@ -397,21 +595,22 @@ calcLive(void) step *step; if (RtsFlags.GcFlags.generations == 1) { - live = g0s0->to_blocks * BLOCK_SIZE_W + + live = (g0s0->to_blocks - 1) * BLOCK_SIZE_W + ((lnat)g0s0->hp_bd->free - (lnat)g0s0->hp_bd->start) / sizeof(W_); + return live; } 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). - */ + * at end of each block). + */ if (g == 0 && s == 0) { continue; } step = &generations[g].steps[s]; - live += step->n_blocks * BLOCK_SIZE_W + - ((lnat)step->hp_bd->free -(lnat)step->hp_bd->start) / sizeof(W_); + live += (step->n_blocks - 1) * BLOCK_SIZE_W + + ((lnat)step->hp_bd->free - (lnat)step->hp_bd->start) / sizeof(W_); } } return live; @@ -481,7 +680,7 @@ memInventory(void) */ if (bd->blocks > BLOCKS_PER_MBLOCK) { total_blocks -= (MBLOCK_SIZE / BLOCK_SIZE - BLOCKS_PER_MBLOCK) - * bd->blocks/(MBLOCK_SIZE/BLOCK_SIZE); + * (bd->blocks/(MBLOCK_SIZE/BLOCK_SIZE)); } } }