Use https links in user-facing startup and error messages
[ghc.git] / rts / Sparks.c
index 0fe8b61..bd5e120 100644 (file)
@@ -2,35 +2,27 @@
  *
  * (c) The GHC Team, 2000-2008
  *
- * Sparking support for PARALLEL_HASKELL and THREADED_RTS versions of the RTS.
+ * Sparking support for THREADED_RTS version of the RTS.
  *
  -------------------------------------------------------------------------*/
 
 #include "PosixSource.h"
 #include "Rts.h"
-#include "Storage.h"
+
 #include "Schedule.h"
-#include "SchedAPI.h"
-#include "RtsFlags.h"
 #include "RtsUtils.h"
-#include "ParTicky.h"
 #include "Trace.h"
 #include "Prelude.h"
-
-#include "SMP.h" // for cas
-
 #include "Sparks.h"
+#include "ThreadLabels.h"
+#include "sm/HeapAlloc.h"
 
 #if defined(THREADED_RTS)
 
-void
-initSparkPools( void )
+SparkPool *
+allocSparkPool( void )
 {
-    /* walk over the capabilities, allocating a spark pool for each one */
-    nat i;
-    for (i = 0; i < n_capabilities; i++) {
-      capabilities[i].sparks = newWSDeque(RtsFlags.ParFlags.maxLocalSparks);
-    }
+    return newWSDeque(RtsFlags.ParFlags.maxLocalSparks);
 }
 
 void
@@ -40,7 +32,7 @@ freeSparkPool (SparkPool *pool)
 }
 
 /* -----------------------------------------------------------------------------
- * 
+ *
  * Turn a spark into a real thread
  *
  * -------------------------------------------------------------------------- */
@@ -50,10 +42,10 @@ createSparkThread (Capability *cap)
 {
     StgTSO *tso;
 
-    tso = createIOThread (cap, RtsFlags.GcFlags.initialStkSize, 
-                          &base_GHCziConc_runSparks_closure);
-
-    postEvent(cap, EVENT_CREATE_SPARK_THREAD, 0, tso->id);
+    tso = createIOThread (cap, RtsFlags.GcFlags.initialStkSize,
+                          (StgClosure *)runSparks_closure);
+    labelThread(cap, tso, "spark evaluator");
+    traceEventCreateSparkThread(cap, tso->id);
 
     appendToRunQueue(cap,tso);
 }
@@ -69,49 +61,23 @@ newSpark (StgRegTable *reg, StgClosure *p)
     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
-     * instead of throwing it away?
-     */
-    p = UNTAG_CLOSURE(p);
-
-    if (closure_SHOULD_SPARK(p)) {
-        pushWSDeque(pool,p);
-    }  
-
-    cap->sparks_created++;
-
-    postEvent(cap, EVENT_CREATE_SPARK, cap->r.rCurrentTSO->id, 0);
+    if (!fizzledSpark(p)) {
+        if (pushWSDeque(pool,p)) {
+            cap->spark_stats.created++;
+            traceEventSparkCreate(cap);
+        } else {
+            /* overflowing the spark pool */
+            cap->spark_stats.overflowed++;
+            traceEventSparkOverflow(cap);
+        }
+    } else {
+        cap->spark_stats.dud++;
+        traceEventSparkDud(cap);
+    }
 
     return 1;
 }
 
-/* -----------------------------------------------------------------------------
- * 
- * tryStealSpark: try to steal a spark from a Capability.
- *
- * 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.
- *
- -------------------------------------------------------------------------- */
-
-StgClosure *
-tryStealSpark (Capability *cap)
-{
-  SparkPool *pool = cap->sparks;
-  StgClosure *stolen;
-
-  do { 
-      stolen = stealWSDeque_(pool); 
-      // use the no-loopy version, stealWSDeque_(), since if we get a
-      // spurious NULL here the caller may want to try stealing from
-      // other pools before trying again.
-  } while (stolen != NULL && !closure_SHOULD_SPARK(stolen));
-
-  return stolen;
-}
-
 /* --------------------------------------------------------------------------
  * Remove all sparks from the spark queues which should not spark any
  * more.  Called after GC. We assume exclusive access to the structure
@@ -120,21 +86,19 @@ tryStealSpark (Capability *cap)
  * -------------------------------------------------------------------------- */
 
 void
-pruneSparkQueue (evac_fn evac, void *user, Capability *cap)
-{ 
+pruneSparkQueue (Capability *cap)
+{
     SparkPool *pool;
     StgClosurePtr spark, tmp, *elements;
-    nat n, pruned_sparks; // stats only
+    uint32_t n, pruned_sparks; // stats only
     StgWord botInd,oldBotInd,currInd; // indices in array (always < size)
     const StgInfoTable *info;
-    
-    PAR_TICKY_MARK_SPARK_QUEUE_START();
-    
+
     n = 0;
     pruned_sparks = 0;
-    
+
     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.
@@ -148,7 +112,7 @@ pruneSparkQueue (evac_fn evac, void *user, Capability *cap)
     pool->top     &= pool->moduloSize;
     pool->topBound = pool->top;
 
-    debugTrace(DEBUG_sched,
+    debugTrace(DEBUG_sparks,
                "markSparkQueue: current spark queue len=%ld; (hd=%ld; tl=%ld)",
                sparkPoolSize(pool), pool->bottom, pool->top);
 
@@ -196,39 +160,72 @@ pruneSparkQueue (evac_fn evac, void *user, Capability *cap)
 
     while (currInd != oldBotInd ) {
       /* must use != here, wrap-around at size
-        subtle: loop not entered if queue empty
+         subtle: loop not entered if queue empty
        */
 
       /* check element at currInd. if valuable, evacuate and move to
-        botInd, otherwise move on */
+         botInd, otherwise move on */
       spark = elements[currInd];
 
       // 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++;
-          }
+      if (GET_CLOSURE_TAG(spark) != 0) {
+          // Tagged pointer is a value, so the spark has fizzled.  It
+          // probably never happens that we get a tagged pointer in
+          // the spark pool, because we would have pruned the spark
+          // during the previous GC cycle if it turned out to be
+          // evaluated, but it doesn't hurt to have this check for
+          // robustness.
+          pruned_sparks++;
+          cap->spark_stats.fizzled++;
+          traceEventSparkFizzle(cap);
       } else {
-          if (!(closure_flags[INFO_PTR_TO_STRUCT(info)->type] & _NS)) {
-              elements[botInd] = spark; // keep entry (new address)
-              evac (user, &elements[botInd]);
-              botInd++;
-              n++;
+          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->spark_stats.fizzled++;
+                  traceEventSparkFizzle(cap);
+              }
+          } else if (HEAP_ALLOCED(spark)) {
+              if ((Bdescr((P_)spark)->flags & BF_EVACUATED)) {
+                  if (closure_SHOULD_SPARK(spark)) {
+                      elements[botInd] = spark; // keep entry (new address)
+                      botInd++;
+                      n++;
+                  } else {
+                      pruned_sparks++; // discard spark
+                      cap->spark_stats.fizzled++;
+                      traceEventSparkFizzle(cap);
+                  }
+              } else {
+                  pruned_sparks++; // discard spark
+                  cap->spark_stats.gcd++;
+                  traceEventSparkGC(cap);
+              }
           } else {
-              pruned_sparks++; // discard spark
-              cap->sparks_pruned++;
+              if (INFO_PTR_TO_STRUCT(info)->type == THUNK_STATIC) {
+                  // We can't tell whether a THUNK_STATIC is garbage or not.
+                  // See also Note [STATIC_LINK fields]
+                  // isAlive() also ignores static closures (see GCAux.c)
+                  elements[botInd] = spark; // keep entry (new address)
+                  botInd++;
+                  n++;
+              } else {
+                  pruned_sparks++; // discard spark
+                  cap->spark_stats.fizzled++;
+                  traceEventSparkFizzle(cap);
+              }
           }
       }
+
       currInd++;
 
       // in the loop, we may reach the bounds, and instantly wrap around
@@ -243,14 +240,12 @@ pruneSparkQueue (evac_fn evac, void *user, Capability *cap)
     pool->top = oldBotInd; // where we started writing
     pool->topBound = pool->top;
 
-    pool->bottom = (oldBotInd <= botInd) ? botInd : (botInd + pool->size); 
+    pool->bottom = (oldBotInd <= botInd) ? botInd : (botInd + pool->size);
     // first free place we did not use (corrected by wraparound)
 
-    PAR_TICKY_MARK_SPARK_QUEUE_END(n);
+    debugTrace(DEBUG_sparks, "pruned %d sparks", pruned_sparks);
 
-    debugTrace(DEBUG_sched, "pruned %d sparks", pruned_sparks);
-    
-    debugTrace(DEBUG_sched,
+    debugTrace(DEBUG_sparks,
                "new spark queue len=%ld; (hd=%ld; tl=%ld)",
                sparkPoolSize(pool), pool->bottom, pool->top);
 
@@ -265,7 +260,7 @@ traverseSparkQueue (evac_fn evac, void *user, Capability *cap)
     StgClosure **sparkp;
     SparkPool *pool;
     StgWord top,bottom, modMask;
-    
+
     pool = cap->sparks;
 
     ASSERT_WSDEQUE_INVARIANTS(pool);
@@ -280,29 +275,15 @@ traverseSparkQueue (evac_fn evac, void *user, Capability *cap)
      * In GHC-6.10, evac takes an additional 1st argument to hold a
      * GC-specific register, see rts/sm/GC.c::mark_root()
      */
-      evac( user , sparkp + (top & modMask) ); 
+      evac( user , sparkp + (top & modMask) );
       top++;
     }
 
-    debugTrace(DEBUG_sched,
+    debugTrace(DEBUG_sparks,
                "traversed spark queue, len=%ld; (hd=%ld; tl=%ld)",
                sparkPoolSize(pool), pool->bottom, pool->top);
 }
 
-/* ----------------------------------------------------------------------------
- * balanceSparkPoolsCaps: takes an array of capabilities (usually: all
- * capabilities) and its size. Accesses all spark pools and equally
- * distributes the sparks among them.
- *
- * Could be called after GC, before Cap. release, from scheduler. 
- * -------------------------------------------------------------------------- */
-void balanceSparkPoolsCaps(nat n_caps, Capability caps[]);
-
-void balanceSparkPoolsCaps(nat n_caps STG_UNUSED, 
-                           Capability caps[] STG_UNUSED) {
-  barf("not implemented");
-}
-
 #else
 
 StgInt