X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=rts%2FSparks.c;h=9e4492ac1925c66b4fe942d8a6480671200d4aac;hb=51cae5224f2f2f5db8b3196d0f5118ba4246701a;hp=ac11172a9d958299166be02b01e45c351a2f40ac;hpb=cf9650f2a1690c04051c716124bb0350adc74ae7;p=ghc-hetmet.git diff --git a/rts/Sparks.c b/rts/Sparks.c index ac11172..9e4492a 100644 --- a/rts/Sparks.c +++ b/rts/Sparks.c @@ -44,6 +44,7 @@ #include "RtsUtils.h" #include "ParTicky.h" #include "Trace.h" +#include "Prelude.h" #include "SMP.h" // for cas @@ -53,9 +54,9 @@ /* internal helpers ... */ -StgWord roundUp2(StgWord val); - -StgWord roundUp2(StgWord val) { +static StgWord +roundUp2(StgWord val) +{ StgWord rounded = 1; /* StgWord is unsigned anyway, only catch 0 */ @@ -69,25 +70,6 @@ StgWord roundUp2(StgWord val) { return rounded; } -INLINE_HEADER -rtsBool casTop(StgPtr addr, StgWord old, StgWord new); - -#if !defined(THREADED_RTS) -/* missing def. in non THREADED RTS, and makes no sense anyway... */ -StgWord cas(StgPtr addr,StgWord old,StgWord new); -StgWord cas(StgPtr addr,StgWord old,StgWord new) { - barf("cas: not implemented without multithreading"); - old = new = *addr; /* to avoid gcc warnings */ -} -#endif - -INLINE_HEADER -rtsBool casTop(StgWord* addr, StgWord old, StgWord new) { - StgWord res = cas((StgPtr) addr, old, new); - return ((res == old)); -} - -/* or simply like this */ #define CASTOP(addr,old,new) ((old) == cas(((StgPtr)addr),(old),(new))) /* ----------------------------------------------------------------------------- @@ -97,8 +79,9 @@ rtsBool casTop(StgWord* addr, StgWord old, StgWord new) { * -------------------------------------------------------------------------- */ /* constructor */ -SparkPool* initPool(StgWord size) { - +static SparkPool* +initPool(StgWord size) +{ StgWord realsize; SparkPool *q; @@ -136,14 +119,17 @@ initSparkPools( void ) } void -freeSparkPool(SparkPool *pool) { +freeSparkPool (SparkPool *pool) +{ /* should not interfere with concurrent findSpark() calls! And nobody should use the pointer any more. We cross our fingers...*/ stgFree(pool->elements); stgFree(pool); } -/* reclaimSpark(cap): remove a spark from the write end of the queue. +/* ----------------------------------------------------------------------------- + * + * reclaimSpark: remove a spark from the write end of the queue. * Returns the removed spark, and NULL if a race is lost or the pool * empty. * @@ -151,9 +137,12 @@ freeSparkPool(SparkPool *pool) { * concurrently stealing threads by using cas to modify the top field. * This routine should NEVER be called by a task which does not own * the capability. Can this be checked here? - */ -StgClosure* reclaimSpark(Capability *cap) { - SparkPool *deque = cap->sparks; + * + * -------------------------------------------------------------------------- */ + +StgClosure * +reclaimSpark (SparkPool *deque) +{ /* also a bit tricky, has to avoid concurrent steal() calls by accessing top with cas, when there is only one element left */ StgWord t, b; @@ -196,30 +185,32 @@ StgClosure* reclaimSpark(Capability *cap) { /* ----------------------------------------------------------------------------- * - * findSpark: find a spark on the current Capability that we can fork - * into a thread. + * tryStealSpark: try to steal a spark from a Capability. * - * May be called by concurrent threads, which synchronise on top - * variable. Returns a spark, or NULL if pool empty or race lost. + * Returns a valid spark, or NULL if the pool was empty, and can + * occasionally return NULL if there was a race with another thread + * stealing from the same pool. In this case, try again later. * -------------------------------------------------------------------------- */ -StgClosurePtr steal(SparkPool *deque); - -/* steal an element from the read end. Synchronises multiple callers - by failing with NULL return. Returns NULL when deque is empty. */ -StgClosurePtr steal(SparkPool *deque) { +static StgClosurePtr +steal(SparkPool *deque) +{ StgClosurePtr* pos; StgClosurePtr* arraybase; StgWord sz; StgClosurePtr stolen; StgWord b,t; - ASSERT_SPARK_POOL_INVARIANTS(deque); +// Can't do this on someone else's spark pool: +// ASSERT_SPARK_POOL_INVARIANTS(deque); b = deque->bottom; t = deque->top; - if (b - t <= 0 ) { + + // NB. b and t are unsigned; we need a signed value for the test + // below. + if ((long)b - (long)t <= 0 ) { return NULL; /* already looks empty, abort */ } @@ -231,47 +222,46 @@ StgClosurePtr steal(SparkPool *deque) { /* now decide whether we have won */ if ( !(CASTOP(&(deque->top),t,t+1)) ) { - /* lost the race, someon else has changed top in the meantime */ - stolen = NULL; + /* lost the race, someon else has changed top in the meantime */ + return NULL; } /* else: OK, top has been incremented by the cas call */ +// Can't do this on someone else's spark pool: +// ASSERT_SPARK_POOL_INVARIANTS(deque); - ASSERT_SPARK_POOL_INVARIANTS(deque); - /* return NULL or stolen element */ + /* return stolen element */ return stolen; } StgClosure * -findSpark (Capability *cap) +tryStealSpark (Capability *cap) { - SparkPool *deque = (cap->sparks); + SparkPool *pool = cap->sparks; StgClosure *stolen; - ASSERT_SPARK_POOL_INVARIANTS(deque); - do { - /* keep trying until good spark found or pool looks empty. - TODO is this a good idea? */ - - stolen = steal(deque); - - } while ( ( !stolen /* nothing stolen*/ - || !closure_SHOULD_SPARK(stolen)) /* spark not OK */ - && !looksEmpty(deque)); /* run empty, give up */ + stolen = steal(pool); + } while (stolen != NULL && !closure_SHOULD_SPARK(stolen)); - /* return stolen element */ return stolen; } -/* "guesses" whether a deque is empty. Can return false negatives in - presence of concurrent steal() calls, and false positives in - presence of a concurrent pushBottom().*/ -rtsBool looksEmpty(SparkPool* deque) { +/* ----------------------------------------------------------------------------- + * + * "guesses" whether a deque is empty. Can return false negatives in + * presence of concurrent steal() calls, and false positives in + * presence of a concurrent pushBottom(). + * + * -------------------------------------------------------------------------- */ + +rtsBool +looksEmpty(SparkPool* deque) +{ StgWord t = deque->top; StgWord b = deque->bottom; /* try to prefer false negatives by reading top first */ - return (b - t <= 0); + return ((long)b - (long)t <= 0); /* => array is *never* completely filled, always 1 place free! */ } @@ -282,11 +272,12 @@ rtsBool looksEmpty(SparkPool* deque) { * -------------------------------------------------------------------------- */ void -createSparkThread (Capability *cap, StgClosure *p) +createSparkThread (Capability *cap) { StgTSO *tso; - tso = createGenThread (cap, RtsFlags.GcFlags.initialStkSize, p); + tso = createIOThread (cap, RtsFlags.GcFlags.initialStkSize, + &base_GHCziConc_runSparks_closure); appendToRunQueue(cap,tso); } @@ -297,11 +288,12 @@ createSparkThread (Capability *cap, StgClosure *p) * -------------------------------------------------------------------------- */ #define DISCARD_NEW -void pushBottom(SparkPool* deque, StgClosurePtr elem); /* enqueue an element. Should always succeed by resizing the array (not implemented yet, silently fails in that case). */ -void pushBottom(SparkPool* deque, StgClosurePtr elem) { +static void +pushBottom (SparkPool* deque, StgClosurePtr elem) +{ StgWord t; StgClosurePtr* pos; StgWord sz = deque->moduloSize; @@ -314,7 +306,10 @@ void pushBottom(SparkPool* deque, StgClosurePtr elem) { This is why we do not just call empty(deque) here. */ t = deque->topBound; - if ( b - t >= sz ) { /* nota bene: sz == deque->size - 1, thus ">=" */ + if ( (StgInt)b - (StgInt)t >= (StgInt)sz ) { + /* NB. 1. sz == deque->size - 1, thus ">=" + 2. signed comparison, it is possible that t > b + */ /* could be full, check the real top value in this case */ t = deque->top; deque->topBound = t; @@ -349,12 +344,16 @@ void pushBottom(SparkPool* deque, StgClosurePtr elem) { } -/* this is called as a direct C-call from Stg => we need to keep the - pool in a register (???) */ +/* -------------------------------------------------------------------------- + * newSpark: create a new spark, as a result of calling "par" + * Called directly from STG. + * -------------------------------------------------------------------------- */ + StgInt newSpark (StgRegTable *reg, StgClosure *p) { - SparkPool *pool = (reg->rCurrentTSO->cap->sparks); + Capability *cap = regTableToCapability(reg); + SparkPool *pool = cap->sparks; /* I am not sure whether this is the right thing to do. * Maybe it is better to exploit the tag information @@ -368,6 +367,8 @@ newSpark (StgRegTable *reg, StgClosure *p) pushBottom(pool,p); } + cap->sparks_created++; + ASSERT_SPARK_POOL_INVARIANTS(pool); return 1; } @@ -381,13 +382,14 @@ newSpark (StgRegTable *reg, StgClosure *p) * the spark pool only contains sparkable closures. * -------------------------------------------------------------------------- */ -static void -pruneSparkQueue (Capability *cap) +void +pruneSparkQueue (evac_fn evac, void *user, Capability *cap) { SparkPool *pool; - StgClosurePtr spark, evacspark, *elements; + StgClosurePtr spark, tmp, *elements; nat n, pruned_sparks; // stats only StgWord botInd,oldBotInd,currInd; // indices in array (always < size) + const StgInfoTable *info; PAR_TICKY_MARK_SPARK_QUEUE_START(); @@ -396,6 +398,19 @@ pruneSparkQueue (Capability *cap) pool = cap->sparks; + // it is possible that top > bottom, indicating an empty pool. We + // fix that here; this is only necessary because the loop below + // assumes it. + if (pool->top > pool->bottom) + pool->top = pool->bottom; + + // Take this opportunity to reset top/bottom modulo the size of + // the array, to avoid overflow. This is only possible because no + // stealing is happening during GC. + pool->bottom -= pool->top & ~pool->moduloSize; + pool->top &= pool->moduloSize; + pool->topBound = pool->top; + debugTrace(DEBUG_sched, "markSparkQueue: current spark queue len=%d; (hd=%ld; tl=%ld)", sparkPoolSize(pool), pool->bottom, pool->top); @@ -450,13 +465,31 @@ pruneSparkQueue (Capability *cap) botInd, otherwise move on */ spark = elements[currInd]; - /* if valuable work: shift inside the pool */ - if ( closure_SHOULD_SPARK(spark) ) { - elements[botInd] = spark; // keep entry (new address) - botInd++; - n++; - } else { - pruned_sparks++; // discard spark + // We have to be careful here: in the parallel GC, another + // thread might evacuate this closure while we're looking at it, + // so grab the info pointer just once. + info = spark->header.info; + if (IS_FORWARDING_PTR(info)) { + tmp = (StgClosure*)UN_FORWARDING_PTR(info); + /* if valuable work: shift inside the pool */ + if (closure_SHOULD_SPARK(tmp)) { + elements[botInd] = tmp; // keep entry (new address) + botInd++; + n++; + } else { + pruned_sparks++; // discard spark + cap->sparks_pruned++; + } + } else { + if (!(closure_flags[INFO_PTR_TO_STRUCT(info)->type] & _NS)) { + elements[botInd] = spark; // keep entry (new address) + evac (user, &elements[botInd]); + botInd++; + n++; + } else { + pruned_sparks++; // discard spark + cap->sparks_pruned++; + } } currInd++; @@ -486,15 +519,6 @@ pruneSparkQueue (Capability *cap) ASSERT_SPARK_POOL_INVARIANTS(pool); } -void -pruneSparkQueues (void) -{ - nat i; - for (i = 0; i < n_capabilities; i++) { - pruneSparkQueue(&capabilities[i]); - } -} - /* GC for the spark pool, called inside Capability.c for all capabilities in turn. Blindly "evac"s complete spark pool. */ void @@ -528,7 +552,6 @@ traverseSparkQueue (evac_fn evac, void *user, Capability *cap) } /* ---------------------------------------------------------------------------- - * balanceSparkPoolsCaps: takes an array of capabilities (usually: all * capabilities) and its size. Accesses all spark pools and equally * distributes the sparks among them. @@ -537,7 +560,8 @@ traverseSparkQueue (evac_fn evac, void *user, Capability *cap) * -------------------------------------------------------------------------- */ void balanceSparkPoolsCaps(nat n_caps, Capability caps[]); -void balanceSparkPoolsCaps(nat n_caps, Capability caps[]) { +void balanceSparkPoolsCaps(nat n_caps STG_UNUSED, + Capability caps[] STG_UNUSED) { barf("not implemented"); }