#include "RtsUtils.h"
#include "ParTicky.h"
#include "Trace.h"
+#include "Prelude.h"
#include "SMP.h" // for cas
/* 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 */
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)))
/* -----------------------------------------------------------------------------
* -------------------------------------------------------------------------- */
/* constructor */
-SparkPool* initPool(StgWord size) {
-
+static SparkPool*
+initPool(StgWord size)
+{
StgWord realsize;
SparkPool *q;
}
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.
*
* 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;
/* -----------------------------------------------------------------------------
*
- * 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 */
}
/* 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! */
}
* -------------------------------------------------------------------------- */
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);
}
* -------------------------------------------------------------------------- */
#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;
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;
}
-/* 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
pushBottom(pool,p);
}
+ cap->sparks_created++;
+
ASSERT_SPARK_POOL_INVARIANTS(pool);
return 1;
}
* 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();
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);
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++;
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
}
/* ----------------------------------------------------------------------------
-
* balanceSparkPoolsCaps: takes an array of capabilities (usually: all
* capabilities) and its size. Accesses all spark pools and equally
* distributes the sparks among them.
* -------------------------------------------------------------------------- */
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");
}