[project @ 2005-04-06 15:27:06 by simonmar]
[ghc-hetmet.git] / ghc / rts / Capability.c
index 3ea96fe..bdb651d 100644 (file)
 Capability MainCapability;     /* for non-SMP, we have one global capability */
 #endif
 
-#if defined(RTS_SUPPORTS_THREADS)
-
 nat rts_n_free_capabilities;
 
+#if defined(RTS_SUPPORTS_THREADS)
+
 /* returning_worker_cond: when a worker thread returns from executing an
  * external call, it needs to wait for an RTS Capability before passing
  * on the result of the call to the Haskell thread that made it.
@@ -56,8 +56,8 @@ nat rts_n_waiting_workers = 0;
  * exclusive access to the RTS and all its data structures (that are not
  * locked by the Scheduler's mutex).
  *
- * thread_ready_cond is signalled whenever noCapabilities doesn't hold.
- *
+ * thread_ready_cond is signalled whenever
+ *      !noCapabilities && !EMPTY_RUN_QUEUE().
  */
 Condition thread_ready_cond = INIT_COND_VAR;
 
@@ -76,12 +76,25 @@ static Condition *passTarget = NULL;
 static rtsBool passingCapability = rtsFalse;
 #endif
 
+#if defined(SMP)
+/*
+ * Free capability list. 
+ */
+Capability *free_capabilities;
+#endif
+
 #ifdef SMP
 #define UNUSED_IF_NOT_SMP
 #else
 #define UNUSED_IF_NOT_SMP STG_UNUSED
 #endif
 
+#if defined(RTS_USER_SIGNALS)
+#define ANY_WORK_TO_DO() (!EMPTY_RUN_QUEUE() || interrupted || blackholes_need_checking || signals_pending())
+#else
+#define ANY_WORK_TO_DO() (!EMPTY_RUN_QUEUE() || interrupted || blackholes_need_checking)
+#endif
+
 /* ----------------------------------------------------------------------------
    Initialisation
    ------------------------------------------------------------------------- */
@@ -93,9 +106,34 @@ initCapability( Capability *cap )
     cap->f.stgGCFun        = (F_)__stg_gc_fun;
 }
 
+/* -----------------------------------------------------------------------------
+ * Function: initCapabilities_(nat)
+ *
+ * Purpose:  upon startup, allocate and fill in table
+ *           holding 'n' Capabilities. Only for SMP, since
+ *           it is the only build that supports multiple
+ *           capabilities within the RTS.
+ * -------------------------------------------------------------------------- */
 #if defined(SMP)
-static void initCapabilities_(nat n);
-#endif
+static void
+initCapabilities_(nat n)
+{
+  nat i;
+  Capability *cap, *prev;
+  cap  = NULL;
+  prev = NULL;
+  for (i = 0; i < n; i++) {
+    cap = stgMallocBytes(sizeof(Capability), "initCapabilities");
+    initCapability(cap);
+    cap->link = prev;
+    prev = cap;
+  }
+  free_capabilities = cap;
+  rts_n_free_capabilities = n;
+  IF_DEBUG(scheduler,
+          sched_belch("allocated %d capabilities", rts_n_free_capabilities));
+}
+#endif /* SMP */
 
 /* ---------------------------------------------------------------------------
  * Function:  initCapabilities()
@@ -117,19 +155,11 @@ initCapabilities( void )
 #if defined(RTS_SUPPORTS_THREADS)
   initCondition(&returning_worker_cond);
   initCondition(&thread_ready_cond);
-  rts_n_free_capabilities = 1;
 #endif
 
-  return;
+  rts_n_free_capabilities = 1;
 }
 
-#if defined(SMP)
-/* Free capability list. */
-static Capability *free_capabilities; /* Available capabilities for running threads */
-static Capability *returning_capabilities; 
-       /* Capabilities being passed to returning worker threads */
-#endif
-
 /* ----------------------------------------------------------------------------
    grabCapability( Capability** )
 
@@ -143,17 +173,18 @@ static
 void
 grabCapability( Capability** cap )
 {
-#if !defined(SMP)
-#if defined(RTS_SUPPORTS_THREADS)
+#if defined(SMP)
+  ASSERT(rts_n_free_capabilities > 0);
+  *cap = free_capabilities;
+  free_capabilities = (*cap)->link;
+  rts_n_free_capabilities--;
+#else
+# if defined(RTS_SUPPORTS_THREADS)
   ASSERT(rts_n_free_capabilities == 1);
   rts_n_free_capabilities = 0;
-#endif
+# endif
   *cap = &MainCapability;
   handleSignalsInThisThread();
-#else
-  *cap = free_capabilities;
-  free_capabilities = (*cap)->link;
-  rts_n_free_capabilities--;
 #endif
 #if defined(RTS_SUPPORTS_THREADS)
   IF_DEBUG(scheduler, sched_belch("worker: got capability"));
@@ -173,9 +204,13 @@ releaseCapability( Capability* cap UNUSED_IF_NOT_SMP )
 {
     // Precondition: sched_mutex is held.
 #if defined(RTS_SUPPORTS_THREADS)
-#ifndef SMP
+#if !defined(SMP)
     ASSERT(rts_n_free_capabilities == 0);
 #endif
+#if defined(SMP)
+    cap->link = free_capabilities;
+    free_capabilities = cap;
+#endif
     // Check to see whether a worker thread can be given
     // the go-ahead to return the result of an external call..
     if (rts_n_waiting_workers > 0) {
@@ -183,12 +218,6 @@ releaseCapability( Capability* cap UNUSED_IF_NOT_SMP )
        // thread that is yielding its capability will repeatedly
        // signal returning_worker_cond.
 
-#if defined(SMP)
-       // SMP variant untested
-       cap->link = returning_capabilities;
-       returning_capabilities = cap;
-#endif
-
        rts_n_waiting_workers--;
        signalCondition(&returning_worker_cond);
        IF_DEBUG(scheduler, sched_belch("worker: released capability to returning worker"));
@@ -199,19 +228,21 @@ releaseCapability( Capability* cap UNUSED_IF_NOT_SMP )
        } else {
            signalCondition(passTarget);
        }
+#if defined(SMP)
+       rts_n_free_capabilities++;
+#else
        rts_n_free_capabilities = 1;
+#endif
        IF_DEBUG(scheduler, sched_belch("worker: released capability, passing it"));
 
     } else {
 #if defined(SMP)
-       cap->link = free_capabilities;
-       free_capabilities = cap;
        rts_n_free_capabilities++;
 #else
        rts_n_free_capabilities = 1;
 #endif
        // Signal that a capability is available
-       if (rts_n_waiting_tasks > 0) {
+       if (rts_n_waiting_tasks > 0 && ANY_WORK_TO_DO()) {
            signalCondition(&thread_ready_cond);
        }
        startSchedulerTaskIfNecessary();
@@ -263,17 +294,17 @@ waitForReturnCapability( Mutex* pMutex, Capability** pCap )
 
     if ( noCapabilities() || passingCapability ) {
        rts_n_waiting_workers++;
-       wakeBlockedWorkerThread();
        context_switch = 1;     // make sure it's our turn soon
        waitCondition(&returning_worker_cond, pMutex);
 #if defined(SMP)
-       *pCap = returning_capabilities;
-       returning_capabilities = (*pCap)->link;
+       *pCap = free_capabilities;
+       free_capabilities = (*pCap)->link;
+       ASSERT(pCap != NULL);
 #else
        *pCap = &MainCapability;
        ASSERT(rts_n_free_capabilities == 0);
-       handleSignalsInThisThread();
 #endif
+       handleSignalsInThisThread();
     } else {
        grabCapability(pCap);
     }
@@ -294,13 +325,21 @@ yieldCapability( Capability** pCap )
     // Pre-condition:  pMutex is assumed held, the current thread
     // holds the capability pointed to by pCap.
 
-    if ( rts_n_waiting_workers > 0 || passingCapability ) {
-       IF_DEBUG(scheduler, sched_belch("worker: giving up capability"));
+    if ( rts_n_waiting_workers > 0 || passingCapability || !ANY_WORK_TO_DO()) {
+       IF_DEBUG(scheduler, 
+                if (rts_n_waiting_workers > 0) {
+                    sched_belch("worker: giving up capability (returning wkr)");
+                } else if (passingCapability) {
+                    sched_belch("worker: giving up capability (passing capability)");
+                } else {
+                    sched_belch("worker: giving up capability (no threads to run)");
+                }
+           );
        releaseCapability(*pCap);
        *pCap = NULL;
     }
 
-    // Post-condition:  pMutex is assumed held, and either:
+    // Post-condition:  either:
     //
     //  1. *pCap is NULL, in which case the current thread does not
     //     hold a capability now, or
@@ -324,13 +363,14 @@ yieldCapability( Capability** pCap )
  *           passed to this thread using passCapability.
  * ------------------------------------------------------------------------- */
  
-void 
+void
 waitForCapability( Mutex* pMutex, Capability** pCap, Condition* pThreadCond )
 {
     // Pre-condition: pMutex is held.
 
-    while ( noCapabilities() || 
-           (passingCapability && passTarget != pThreadCond)) {
+    while ( noCapabilities() ||
+           (passingCapability && passTarget != pThreadCond) ||
+           !ANY_WORK_TO_DO()) {
        IF_DEBUG(scheduler,
                 sched_belch("worker: wait for capability (cond: %p)",
                             pThreadCond));
@@ -384,35 +424,36 @@ passCapabilityToWorker( void )
 
 #endif /* RTS_SUPPORTS_THREADS */
 
-/* ------------------------------------------------------------------------- */
+/* ----------------------------------------------------------------------------
+   threadRunnable()
 
-#if defined(SMP)
-/*
- * Function: initCapabilities_(nat)
- *
- * Purpose:  upon startup, allocate and fill in table
- *           holding 'n' Capabilities. Only for SMP, since
- *           it is the only build that supports multiple
- *           capabilities within the RTS.
- */
-static void
-initCapabilities_(nat n)
+   Signals that a thread has been placed on the run queue, so a worker
+   might need to be woken up to run it.
+
+   ToDo: should check whether the thread at the front of the queue is
+   bound, and if so wake up the appropriate worker.
+   -------------------------------------------------------------------------- */
+void
+threadRunnable ( void )
 {
-  nat i;
-  Capability *cap, *prev;
-  cap  = NULL;
-  prev = NULL;
-  for (i = 0; i < n; i++) {
-    cap = stgMallocBytes(sizeof(Capability), "initCapabilities");
-    initCapability(cap);
-    cap->link = prev;
-    prev = cap;
-  }
-  free_capabilities = cap;
-  rts_n_free_capabilities = n;
-  returning_capabilities = NULL;
-  IF_DEBUG(scheduler,
-          sched_belch("allocated %d capabilities", n_free_capabilities));
+#if defined(RTS_SUPPORTS_THREADS)
+    if ( !noCapabilities() && ANY_WORK_TO_DO() && rts_n_waiting_tasks > 0 ) {
+       signalCondition(&thread_ready_cond);
+    }
+    startSchedulerTaskIfNecessary();
+#endif
 }
-#endif /* SMP */
 
+
+/* ----------------------------------------------------------------------------
+   prodWorker()
+
+   Wake up... time to die.
+   -------------------------------------------------------------------------- */
+void
+prodWorker ( void )
+{
+#if defined(RTS_SUPPORTS_THREADS)
+    signalCondition(&thread_ready_cond);
+#endif
+}