[project @ 2003-12-15 16:43:45 by simonmar]
[ghc-hetmet.git] / ghc / rts / Schedule.c
index 4bbe486..c1c9299 100644 (file)
@@ -1,5 +1,5 @@
 /* ---------------------------------------------------------------------------
- * $Id: Schedule.c,v 1.167 2003/04/02 00:28:59 sof Exp $
+ * $Id: Schedule.c,v 1.182 2003/12/12 16:35:20 simonmar Exp $
  *
  * (c) The GHC Team, 1998-2000
  *
 #include <stdlib.h>
 #include <stdarg.h>
 
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#ifdef THREADED_RTS
+#define USED_IN_THREADED_RTS
+#else
+#define USED_IN_THREADED_RTS STG_UNUSED
+#endif
+
+#ifdef RTS_SUPPORTS_THREADS
+#define USED_WHEN_RTS_SUPPORTS_THREADS
+#else
+#define USED_WHEN_RTS_SUPPORTS_THREADS STG_UNUSED
+#endif
+
 //@node Variables and Data structures, Prototypes, Includes, Main scheduling code
 //@subsection Variables and Data structures
 
  */
 StgMainThread *main_threads = NULL;
 
-#ifdef THREADED_RTS
-// Pointer to the thread that executes main
-// When this thread is finished, the program terminates
-// by calling shutdownHaskellAndExit.
-// It would be better to add a call to shutdownHaskellAndExit
-// to the Main.main wrapper and to remove this hack.
-StgMainThread *main_main_thread = NULL;
-#endif
-
 /* Thread queues.
  * Locks required: sched_mutex.
  */
@@ -217,14 +224,15 @@ static StgThreadID next_thread_id = 1;
 /* The smallest stack size that makes any sense is:
  *    RESERVED_STACK_WORDS    (so we can get back from the stack overflow)
  *  + sizeofW(StgStopFrame)   (the stg_stop_thread_info frame)
- *  + 1                       (the realworld token for an IO thread)
  *  + 1                       (the closure to enter)
+ *  + 1                              (stg_ap_v_ret)
+ *  + 1                              (spare slot req'd by stg_ap_v_ret)
  *
  * A thread with this stack will bomb immediately with a stack
  * overflow, which will increase its stack size.  
  */
 
-#define MIN_STACK_WORDS (RESERVED_STACK_WORDS + sizeofW(StgStopFrame) + 2)
+#define MIN_STACK_WORDS (RESERVED_STACK_WORDS + sizeofW(StgStopFrame) + 3)
 
 
 #if defined(GRAN)
@@ -248,15 +256,11 @@ static rtsBool shutting_down_scheduler = rtsFalse;
 
 void            addToBlockedQueue ( StgTSO *tso );
 
-static void     schedule          ( void );
+static void     schedule          ( StgMainThread *mainThread, Capability *initialCapability );
        void     interruptStgRts   ( void );
 
 static void     detectBlackHoles  ( void );
 
-#ifdef DEBUG
-static void sched_belch(char *s, ...);
-#endif
-
 #if defined(RTS_SUPPORTS_THREADS)
 /* ToDo: carefully document the invariants that go together
  *       with these synchronisation objects.
@@ -305,20 +309,38 @@ StgTSO * activateSpark (rtsSpark spark);
 StgTSO   *MainTSO;
  */
 
-#if defined(PAR) || defined(RTS_SUPPORTS_THREADS)
+#if defined(RTS_SUPPORTS_THREADS)
+static rtsBool startingWorkerThread = rtsFalse;
+
 static void taskStart(void);
 static void
 taskStart(void)
 {
-  schedule();
+  Capability *cap;
+  
+  ACQUIRE_LOCK(&sched_mutex);
+  startingWorkerThread = rtsFalse;
+  waitForWorkCapability(&sched_mutex, &cap, NULL);
+  RELEASE_LOCK(&sched_mutex);
+  
+  schedule(NULL,cap);
 }
-#endif
 
-#if defined(RTS_SUPPORTS_THREADS)
 void
-startSchedulerTask(void)
+startSchedulerTaskIfNecessary(void)
 {
-    startTask(taskStart);
+  if(run_queue_hd != END_TSO_QUEUE
+    || blocked_queue_hd != END_TSO_QUEUE
+    || sleeping_queue != END_TSO_QUEUE)
+  {
+    if(!startingWorkerThread)
+    { // we don't want to start another worker thread
+      // just because the last one hasn't yet reached the
+      // "waiting for capability" state
+      startingWorkerThread = rtsTrue;
+      startTask(taskStart);
+    }
+  }
 }
 #endif
 
@@ -362,10 +384,11 @@ startSchedulerTask(void)
    ------------------------------------------------------------------------ */
 //@cindex schedule
 static void
-schedule( void )
+schedule( StgMainThread *mainThread USED_WHEN_RTS_SUPPORTS_THREADS,
+          Capability *initialCapability )
 {
   StgTSO *t;
-  Capability *cap;
+  Capability *cap = initialCapability;
   StgThreadReturnCode ret;
 #if defined(GRAN)
   rtsEvent *event;
@@ -385,8 +408,15 @@ schedule( void )
   ACQUIRE_LOCK(&sched_mutex);
  
 #if defined(RTS_SUPPORTS_THREADS)
-  waitForWorkCapability(&sched_mutex, &cap, rtsFalse);
-  IF_DEBUG(scheduler, sched_belch("worker thread (osthread %p): entering RTS", osThreadId()));
+  //
+  // in the threaded case, the capability is either passed in via the
+  // initialCapability parameter, or initialized inside the scheduler
+  // loop 
+  //
+  IF_DEBUG(scheduler,
+          sched_belch("### NEW SCHEDULER LOOP (main thr: %p, cap: %p)",
+                      mainThread, initialCapability);
+      );
 #else
   /* simply initialise it in the non-threaded case */
   grabCapability(&cap);
@@ -419,98 +449,124 @@ schedule( void )
 
   while (!receivedFinish) {    /* set by processMessages */
                                /* when receiving PP_FINISH message         */ 
-#else
+
+#else // everything except GRAN and PAR
 
   while (1) {
 
 #endif
 
-    IF_DEBUG(scheduler, printAllThreads());
+     IF_DEBUG(scheduler, printAllThreads());
 
 #if defined(RTS_SUPPORTS_THREADS)
-    /* Check to see whether there are any worker threads
-       waiting to deposit external call results. If so,
-       yield our capability */
-    yieldToReturningWorker(&sched_mutex, &cap);
+    //
+    // Check to see whether there are any worker threads
+    // waiting to deposit external call results. If so,
+    // yield our capability... if we have a capability, that is.
+    //
+    if (cap != NULL) {
+       yieldToReturningWorker(&sched_mutex, &cap,
+                              mainThread ? &mainThread->bound_thread_cond
+                                         : NULL);
+    }
+
+    // If we do not currently hold a capability, we wait for one
+    if (cap == NULL) {
+       waitForWorkCapability(&sched_mutex, &cap,
+                             mainThread ? &mainThread->bound_thread_cond
+                                        : NULL);
+    }
 #endif
 
-    /* If we're interrupted (the user pressed ^C, or some other
-     * termination condition occurred), kill all the currently running
-     * threads.
-     */
+    //
+    // If we're interrupted (the user pressed ^C, or some other
+    // termination condition occurred), kill all the currently running
+    // threads.
+    //
     if (interrupted) {
-      IF_DEBUG(scheduler, sched_belch("interrupted"));
-      interrupted = rtsFalse;
-      was_interrupted = rtsTrue;
+       IF_DEBUG(scheduler, sched_belch("interrupted"));
+       interrupted = rtsFalse;
+       was_interrupted = rtsTrue;
 #if defined(RTS_SUPPORTS_THREADS)
-      // In the threaded RTS, deadlock detection doesn't work,
-      // so just exit right away.
-      prog_belch("interrupted");
-      releaseCapability(cap);
-      startTask(taskStart);    // thread-safe-call to shutdownHaskellAndExit
-      RELEASE_LOCK(&sched_mutex);
-      shutdownHaskellAndExit(EXIT_SUCCESS);
+       // In the threaded RTS, deadlock detection doesn't work,
+       // so just exit right away.
+       prog_belch("interrupted");
+       releaseCapability(cap);
+       RELEASE_LOCK(&sched_mutex);
+       shutdownHaskellAndExit(EXIT_SUCCESS);
 #else
-      deleteAllThreads();
+       deleteAllThreads();
 #endif
     }
 
-    /* Go through the list of main threads and wake up any
-     * clients whose computations have finished.  ToDo: this
-     * should be done more efficiently without a linear scan
-     * of the main threads list, somehow...
-     */
+    //
+    // Go through the list of main threads and wake up any
+    // clients whose computations have finished.  ToDo: this
+    // should be done more efficiently without a linear scan
+    // of the main threads list, somehow...
+    //
 #if defined(RTS_SUPPORTS_THREADS)
     { 
-      StgMainThread *m, **prev;
-      prev = &main_threads;
-      for (m = main_threads; m != NULL; prev = &m->link, m = m->link) {
-       switch (m->tso->what_next) {
-       case ThreadComplete:
-         if (m->ret) {
-              // NOTE: return val is tso->sp[1] (see StgStartup.hc)
-             *(m->ret) = (StgClosure *)m->tso->sp[1]; 
-         }
-         *prev = m->link;
-         m->stat = Success;
-         broadcastCondition(&m->wakeup);
-#ifdef DEBUG
-         removeThreadLabel((StgWord)m->tso);
-#endif
-          if(m == main_main_thread)
-          {
-              releaseCapability(cap);
-              startTask(taskStart);    // thread-safe-call to shutdownHaskellAndExit
-              RELEASE_LOCK(&sched_mutex);
-              shutdownHaskellAndExit(EXIT_SUCCESS);
-          }
-         break;
-       case ThreadKilled:
-         if (m->ret) *(m->ret) = NULL;
-         *prev = m->link;
-         if (was_interrupted) {
-           m->stat = Interrupted;
-         } else {
-           m->stat = Killed;
-         }
-         broadcastCondition(&m->wakeup);
+       StgMainThread *m, **prev;
+       prev = &main_threads;
+       for (m = main_threads; m != NULL; prev = &m->link, m = m->link) {
+         if (m->tso->what_next == ThreadComplete
+             || m->tso->what_next == ThreadKilled)
+         {
+           if (m == mainThread)
+           {
+              if (m->tso->what_next == ThreadComplete)
+              {
+                if (m->ret)
+                {
+                  // NOTE: return val is tso->sp[1] (see StgStartup.hc)
+                  *(m->ret) = (StgClosure *)m->tso->sp[1]; 
+                }
+                m->stat = Success;
+              }
+              else
+              {
+                if (m->ret)
+                {
+                  *(m->ret) = NULL;
+                }
+                if (was_interrupted)
+                {
+                  m->stat = Interrupted;
+                }
+                else
+                {
+                  m->stat = Killed;
+                }
+              }
+              *prev = m->link;
+           
 #ifdef DEBUG
-         removeThreadLabel((StgWord)m->tso);
+             removeThreadLabel((StgWord)m->tso->id);
 #endif
-          if(m == main_main_thread)
-          {
               releaseCapability(cap);
-              startTask(taskStart);    // thread-safe-call to shutdownHaskellAndExit
               RELEASE_LOCK(&sched_mutex);
-              shutdownHaskellAndExit(EXIT_SUCCESS);
+              return;
+            }
+            else
+            {
+                // The current OS thread can not handle the fact that
+                // the Haskell thread "m" has ended.  "m" is bound;
+                // the scheduler loop in it's bound OS thread has to
+                // return, so let's pass our capability directly to
+                // that thread.
+               passCapability(&sched_mutex, cap, &m->bound_thread_cond);
+               cap = NULL;
+            }
           }
-         break;
-       default:
-         break;
        }
-      }
     }
-
+    
+    // If we gave our capability away, go to the top to get it back
+    if (cap == NULL) {
+       continue;       
+    }
+      
 #else /* not threaded */
 
 # if defined(PAR)
@@ -524,7 +580,7 @@ schedule( void )
       if (m->tso->what_next == ThreadComplete
          || m->tso->what_next == ThreadKilled) {
 #ifdef DEBUG
-       removeThreadLabel((StgWord)m->tso);
+       removeThreadLabel((StgWord)m->tso->id);
 #endif
        main_threads = main_threads->link;
        if (m->tso->what_next == ThreadComplete) {
@@ -546,6 +602,8 @@ schedule( void )
     }
 #endif
 
+
+#if 0 /* defined(SMP) */
     /* Top up the run queue from our spark pool.  We try to make the
      * number of threads in the run queue equal to the number of
      * free capabilities.
@@ -553,7 +611,6 @@ schedule( void )
      * Disable spark support in SMP for now, non-essential & requires
      * a little bit of work to make it compile cleanly. -- sof 1/02.
      */
-#if 0 /* defined(SMP) */
     {
       nat n = getFreeCapabilities();
       StgTSO *tso = run_queue_hd;
@@ -588,8 +645,8 @@ schedule( void )
     }
 #endif // SMP
 
-    /* check for signals each time around the scheduler */
 #if defined(RTS_USER_SIGNALS)
+    // check for signals each time around the scheduler
     if (signals_pending()) {
       RELEASE_LOCK(&sched_mutex); /* ToDo: kill */
       startSignalHandlers();
@@ -773,9 +830,9 @@ schedule( void )
        RELEASE_LOCK(&sched_mutex);
        return;
       }
-      IF_DEBUG(scheduler, sched_belch("thread %d: waiting for work", osThreadId()));
+      IF_DEBUG(scheduler, sched_belch("waiting for work"));
       waitForWorkCapability(&sched_mutex, &cap, rtsTrue);
-      IF_DEBUG(scheduler, sched_belch("thread %d: work now available", osThreadId()));
+      IF_DEBUG(scheduler, sched_belch("work now available"));
     }
 #else
     if ( EMPTY_RUN_QUEUE() ) {
@@ -907,7 +964,7 @@ schedule( void )
     /* in a GranSim setup the TSO stays on the run queue */
     t = CurrentTSO;
     /* Take a thread from the run queue. */
-    t = POP_RUN_QUEUE(); // take_off_run_queue(t);
+    POP_RUN_QUEUE(t); // take_off_run_queue(t);
 
     IF_DEBUG(gran, 
             fprintf(stderr, "GRAN: About to run current thread, which is\n");
@@ -1014,7 +1071,7 @@ schedule( void )
     ASSERT(run_queue_hd != END_TSO_QUEUE);
 
     /* Take a thread from the run queue, if we have work */
-    t = POP_RUN_QUEUE();  // take_off_run_queue(END_TSO_QUEUE);
+    POP_RUN_QUEUE(t);  // take_off_run_queue(END_TSO_QUEUE);
     IF_DEBUG(sanity,checkTSO(t));
 
     /* ToDo: write something to the log-file
@@ -1058,14 +1115,61 @@ schedule( void )
 # endif
 #else /* !GRAN && !PAR */
   
-    /* grab a thread from the run queue */
+    // grab a thread from the run queue
     ASSERT(run_queue_hd != END_TSO_QUEUE);
-    t = POP_RUN_QUEUE();
+    POP_RUN_QUEUE(t);
+
     // Sanity check the thread we're about to run.  This can be
     // expensive if there is lots of thread switching going on...
     IF_DEBUG(sanity,checkTSO(t));
 #endif
 
+#ifdef THREADED_RTS
+    {
+      StgMainThread *m;
+      for(m = main_threads; m; m = m->link)
+      {
+       if(m->tso == t)
+         break;
+      }
+      
+      if(m)
+      {
+       if(m == mainThread)
+       {
+         IF_DEBUG(scheduler,
+           sched_belch("### Running thread %d in bound thread", t->id));
+         // yes, the Haskell thread is bound to the current native thread
+       }
+       else
+       {
+         IF_DEBUG(scheduler,
+           sched_belch("### thread %d bound to another OS thread", t->id));
+         // no, bound to a different Haskell thread: pass to that thread
+         PUSH_ON_RUN_QUEUE(t);
+         passCapability(&sched_mutex,cap,&m->bound_thread_cond);
+         cap = NULL;
+         continue;
+       }
+      }
+      else
+      {
+       if(mainThread != NULL)
+        // The thread we want to run is bound.
+       {
+         IF_DEBUG(scheduler,
+           sched_belch("### this OS thread cannot run thread %d", t->id));
+         // no, the current native thread is bound to a different
+         // Haskell thread, so pass it to any worker thread
+         PUSH_ON_RUN_QUEUE(t);
+         passCapabilityToWorker(&sched_mutex, cap);
+         cap = NULL;
+         continue; 
+       }
+      }
+    }
+#endif
+
     cap->r.rCurrentTSO = t;
     
     /* context switches are now initiated by the timer signal, unless
@@ -1102,7 +1206,9 @@ run_thread:
        ret = ThreadFinished;
        break;
     case ThreadRunGHC:
+       errno = t->saved_errno;
        ret = StgRun((StgFunPtr) stg_returnToStackTop, &cap->r);
+       t->saved_errno = errno;
        break;
     case ThreadInterpret:
        ret = interpretBCO(cap);
@@ -1121,9 +1227,9 @@ run_thread:
     ACQUIRE_LOCK(&sched_mutex);
     
 #ifdef RTS_SUPPORTS_THREADS
-    IF_DEBUG(scheduler,fprintf(stderr,"scheduler (task %ld): ", osThreadId()););
+    IF_DEBUG(scheduler,fprintf(stderr,"sched (task %p): ", osThreadId()););
 #elif !defined(GRAN) && !defined(PAR)
-    IF_DEBUG(scheduler,fprintf(stderr,"scheduler: "););
+    IF_DEBUG(scheduler,fprintf(stderr,"sched: "););
 #endif
     t = cap->r.rCurrentTSO;
     
@@ -1388,6 +1494,7 @@ run_thread:
                       t->id, whatNext_strs[t->what_next]);
               printThreadBlockage(t);
               fprintf(stderr, "\n"));
+      fflush(stderr);
 
       /* Only for dumping event to log file 
         ToDo: do I need this in GranSim, too?
@@ -1396,7 +1503,7 @@ run_thread:
 #endif
       threadPaused(t);
       break;
-      
+
     case ThreadFinished:
       /* Need to check whether this was a main thread, and if so, signal
        * the task that started it with the return value.  If we have no
@@ -1491,73 +1598,104 @@ run_thread:
 }
 
 /* ---------------------------------------------------------------------------
+ * rtsSupportsBoundThreads(): is the RTS built to support bound threads?
+ * used by Control.Concurrent for error checking.
+ * ------------------------------------------------------------------------- */
+StgBool
+rtsSupportsBoundThreads(void)
+{
+#ifdef THREADED_RTS
+  return rtsTrue;
+#else
+  return rtsFalse;
+#endif
+}
+
+/* ---------------------------------------------------------------------------
+ * isThreadBound(tso): check whether tso is bound to an OS thread.
+ * ------------------------------------------------------------------------- */
+StgBool
+isThreadBound(StgTSO* tso USED_IN_THREADED_RTS)
+{
+#ifdef THREADED_RTS
+  StgMainThread *m;
+  for(m = main_threads; m; m = m->link)
+  {
+    if(m->tso == tso)
+      return rtsTrue;
+  }
+#endif
+  return rtsFalse;
+}
+
+/* ---------------------------------------------------------------------------
  * Singleton fork(). Do not copy any running threads.
  * ------------------------------------------------------------------------- */
 
-StgInt forkProcess(StgTSO* tso) {
+static void 
+deleteThreadImmediately(StgTSO *tso);
 
+StgInt
+forkProcess(HsStablePtr *entry)
+{
 #ifndef mingw32_TARGET_OS
   pid_t pid;
   StgTSO* t,*next;
   StgMainThread *m;
-  rtsBool doKill;
+  SchedulerStatus rc;
 
   IF_DEBUG(scheduler,sched_belch("forking!"));
+  rts_lock(); // This not only acquires sched_mutex, it also
+              // makes sure that no other threads are running
 
   pid = fork();
+
   if (pid) { /* parent */
 
   /* just return the pid */
+    rts_unlock();
+    return pid;
     
   } else { /* child */
-  /* wipe all other threads */
-  run_queue_hd = run_queue_tl = tso;
-  tso->link = END_TSO_QUEUE;
-
-  /* When clearing out the threads, we need to ensure
-     that a 'main thread' is left behind; if there isn't,
-     the Scheduler will shutdown next time it is entered.
-     
-     ==> we don't kill a thread that's on the main_threads
-         list (nor the current thread.)
     
-     [ Attempts at implementing the more ambitious scheme of
-       killing the main_threads also, and then adding the
-       current thread onto the main_threads list if it wasn't
-       there already, failed -- waitThread() (for one) wasn't
-       up to it. If it proves to be desirable to also kill
-       the main threads, then this scheme will have to be
-       revisited (and fully debugged!)
-       
-       -- sof 7/2002
-     ]
-  */
-  /* DO NOT TOUCH THE QUEUES directly because most of the code around
-     us is picky about finding the thread still in its queue when
-     handling the deleteThread() */
-
-  for (t = all_threads; t != END_TSO_QUEUE; t = next) {
-    next = t->link;
     
-    /* Don't kill the current thread.. */
-    if (t->id == tso->id) continue;
-    doKill=rtsTrue;
-    /* ..or a main thread */
-    for (m = main_threads; m != NULL; m = m->link) {
-       if (m->tso->id == t->id) {
-         doKill=rtsFalse;
-         break;
-       }
+      // delete all threads
+    run_queue_hd = run_queue_tl = END_TSO_QUEUE;
+    
+    for (t = all_threads; t != END_TSO_QUEUE; t = next) {
+      next = t->link;
+
+        // don't allow threads to catch the ThreadKilled exception
+      deleteThreadImmediately(t);
     }
-    if (doKill) {
-      deleteThread(t);
+    
+      // wipe the main thread list
+    while((m = main_threads) != NULL) {
+      main_threads = m->link;
+#ifdef THREADED_RTS
+      closeCondition(&m->bound_thread_cond);
+#endif
+      stgFree(m);
     }
+    
+#ifdef RTS_SUPPORTS_THREADS
+    resetTaskManagerAfterFork();      // tell startTask() and friends that
+    startingWorkerThread = rtsFalse;  // we have no worker threads any more
+    resetWorkerWakeupPipeAfterFork();
+#endif
+    
+    rc = rts_evalStableIO(entry, NULL);  // run the action
+    rts_checkSchedStatus("forkProcess",rc);
+    
+    rts_unlock();
+    
+    hs_exit();                      // clean up and exit
+    stg_exit(0);
   }
-  }
-  return pid;
 #else /* mingw32 */
-  barf("forkProcess#: primop not implemented for mingw32, sorry! (%u)\n", tso->id);
-  /* pointlessly printing out the TSOs 'id' to avoid CC unused warning. */
+  barf("forkProcess#: primop not implemented for mingw32, sorry!\n");
   return -1;
 #endif /* mingw32 */
 }
@@ -1571,7 +1709,8 @@ StgInt forkProcess(StgTSO* tso) {
  * Locks: sched_mutex held.
  * ------------------------------------------------------------------------- */
    
-void deleteAllThreads ( void )
+void
+deleteAllThreads ( void )
 {
   StgTSO* t, *next;
   IF_DEBUG(scheduler,sched_belch("deleting all threads"));
@@ -1615,11 +1754,12 @@ suspendThread( StgRegTable *reg,
 {
   nat tok;
   Capability *cap;
+  int saved_errno = errno;
 
   /* assume that *reg is a pointer to the StgRegTable part
    * of a Capability.
    */
-  cap = (Capability *)((void *)reg - sizeof(StgFunTable));
+  cap = (Capability *)((void *)((unsigned char*)reg - sizeof(StgFunTable)));
 
   ACQUIRE_LOCK(&sched_mutex);
 
@@ -1655,35 +1795,31 @@ suspendThread( StgRegTable *reg,
   /* Preparing to leave the RTS, so ensure there's a native thread/task
      waiting to take over.
   */
-  IF_DEBUG(scheduler, sched_belch("worker thread (%d, osthread %p): leaving RTS", tok, osThreadId()));
-  //if (concCall) { // implementing "safe" as opposed to "threadsafe" is more difficult
-      startTask(taskStart);
-  //}
+  IF_DEBUG(scheduler, sched_belch("worker (token %d): leaving RTS", tok));
 #endif
 
   /* Other threads _might_ be available for execution; signal this */
   THREAD_RUNNABLE();
   RELEASE_LOCK(&sched_mutex);
+  
+  errno = saved_errno;
   return tok; 
 }
 
 StgRegTable *
 resumeThread( StgInt tok,
-             rtsBool concCall
-#if !defined(RTS_SUPPORTS_THREADS)
-              STG_UNUSED
-#endif
-             )
+             rtsBool concCall STG_UNUSED )
 {
   StgTSO *tso, **prev;
   Capability *cap;
+  int saved_errno = errno;
 
 #if defined(RTS_SUPPORTS_THREADS)
   /* Wait for permission to re-enter the RTS with the result. */
   ACQUIRE_LOCK(&sched_mutex);
   grabReturnCapability(&sched_mutex, &cap);
 
-  IF_DEBUG(scheduler, sched_belch("worker thread (%d, osthread %p): re-entering RTS", tok, osThreadId()));
+  IF_DEBUG(scheduler, sched_belch("worker (token %d): re-entering RTS", tok));
 #else
   grabCapability(&cap);
 #endif
@@ -1718,6 +1854,7 @@ resumeThread( StgInt tok,
 #if defined(RTS_SUPPORTS_THREADS)
   RELEASE_LOCK(&sched_mutex);
 #endif
+  errno = saved_errno;
   return &cap->r;
 }
 
@@ -1768,7 +1905,7 @@ labelThread(StgPtr tso, char *label)
   buf = stgMallocBytes(len * sizeof(char), "Schedule.c:labelThread()");
   strncpy(buf,label,len);
   /* Update will free the old memory for us */
-  updateThreadLabel((StgWord)tso,buf);
+  updateThreadLabel(((StgTSO *)tso)->id,buf);
 }
 #endif /* DEBUG */
 
@@ -1846,6 +1983,8 @@ createThread(nat size)
   tso->why_blocked  = NotBlocked;
   tso->blocked_exceptions = NULL;
 
+  tso->saved_errno = 0;
+  
   tso->stack_size   = stack_size;
   tso->max_stack_size = round_to_mblocks(RtsFlags.GcFlags.maxStkSize) 
                               - TSO_STRUCT_SIZEW;
@@ -2017,10 +2156,8 @@ activateSpark (rtsSpark spark)
 }
 #endif
 
-static SchedulerStatus waitThread_(/*out*/StgMainThread* m
-#if defined(THREADED_RTS)
-                                  , rtsBool blockWaiting
-#endif
+static SchedulerStatus waitThread_(/*out*/StgMainThread* m,
+                                  Capability *initialCapability
                                   );
 
 
@@ -2062,7 +2199,7 @@ void scheduleThread(StgTSO* tso)
 }
 
 SchedulerStatus
-scheduleWaitThread(StgTSO* tso, /*[out]*/HaskellObj* ret)
+scheduleWaitThread(StgTSO* tso, /*[out]*/HaskellObj* ret, Capability *initialCapability)
 {      // Precondition: sched_mutex must be held
   StgMainThread *m;
 
@@ -2071,8 +2208,12 @@ scheduleWaitThread(StgTSO* tso, /*[out]*/HaskellObj* ret)
   m->ret = ret;
   m->stat = NoStatus;
 #if defined(RTS_SUPPORTS_THREADS)
+#if defined(THREADED_RTS)
+  initCondition(&m->bound_thread_cond);
+#else
   initCondition(&m->wakeup);
 #endif
+#endif
 
   /* Put the thread on the main-threads list prior to scheduling the TSO.
      Failure to do so introduces a race condition in the MT case (as
@@ -2083,17 +2224,14 @@ scheduleWaitThread(StgTSO* tso, /*[out]*/HaskellObj* ret)
      signal the completion of the its work item for the main thread to
      see (==> it got stuck waiting.)    -- sof 6/02.
   */
-  IF_DEBUG(scheduler, sched_belch("waiting for thread (%d)\n", tso->id));
+  IF_DEBUG(scheduler, sched_belch("waiting for thread (%d)", tso->id));
   
   m->link = main_threads;
   main_threads = m;
 
   scheduleThread_(tso);
-#if defined(THREADED_RTS)
-  return waitThread_(m, rtsTrue);
-#else
-  return waitThread_(m);
-#endif
+
+  return waitThread_(m, initialCapability);
 }
 
 /* ---------------------------------------------------------------------------
@@ -2260,13 +2398,13 @@ finishAllThreads ( void )
 {
    do {
       while (run_queue_hd != END_TSO_QUEUE) {
-         waitThread ( run_queue_hd, NULL);
+         waitThread ( run_queue_hd, NULL, NULL );
       }
       while (blocked_queue_hd != END_TSO_QUEUE) {
-         waitThread ( blocked_queue_hd, NULL);
+         waitThread ( blocked_queue_hd, NULL, NULL );
       }
       while (sleeping_queue != END_TSO_QUEUE) {
-         waitThread ( blocked_queue_hd, NULL);
+         waitThread ( blocked_queue_hd, NULL, NULL );
       }
    } while 
       (blocked_queue_hd != END_TSO_QUEUE || 
@@ -2275,7 +2413,7 @@ finishAllThreads ( void )
 }
 
 SchedulerStatus
-waitThread(StgTSO *tso, /*out*/StgClosure **ret)
+waitThread(StgTSO *tso, /*out*/StgClosure **ret, Capability *initialCapability)
 { 
   StgMainThread *m;
   SchedulerStatus stat;
@@ -2285,8 +2423,12 @@ waitThread(StgTSO *tso, /*out*/StgClosure **ret)
   m->ret = ret;
   m->stat = NoStatus;
 #if defined(RTS_SUPPORTS_THREADS)
+#if defined(THREADED_RTS)
+  initCondition(&m->bound_thread_cond);
+#else
   initCondition(&m->wakeup);
 #endif
+#endif
 
   /* see scheduleWaitThread() comment */
   ACQUIRE_LOCK(&sched_mutex);
@@ -2294,45 +2436,25 @@ waitThread(StgTSO *tso, /*out*/StgClosure **ret)
   main_threads = m;
 
   IF_DEBUG(scheduler, sched_belch("waiting for thread %d", tso->id));
-#if defined(THREADED_RTS)
-  stat = waitThread_(m, rtsFalse);
-#else
-  stat = waitThread_(m);
-#endif
+
+  stat = waitThread_(m,initialCapability);
+  
   RELEASE_LOCK(&sched_mutex);
   return stat;
 }
 
 static
 SchedulerStatus
-waitThread_(StgMainThread* m
-#if defined(THREADED_RTS)
-           , rtsBool blockWaiting
-#endif
-          )
+waitThread_(StgMainThread* m, Capability *initialCapability)
 {
   SchedulerStatus stat;
 
   // Precondition: sched_mutex must be held.
-  IF_DEBUG(scheduler, sched_belch("== scheduler: new main thread (%d)\n", m->tso->id));
+  IF_DEBUG(scheduler, sched_belch("new main thread (%d)", m->tso->id));
 
-#if defined(RTS_SUPPORTS_THREADS)
-
-# if defined(THREADED_RTS)
-  if (!blockWaiting) {
-    /* In the threaded case, the OS thread that called main()
-     * gets to enter the RTS directly without going via another
-     * task/thread.
-     */
-    main_main_thread = m;
-    RELEASE_LOCK(&sched_mutex);
-    schedule();
-    ACQUIRE_LOCK(&sched_mutex);
-    main_main_thread = NULL;
-    ASSERT(m->stat != NoStatus);
-  } else 
-# endif
-  {
+#if defined(RTS_SUPPORTS_THREADS) && !defined(THREADED_RTS)
+  {    // FIXME: does this still make sense?
+       // It's not for the threaded rts => SMP only
     do {
       waitCondition(&m->wakeup, &sched_mutex);
     } while (m->stat == NoStatus);
@@ -2344,20 +2466,25 @@ waitThread_(StgMainThread* m
   CurrentProc = MainProc;             // PE to run it on
 
   RELEASE_LOCK(&sched_mutex);
-  schedule();
+  schedule(m,initialCapability);
 #else
   RELEASE_LOCK(&sched_mutex);
-  schedule();
+  schedule(m,initialCapability);
+  ACQUIRE_LOCK(&sched_mutex);
   ASSERT(m->stat != NoStatus);
 #endif
 
   stat = m->stat;
 
 #if defined(RTS_SUPPORTS_THREADS)
+#if defined(THREADED_RTS)
+  closeCondition(&m->bound_thread_cond);
+#else
   closeCondition(&m->wakeup);
 #endif
+#endif
 
-  IF_DEBUG(scheduler, fprintf(stderr, "== scheduler: main thread (%d) finished\n", 
+  IF_DEBUG(scheduler, fprintf(stderr, "== sched: main thread (%d) finished\n", 
                              m->tso->id));
   stgFree(m);
 
@@ -2627,7 +2754,7 @@ threadStackOverflow(StgTSO *tso)
   if (tso->stack_size >= tso->max_stack_size) {
 
     IF_DEBUG(gc,
-            belch("@@ threadStackOverflow of TSO %d (%p): stack too large (now %ld; max is %ld",
+            belch("@@ threadStackOverflow of TSO %d (%p): stack too large (now %ld; max is %ld)",
                   tso->id, tso, tso->stack_size, tso->max_stack_size);
             /* If we're debugging, just print out the top of the stack */
             printStackChunk(tso->sp, stg_min(tso->stack+tso->stack_size, 
@@ -2648,7 +2775,7 @@ threadStackOverflow(StgTSO *tso)
   new_tso_size = round_to_mblocks(new_tso_size);  /* Be MBLOCK-friendly */
   new_stack_size = new_tso_size - TSO_STRUCT_SIZEW;
 
-  IF_DEBUG(scheduler, fprintf(stderr,"== scheduler: increasing stack size from %d words to %d.\n", tso->stack_size, new_stack_size));
+  IF_DEBUG(scheduler, fprintf(stderr,"== sched: increasing stack size from %d words to %d.\n", tso->stack_size, new_stack_size));
 
   dest = (StgTSO *)allocate(new_tso_size);
   TICK_ALLOC_TSO(new_stack_size,0);
@@ -2699,12 +2826,12 @@ threadStackOverflow(StgTSO *tso)
    ------------------------------------------------------------------------ */
 
 #if defined(GRAN)
-static inline void
+STATIC_INLINE void
 unblockCount ( StgBlockingQueueElement *bqe, StgClosure *node )
 {
 }
 #elif defined(PAR)
-static inline void
+STATIC_INLINE void
 unblockCount ( StgBlockingQueueElement *bqe, StgClosure *node )
 {
   /* write RESUME events to log file and
@@ -2840,7 +2967,7 @@ unblockOneLocked(StgTSO *tso)
 #endif
 
 #if defined(GRAN) || defined(PAR)
-inline StgBlockingQueueElement *
+INLINE_ME StgBlockingQueueElement *
 unblockOne(StgBlockingQueueElement *bqe, StgClosure *node)
 {
   ACQUIRE_LOCK(&sched_mutex);
@@ -2849,7 +2976,7 @@ unblockOne(StgBlockingQueueElement *bqe, StgClosure *node)
   return bqe;
 }
 #else
-inline StgTSO *
+INLINE_ME StgTSO *
 unblockOne(StgTSO *tso)
 {
   ACQUIRE_LOCK(&sched_mutex);
@@ -3009,6 +3136,9 @@ interruptStgRts(void)
 {
     interrupted    = 1;
     context_switch = 1;
+#ifdef RTS_SUPPORTS_THREADS
+    wakeBlockedWorkerThread();
+#endif
 }
 
 /* -----------------------------------------------------------------------------
@@ -3104,6 +3234,9 @@ unblockThread(StgTSO *tso)
 
   case BlockedOnRead:
   case BlockedOnWrite:
+#if defined(mingw32_TARGET_OS)
+  case BlockedOnDoProc:
+#endif
     {
       /* take TSO off blocked_queue */
       StgBlockingQueueElement *prev = NULL;
@@ -3142,7 +3275,7 @@ unblockThread(StgTSO *tso)
          goto done;
        }
       }
-      barf("unblockThread (I/O): TSO not found");
+      barf("unblockThread (delay): TSO not found");
     }
 
   default:
@@ -3231,6 +3364,9 @@ unblockThread(StgTSO *tso)
 
   case BlockedOnRead:
   case BlockedOnWrite:
+#if defined(mingw32_TARGET_OS)
+  case BlockedOnDoProc:
+#endif
     {
       StgTSO *prev = NULL;
       for (t = blocked_queue_hd; t != END_TSO_QUEUE; 
@@ -3267,7 +3403,7 @@ unblockThread(StgTSO *tso)
          goto done;
        }
       }
-      barf("unblockThread (I/O): TSO not found");
+      barf("unblockThread (delay): TSO not found");
     }
 
   default:
@@ -3322,6 +3458,22 @@ deleteThread(StgTSO *tso)
   raiseAsync(tso,NULL);
 }
 
+static void 
+deleteThreadImmediately(StgTSO *tso)
+{ // for forkProcess only:
+  // delete thread without giving it a chance to catch the KillThread exception
+
+  if (tso->what_next == ThreadComplete || tso->what_next == ThreadKilled) {
+      return;
+  }
+#if defined(RTS_SUPPORTS_THREADS)
+  if (tso->why_blocked != BlockedOnCCall
+      && tso->why_blocked != BlockedOnCCall_NoUnblockExc)
+#endif
+    unblockThread(tso);
+  tso->what_next = ThreadKilled;
+}
+
 void
 raiseAsyncWithLock(StgTSO *tso, StgClosure *exception)
 {
@@ -3459,7 +3611,7 @@ raiseAsync(StgTSO *tso, StgClosure *exception)
            TICK_ALLOC_UP_THK(words+1,0);
            
            IF_DEBUG(scheduler,
-                    fprintf(stderr,  "scheduler: Updating ");
+                    fprintf(stderr,  "sched: Updating ");
                     printPtr((P_)((StgUpdateFrame *)frame)->updatee); 
                     fprintf(stderr,  " with ");
                     printObj((StgClosure *)ap);
@@ -3624,6 +3776,11 @@ printThreadBlockage(StgTSO *tso)
   case BlockedOnWrite:
     fprintf(stderr,"is blocked on write to fd %d", tso->block_info.fd);
     break;
+#if defined(mingw32_TARGET_OS)
+    case BlockedOnDoProc:
+    fprintf(stderr,"is blocked on proc (request: %d)", tso->block_info.async_result->reqID);
+    break;
+#endif
   case BlockedOnDelay:
     fprintf(stderr,"is blocked until %d", tso->block_info.target);
     break;
@@ -3704,7 +3861,7 @@ printAllThreads(void)
 
   for (t = all_threads; t != END_TSO_QUEUE; t = t->global_link) {
     fprintf(stderr, "\tthread %d @ %p ", t->id, (void *)t);
-    label = lookupThreadLabel((StgWord)t);
+    label = lookupThreadLabel(t->id);
     if (label) fprintf(stderr,"[\"%s\"] ",(char *)label);
     printThreadStatus(t);
     fprintf(stderr,"\n");
@@ -3881,20 +4038,21 @@ run_queue_len(void)
 }
 #endif
 
-static void
+void
 sched_belch(char *s, ...)
 {
   va_list ap;
   va_start(ap,s);
-#ifdef SMP
-  fprintf(stderr, "scheduler (task %ld): ", osThreadId());
+#ifdef RTS_SUPPORTS_THREADS
+  fprintf(stderr, "sched (task %p): ", osThreadId());
 #elif defined(PAR)
   fprintf(stderr, "== ");
 #else
-  fprintf(stderr, "scheduler: ");
+  fprintf(stderr, "sched: ");
 #endif
   vfprintf(stderr, s, ap);
   fprintf(stderr, "\n");
+  fflush(stderr);
   va_end(ap);
 }