[project @ 2003-10-01 10:49:07 by wolfgang]
[ghc-hetmet.git] / ghc / rts / Capability.c
index 668f435..d748aee 100644 (file)
 #include "OSThreads.h"
 #include "Capability.h"
 #include "Schedule.h"  /* to get at EMPTY_RUN_QUEUE() */
+#include "Signals.h" /* to get at handleSignalsInThisThread() */
 
 #if !defined(SMP)
 Capability MainCapability;     /* for non-SMP, we have one global capability */
 #endif
 
-static nat rts_n_free_capabilities;
+nat rts_n_free_capabilities;
 
 #if defined(RTS_SUPPORTS_THREADS)
 /* returning_worker_cond: when a worker thread returns from executing an
@@ -44,7 +45,7 @@ Condition returning_worker_cond = INIT_COND_VAR;
  * there are one or more worker threads blocked waiting on
  * returning_worker_cond.
  */
-static nat rts_n_waiting_workers = 0;
+nat rts_n_waiting_workers = 0;
 
 /* thread_ready_cond: when signalled, a thread has become runnable for a
  * task to execute.
@@ -53,14 +54,10 @@ static 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 COND_NO_THREADS_READY doesn't hold.
+ * thread_ready_cond is signalled whenever noCapabilities doesn't hold.
  *
  */
 Condition thread_ready_cond = INIT_COND_VAR;
-#if 0
-/* For documentation purposes only */
-#define COND_NO_THREADS_READY() (noCapabilities() || EMPTY_RUN_QUEUE())
-#endif
 
 /*
  * To be able to make an informed decision about whether or not 
@@ -81,10 +78,8 @@ static
 void
 initCapability( Capability *cap )
 {
-    cap->f.stgChk0         = (F_)__stg_chk_0;
-    cap->f.stgChk1         = (F_)__stg_chk_1;
     cap->f.stgGCEnter1     = (F_)__stg_gc_enter_1;
-    cap->f.stgUpdatePAP    = (F_)__stg_update_PAP;
+    cap->f.stgGCFun        = (F_)__stg_gc_fun;
 }
 
 #if defined(SMP)
@@ -121,6 +116,8 @@ initCapabilities()
 #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
 
 /* -----------------------------------------------------------------------------
@@ -140,14 +137,23 @@ static Capability *free_capabilities; /* Available capabilities for running thre
  */ 
 void grabCapability(Capability** cap)
 {
+#ifdef RTS_SUPPORTS_THREADS
+  ASSERT(rts_n_free_capabilities > 0);
+#endif
 #if !defined(SMP)
   rts_n_free_capabilities = 0;
   *cap = &MainCapability;
+  handleSignalsInThisThread();
 #else
   *cap = free_capabilities;
   free_capabilities = (*cap)->link;
   rts_n_free_capabilities--;
 #endif
+#ifdef RTS_SUPPORTS_THREADS
+  IF_DEBUG(scheduler,
+          fprintf(stderr,"worker thread (%p): got capability\n",
+                  osThreadId()));
+#endif
 }
 
 /*
@@ -163,16 +169,11 @@ void releaseCapability(Capability* cap
                       STG_UNUSED
 #endif
 )
-{
-#if defined(SMP)
-  cap->link = free_capabilities;
-  free_capabilities = cap;
-  rts_n_free_capabilities++;
-#else
-  rts_n_free_capabilities = 1;
-#endif
-
+{      // Precondition: sched_mutex must be held
 #if defined(RTS_SUPPORTS_THREADS)
+#ifndef SMP
+  ASSERT(rts_n_free_capabilities == 0);
+#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) {
@@ -180,14 +181,34 @@ void releaseCapability(Capability* cap
      * 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;
+#else
+#endif
     rts_n_waiting_workers--;
     signalCondition(&returning_worker_cond);
-  } else if ( !EMPTY_RUN_QUEUE() ) {
-    /* Signal that work is available */
+  } else /*if ( !EMPTY_RUN_QUEUE() )*/ {
+#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 */
     signalCondition(&thread_ready_cond);
+    startSchedulerTaskIfNecessary();  // if there is more work to be done,
+                                     // we'll need a new thread
   }
 #endif
-  return;
+#ifdef RTS_SUPPORTS_THREADS
+  IF_DEBUG(scheduler,
+          fprintf(stderr,"worker thread (%p): released capability\n",
+                  osThreadId()));
+#endif
+ return;
 }
 
 #if defined(RTS_SUPPORTS_THREADS)
@@ -227,16 +248,26 @@ void
 grabReturnCapability(Mutex* pMutex, Capability** pCap)
 {
   IF_DEBUG(scheduler,
-          fprintf(stderr,"worker (%ld): returning, waiting for lock.\n", osThreadId()));
-  rts_n_waiting_workers++;
+          fprintf(stderr,"worker (%p): returning, waiting for lock.\n", osThreadId()));
   IF_DEBUG(scheduler,
-          fprintf(stderr,"worker (%ld): returning; workers waiting: %d\n",
+          fprintf(stderr,"worker (%p): returning; workers waiting: %d\n",
                   osThreadId(), rts_n_waiting_workers));
-  while ( noCapabilities() ) {
+  if ( noCapabilities() ) {
+    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;
+#else
+    *pCap = &MainCapability;
+    ASSERT(rts_n_free_capabilities == 0);
+    handleSignalsInThisThread();
+#endif
+  } else {
+    grabCapability(pCap);
   }
-  
-  grabCapability(pCap);
   return;
 }
 
@@ -246,34 +277,40 @@ grabReturnCapability(Mutex* pMutex, Capability** pCap)
    -------------------------------------------------------------------------- */
 
 /*
- * Function: yieldToReturningWorker(Mutex*,Capability*)
+ * Function: yieldToReturningWorker(Mutex*,Capability*,Condition*)
  *
  * Purpose:  when, upon entry to the Scheduler, an OS worker thread
  *           spots that one or more threads are blocked waiting for
  *           permission to return back their result, it gives up
- *           its Capability. 
+ *           its Capability.
+ *           Immediately afterwards, it tries to reaquire the Capabilty
+ *           using waitForWorkCapability.
+ *
  *
  * Pre-condition:  pMutex is assumed held and the thread possesses
  *                 a Capability.
- * Post-condition: pMutex isn't held and the Capability has
- *                 been given back.
+ * Post-condition: pMutex is held and the thread possesses
+ *                 a Capability.
  */
 void
-yieldToReturningWorker(Mutex* pMutex, Capability** pCap)
+yieldToReturningWorker(Mutex* pMutex, Capability** pCap, Condition* pThreadCond)
 {
-  if ( rts_n_waiting_workers > 0 && noCapabilities() ) {
+  if ( rts_n_waiting_workers > 0 ) {
     IF_DEBUG(scheduler,
-            fprintf(stderr,"worker thread (%ld): giving up RTS token\n", osThreadId()));
+            fprintf(stderr,"worker thread (%p): giving up RTS token\n", osThreadId()));
     releaseCapability(*pCap);
-    /* And wait for work */
-    waitForWorkCapability(pMutex, pCap, rtsFalse);
+        /* And wait for work */
+    waitForWorkCapability(pMutex, pCap, pThreadCond);
+    IF_DEBUG(scheduler,
+            fprintf(stderr,"worker thread (%p): got back RTS token (after yieldToReturningWorker)\n",
+               osThreadId()));
   }
   return;
 }
 
 
 /*
- * Function: waitForWorkCapability(Mutex*, Capability**, rtsBool)
+ * Function: waitForWorkCapability(Mutex*, Capability**, Condition*)
  *
  * Purpose:  wait for a Capability to become available. In
  *           the process of doing so, updates the number
@@ -281,20 +318,103 @@ yieldToReturningWorker(Mutex* pMutex, Capability** pCap)
  *           work. That counter is used when deciding whether or
  *           not to create a new worker thread when an external
  *           call is made.
+ *           If pThreadCond is not NULL, a capability can be specifically
+ *           passed to this thread using passCapability.
  *
  * Pre-condition: pMutex is held.
+ * Post-condition: pMutex is held and *pCap is held by the current thread
  */
+static Condition *passTarget = NULL;
+static rtsBool passingCapability = rtsFalse;
 void 
-waitForWorkCapability(Mutex* pMutex, Capability** pCap, rtsBool runnable)
+waitForWorkCapability(Mutex* pMutex, Capability** pCap, Condition* pThreadCond)
 {
-  while ( noCapabilities() || (runnable && EMPTY_RUN_QUEUE()) ) {
-    rts_n_waiting_tasks++;
-    waitCondition(&thread_ready_cond, pMutex);
-    rts_n_waiting_tasks--;
+#ifdef SMP
+  #error SMP version not implemented
+#endif
+  IF_DEBUG(scheduler,
+          fprintf(stderr,"worker thread (%p): wait for cap (cond: %p)\n",
+             osThreadId(),pThreadCond));
+  while ( noCapabilities() || (passingCapability && passTarget != pThreadCond)) {
+    if(pThreadCond)
+    {
+      waitCondition(pThreadCond, pMutex);
+      IF_DEBUG(scheduler,
+              fprintf(stderr,"worker thread (%p): get passed capability\n",
+                 osThreadId()));
+    }
+    else
+    {
+      rts_n_waiting_tasks++;
+      waitCondition(&thread_ready_cond, pMutex);
+      rts_n_waiting_tasks--;
+      IF_DEBUG(scheduler,
+              fprintf(stderr,"worker thread (%p): get normal capability\n",
+                 osThreadId()));
+    }
   }
+  passingCapability = rtsFalse;
   grabCapability(pCap);
   return;
 }
+
+/*
+ * Function: passCapability(Mutex*, Capability*, Condition*)
+ *
+ * Purpose:  Let go of the capability and make sure the thread associated
+ *          with the Condition pTargetThreadCond gets it next.
+ *
+ * Pre-condition: pMutex is held and cap is held by the current thread
+ * Post-condition: pMutex is held; cap will be grabbed by the "target"
+ *                thread when pMutex is released.
+ */
+
+void
+passCapability(Mutex* pMutex, Capability* cap, Condition *pTargetThreadCond)
+{
+#ifdef SMP
+  #error SMP version not implemented
+#endif
+    rts_n_free_capabilities = 1;
+    signalCondition(pTargetThreadCond);
+    passTarget = pTargetThreadCond;
+       passingCapability = rtsTrue;
+    IF_DEBUG(scheduler,
+            fprintf(stderr,"worker thread (%p): passCapability\n",
+               osThreadId()));
+}
+
+/*
+ * Function: passCapabilityToWorker(Mutex*, Capability*)
+ *
+ * Purpose:  Let go of the capability and make sure that a
+ *          "plain" worker thread (not a bound thread) gets it next.
+ *
+ * Pre-condition: pMutex is held and cap is held by the current thread
+ * Post-condition: pMutex is held; cap will be grabbed by the "target"
+ *                thread when pMutex is released.
+ */
+
+void
+passCapabilityToWorker(Mutex* pMutex, Capability* cap)
+{
+#ifdef SMP
+  #error SMP version not implemented
+#endif
+    rts_n_free_capabilities = 1;
+    signalCondition(&thread_ready_cond);
+    startSchedulerTaskIfNecessary();
+    passTarget = NULL;
+    passingCapability = rtsTrue;
+    IF_DEBUG(scheduler,
+            fprintf(stderr,"worker thread (%p): passCapabilityToWorker\n",
+               osThreadId()));
+}
+
+
+
 #endif /* RTS_SUPPORTS_THREADS */
 
 #if defined(SMP)
@@ -321,6 +441,7 @@ initCapabilities_(nat n)
   }
   free_capabilities = cap;
   rts_n_free_capabilities = n;
+  returning_capabilities = NULL;
   IF_DEBUG(scheduler,fprintf(stderr,"scheduler: Allocated %d capabilities\n", n_free_capabilities););
 }
 #endif /* SMP */