[project @ 2002-04-19 10:25:00 by simonmar]
[ghc-hetmet.git] / ghc / rts / Schedule.c
index 8a47443..6c5edef 100644 (file)
@@ -1,5 +1,5 @@
 /* ---------------------------------------------------------------------------
- * $Id: Schedule.c,v 1.135 2002/04/01 11:18:19 panne Exp $
+ * $Id: Schedule.c,v 1.137 2002/04/13 05:33:02 sof Exp $
  *
  * (c) The GHC Team, 1998-2000
  *
 #include "OSThreads.h"
 #include  "Task.h"
 
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
 #include <stdarg.h>
 
 //@node Variables and Data structures, Prototypes, Includes, Main scheduling code
@@ -357,12 +364,10 @@ schedule( void )
   ACQUIRE_LOCK(&sched_mutex);
  
 #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);
-
   waitForWorkCapability(&sched_mutex, &cap, rtsFalse);
+#else
+  /* simply initialise it in the non-threaded case */
+  grabCapability(&cap);
 #endif
 
 #if defined(GRAN)
@@ -400,6 +405,13 @@ schedule( void )
 
     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);
+#endif
+
     /* If we're interrupted (the user pressed ^C, or some other
      * termination condition occurred), kill all the currently running
      * threads.
@@ -429,6 +441,9 @@ schedule( void )
          *prev = m->link;
          m->stat = Success;
          broadcastCondition(&m->wakeup);
+#ifdef DEBUG
+         free(m->tso->label);
+#endif
          break;
        case ThreadKilled:
          if (m->ret) *(m->ret) = NULL;
@@ -439,6 +454,9 @@ schedule( void )
            m->stat = Killed;
          }
          broadcastCondition(&m->wakeup);
+#ifdef DEBUG
+         free(m->tso->label);
+#endif
          break;
        default:
          break;
@@ -458,6 +476,9 @@ schedule( void )
       StgMainThread *m = main_threads;
       if (m->tso->what_next == ThreadComplete
          || m->tso->what_next == ThreadKilled) {
+#ifdef DEBUG
+       free(m->tso->label);
+#endif
        main_threads = main_threads->link;
        if (m->tso->what_next == ThreadComplete) {
          /* we finished successfully, fill in the return value */
@@ -615,11 +636,11 @@ schedule( void )
            for (m = main_threads; m != NULL; m = m->link) {
                switch (m->tso->why_blocked) {
                case BlockedOnBlackHole:
-                   raiseAsync(m->tso, (StgClosure *)NonTermination_closure);
+                   raiseAsyncWithLock(m->tso, (StgClosure *)NonTermination_closure);
                    break;
                case BlockedOnException:
                case BlockedOnMVar:
-                   raiseAsync(m->tso, (StgClosure *)Deadlock_closure);
+                   raiseAsyncWithLock(m->tso, (StgClosure *)Deadlock_closure);
                    break;
                default:
                    barf("deadlock: main thread blocked in a strange way");
@@ -629,11 +650,11 @@ schedule( void )
            m = main_threads;
            switch (m->tso->why_blocked) {
            case BlockedOnBlackHole:
-               raiseAsync(m->tso, (StgClosure *)NonTermination_closure);
+               raiseAsyncWithLock(m->tso, (StgClosure *)NonTermination_closure);
                break;
            case BlockedOnException:
            case BlockedOnMVar:
-               raiseAsync(m->tso, (StgClosure *)Deadlock_closure);
+               raiseAsyncWithLock(m->tso, (StgClosure *)Deadlock_closure);
                break;
            default:
                barf("deadlock: main thread blocked in a strange way");
@@ -966,7 +987,6 @@ schedule( void )
     IF_DEBUG(sanity,checkTSO(t));
 #endif
     
-    grabCapability(&cap);
     cap->r.rCurrentTSO = t;
     
     /* context switches are now initiated by the timer signal, unless
@@ -1314,11 +1334,6 @@ schedule( void )
     default:
       barf("schedule: invalid thread return code %d", (int)ret);
     }
-    
-#if defined(RTS_SUPPORTS_THREADS)
-    /* I don't understand what this re-grab is doing -- sof */
-    grabCapability(&cap);
-#endif
 
 #ifdef PROFILING
     if (RtsFlags.ProfFlags.profileInterval==0 || performHeapProfile) {
@@ -1377,6 +1392,46 @@ schedule( void )
 }
 
 /* ---------------------------------------------------------------------------
+ * Singleton fork(). Do not copy any running threads.
+ * ------------------------------------------------------------------------- */
+
+StgInt forkProcess(StgTSO* tso) {
+
+#ifndef mingw32_TARGET_OS
+  pid_t pid;
+  StgTSO* t,*next;
+
+  IF_DEBUG(scheduler,sched_belch("forking!"));
+
+  pid = fork();
+  if (pid) { /* parent */
+
+  /* just return the pid */
+    
+  } else { /* child */
+  /* wipe all other threads */
+  run_queue_hd = tso;
+  tso->link = END_TSO_QUEUE;
+
+  /* DO NOT TOUCH THE QUEUES directly because most of the code around
+     us is picky about finding the threat still in its queue when
+     handling the deleteThread() */
+
+  for (t = all_threads; t != END_TSO_QUEUE; t = next) {
+    next = t->link;
+    if (t->id != tso->id) {
+      deleteThread(t);
+    }
+  }
+  }
+  return pid;
+#else /* mingw32 */
+  barf("forkProcess#: primop not implemented for mingw32, sorry!");
+  return -1;
+#endif /* mingw32 */
+}
+
+/* ---------------------------------------------------------------------------
  * deleteAllThreads():  kill all the live threads.
  *
  * This is used when we catch a user interrupt (^C), before performing
@@ -1420,7 +1475,7 @@ void deleteAllThreads ( void )
 StgInt
 suspendThread( StgRegTable *reg, 
               rtsBool concCall
-#if !defined(RTS_SUPPORTS_THREADS)
+#if !defined(RTS_SUPPORTS_THREADS) && !defined(DEBUG)
               STG_UNUSED
 #endif
               )
@@ -1436,7 +1491,7 @@ suspendThread( StgRegTable *reg,
   ACQUIRE_LOCK(&sched_mutex);
 
   IF_DEBUG(scheduler,
-          sched_belch("thread %d did a _ccall_gc", cap->r.rCurrentTSO->id));
+          sched_belch("thread %d did a _ccall_gc (is_concurrent: %d)", cap->r.rCurrentTSO->id,concCall));
 
   threadPaused(cap->r.rCurrentTSO);
   cap->r.rCurrentTSO->link = suspended_ccalling_threads;
@@ -1486,6 +1541,7 @@ resumeThread( StgInt tok,
 #if defined(RTS_SUPPORTS_THREADS)
   /* Wait for permission to re-enter the RTS with the result. */
   if ( concCall ) {
+    ACQUIRE_LOCK(&sched_mutex);
     grabReturnCapability(&sched_mutex, &cap);
   } else {
     grabCapability(&cap);
@@ -1550,6 +1606,24 @@ int rts_getThreadId(const StgTSO *tso)
   return tso->id;
 }
 
+#ifdef DEBUG
+void labelThread(StgTSO *tso, char *label)
+{
+  int len;
+  void *buf;
+
+  /* Caveat: Once set, you can only set the thread name to "" */
+  len = strlen(label)+1;
+  buf = realloc(tso->label,len);
+  if (buf == NULL) {
+    fprintf(stderr,"insufficient memory for labelThread!\n");
+    free(tso->label);
+  } else
+    strncpy(buf,label,len);
+  tso->label = buf;
+}
+#endif /* DEBUG */
+
 /* ---------------------------------------------------------------------------
    Create a new thread.
 
@@ -1624,6 +1698,10 @@ createThread_(nat size, rtsBool have_lock)
 #endif
   tso->what_next     = ThreadEnterGHC;
 
+#ifdef DEBUG
+  tso->label = NULL;
+#endif
+
   /* tso->id needs to be unique.  For now we use a heavyweight mutex to
    * protect the increment operation on next_thread_id.
    * In future, we could use an atomic increment instead.
@@ -2041,6 +2119,7 @@ finishAllThreads ( void )
 SchedulerStatus
 waitThread(StgTSO *tso, /*out*/StgClosure **ret)
 { 
+  IF_DEBUG(scheduler, sched_belch("== scheduler: waiting for thread (%d)\n", tso->id));
 #if defined(THREADED_RTS)
   return waitThread_(tso,ret, rtsFalse);
 #else
@@ -2060,6 +2139,7 @@ waitThread_(StgTSO *tso,
   SchedulerStatus stat;
 
   ACQUIRE_LOCK(&sched_mutex);
+  IF_DEBUG(scheduler, sched_belch("== scheduler: waiting for thread (%d)\n", tso->id));
   
   m = stgMallocBytes(sizeof(StgMainThread), "waitThread");
 
@@ -2755,6 +2835,9 @@ interruptStgRts(void)
   NB: only the type of the blocking queue is different in GranSim and GUM
       the operations on the queue-elements are the same
       long live polymorphism!
+
+  Locks: sched_mutex is held upon entry and exit.
+
 */
 static void
 unblockThread(StgTSO *tso)
@@ -2890,13 +2973,15 @@ static void
 unblockThread(StgTSO *tso)
 {
   StgTSO *t, **last;
+  
+  /* To avoid locking unnecessarily. */
+  if (tso->why_blocked == NotBlocked) {
+    return;
+  }
 
   ACQUIRE_LOCK(&sched_mutex);
   switch (tso->why_blocked) {
 
-  case NotBlocked:
-    return;  /* not blocked */
-
   case BlockedOnMVar:
     ASSERT(get_itbl(tso->block_info.closure)->type == MVAR);
     {
@@ -3042,6 +3127,8 @@ unblockThread(StgTSO *tso)
  * CATCH_FRAME on the stack.  In either case, we strip the entire
  * stack and replace the thread with a zombie.
  *
+ * Locks: sched_mutex not held upon entry nor exit.
+ *
  * -------------------------------------------------------------------------- */
  
 void 
@@ -3051,6 +3138,16 @@ deleteThread(StgTSO *tso)
 }
 
 void
+raiseAsyncWithLock(StgTSO *tso, StgClosure *exception)
+{
+  /* When raising async exs from contexts where sched_mutex is held;
+     use raiseAsyncWithLock(). */
+  RELEASE_LOCK(&sched_mutex);
+  raiseAsync(tso,exception);
+  ACQUIRE_LOCK(&sched_mutex);
+}
+
+void
 raiseAsync(StgTSO *tso, StgClosure *exception)
 {
   StgUpdateFrame* su = tso->su;
@@ -3066,6 +3163,7 @@ raiseAsync(StgTSO *tso, StgClosure *exception)
   /* Remove it from any blocking queues */
   unblockThread(tso);
 
+  IF_DEBUG(scheduler, sched_belch("raising exception in thread %ld.", tso->id));
   /* The stack freezing code assumes there's a closure pointer on
    * the top of the stack.  This isn't always the case with compiled
    * code, so we have to push a dummy closure on the top which just
@@ -3249,6 +3347,8 @@ raiseAsync(StgTSO *tso, StgClosure *exception)
    up and sent a signal: BlockedOnDeadMVar if the thread was blocked
    on an MVar, or NonTermination if the thread was blocked on a Black
    Hole.
+
+   Locks: sched_mutex isn't held upon entry nor exit.
    -------------------------------------------------------------------------- */
 
 void
@@ -3265,10 +3365,11 @@ resurrectThreads( StgTSO *threads )
     switch (tso->why_blocked) {
     case BlockedOnMVar:
     case BlockedOnException:
-      raiseAsync(tso,(StgClosure *)BlockedOnDeadMVar_closure);
+      /* Called by GC - sched_mutex lock is currently held. */
+      raiseAsyncWithLock(tso,(StgClosure *)BlockedOnDeadMVar_closure);
       break;
     case BlockedOnBlackHole:
-      raiseAsync(tso,(StgClosure *)NonTermination_closure);
+      raiseAsyncWithLock(tso,(StgClosure *)NonTermination_closure);
       break;
     case NotBlocked:
       /* This might happen if the thread was blocked on a black hole
@@ -3289,6 +3390,8 @@ resurrectThreads( StgTSO *threads )
  *
  * This is only done in a deadlock situation in order to avoid
  * performance overhead in the normal case.
+ *
+ * Locks: sched_mutex is held upon entry and exit.
  * -------------------------------------------------------------------------- */
 
 static void
@@ -3321,7 +3424,7 @@ detectBlackHoles( void )
                     */
                    IF_DEBUG(scheduler, 
                             sched_belch("thread %d is blocked on itself", t->id));
-                   raiseAsync(t, (StgClosure *)NonTermination_closure);
+                   raiseAsyncWithLock(t, (StgClosure *)NonTermination_closure);
                    goto done;
                }
                else {
@@ -3436,6 +3539,7 @@ printAllThreads(void)
 
   for (t = all_threads; t != END_TSO_QUEUE; t = t->global_link) {
     fprintf(stderr, "\tthread %d ", t->id);
+    if (t->label) fprintf(stderr,"[\"%s\"] ",t->label);
     printThreadStatus(t);
     fprintf(stderr,"\n");
   }