[project @ 2005-03-10 14:03:28 by simonmar]
[ghc-hetmet.git] / ghc / rts / Capability.c
index e34f47a..e839a6c 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.
@@ -55,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;
 
@@ -81,6 +82,12 @@ static rtsBool passingCapability = rtsFalse;
 #define UNUSED_IF_NOT_SMP STG_UNUSED
 #endif
 
+#if defined(RTS_USER_SIGNALS)
+#define ANY_WORK_TO_DO() (!EMPTY_RUN_QUEUE() || interrupted || signals_pending())
+#else
+#define ANY_WORK_TO_DO() (!EMPTY_RUN_QUEUE() || interrupted)
+#endif
+
 /* ----------------------------------------------------------------------------
    Initialisation
    ------------------------------------------------------------------------- */
@@ -107,15 +114,15 @@ static void initCapabilities_(nat n);
 void
 initCapabilities( void )
 {
-#if defined(RTS_SUPPORTS_THREADS)
-  initCondition(&returning_worker_cond);
-  initCondition(&thread_ready_cond);
-#endif
-
 #if defined(SMP)
   initCapabilities_(RtsFlags.ParFlags.nNodes);
 #else
   initCapability(&MainCapability);
+#endif
+
+#if defined(RTS_SUPPORTS_THREADS)
+  initCondition(&returning_worker_cond);
+  initCondition(&thread_ready_cond);
   rts_n_free_capabilities = 1;
 #endif
 
@@ -136,12 +143,17 @@ static Capability *returning_capabilities;
    threaded RTS, clients must use waitFor*Capability()).
    ------------------------------------------------------------------------- */
 
+#if defined(RTS_SUPPORTS_THREADS)
+static
+#endif
 void
 grabCapability( Capability** cap )
 {
 #if !defined(SMP)
+#if defined(RTS_SUPPORTS_THREADS)
   ASSERT(rts_n_free_capabilities == 1);
   rts_n_free_capabilities = 0;
+#endif
   *cap = &MainCapability;
   handleSignalsInThisThread();
 #else
@@ -149,7 +161,9 @@ grabCapability( Capability** cap )
   free_capabilities = (*cap)->link;
   rts_n_free_capabilities--;
 #endif
+#if defined(RTS_SUPPORTS_THREADS)
   IF_DEBUG(scheduler, sched_belch("worker: got capability"));
+#endif
 }
 
 /* ----------------------------------------------------------------------------
@@ -203,7 +217,9 @@ releaseCapability( Capability* cap UNUSED_IF_NOT_SMP )
        rts_n_free_capabilities = 1;
 #endif
        // Signal that a capability is available
-       signalCondition(&thread_ready_cond);
+       if (rts_n_waiting_tasks > 0 && ANY_WORK_TO_DO()) {
+           signalCondition(&thread_ready_cond);
+       }
        startSchedulerTaskIfNecessary();
        IF_DEBUG(scheduler, sched_belch("worker: released capability"));
     }
@@ -253,7 +269,6 @@ 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)
@@ -284,8 +299,16 @@ 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;
     }
@@ -314,13 +337,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));
@@ -374,6 +398,27 @@ passCapabilityToWorker( void )
 
 #endif /* RTS_SUPPORTS_THREADS */
 
+/* ----------------------------------------------------------------------------
+   threadRunnable()
+
+   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 )
+{
+#if defined(RTS_SUPPORTS_THREADS)
+    if ( !noCapabilities() && ANY_WORK_TO_DO() && rts_n_waiting_tasks > 0 ) {
+       signalCondition(&thread_ready_cond);
+    }
+    startSchedulerTaskIfNecessary();
+#endif
+}
+
 /* ------------------------------------------------------------------------- */
 
 #if defined(SMP)