[project @ 2002-10-11 08:04:55 by simonpj]
[ghc-hetmet.git] / ghc / rts / Schedule.c
index 03348b0..9759b55 100644 (file)
@@ -1,5 +1,5 @@
 /* ---------------------------------------------------------------------------
- * $Id: Schedule.c,v 1.149 2002/07/19 00:06:05 sof Exp $
+ * $Id: Schedule.c,v 1.156 2002/09/25 14:46:31 simonmar Exp $
  *
  * (c) The GHC Team, 1998-2000
  *
@@ -84,6 +84,7 @@
 #include "StgRun.h"
 #include "StgStartup.h"
 #include "Hooks.h"
+#define COMPILING_SCHEDULER
 #include "Schedule.h"
 #include "StgMiscClosures.h"
 #include "Storage.h"
 /* Main thread queue.
  * Locks required: sched_mutex.
  */
-StgMainThread *main_threads;
+StgMainThread *main_threads = NULL;
 
 /* Thread queues.
  * Locks required: sched_mutex.
@@ -159,16 +160,18 @@ StgTSO *ccalling_threadss[MAX_PROC];
 
 #else /* !GRAN */
 
-StgTSO *run_queue_hd, *run_queue_tl;
-StgTSO *blocked_queue_hd, *blocked_queue_tl;
-StgTSO *sleeping_queue;                /* perhaps replace with a hash table? */
+StgTSO *run_queue_hd = NULL;
+StgTSO *run_queue_tl = NULL;
+StgTSO *blocked_queue_hd = NULL;
+StgTSO *blocked_queue_tl = NULL;
+StgTSO *sleeping_queue = NULL;    /* perhaps replace with a hash table? */
 
 #endif
 
 /* Linked list of all threads.
  * Used for detecting garbage collected threads.
  */
-StgTSO *all_threads;
+StgTSO *all_threads = NULL;
 
 /* When a thread performs a safe C call (_ccall_GC, using old
  * terminology), it gets put on the suspended_ccalling_threads
@@ -185,17 +188,17 @@ static StgTSO *threadStackOverflow(StgTSO *tso);
 
 /* flag set by signal handler to precipitate a context switch */
 //@cindex context_switch
-nat context_switch;
+nat context_switch = 0;
 
 /* if this flag is set as well, give up execution */
 //@cindex interrupted
-rtsBool interrupted;
+rtsBool interrupted = rtsFalse;
 
 /* Next thread ID to allocate.
  * Locks required: thread_id_mutex
  */
 //@cindex next_thread_id
-StgThreadID next_thread_id = 1;
+static StgThreadID next_thread_id = 1;
 
 /*
  * Pointers to the state of the current thread.
@@ -226,7 +229,7 @@ StgTSO *CurrentTSO;
  */
 StgTSO dummy_tso;
 
-rtsBool ready_to_gc;
+static rtsBool ready_to_gc;
 
 /*
  * Set to TRUE when entering a shutdown state (via shutdownHaskellAndExit()) --
@@ -274,21 +277,13 @@ rtsBool emitSchedule = rtsTrue;
 #endif
 
 #if DEBUG
-char *whatNext_strs[] = {
+static char *whatNext_strs[] = {
   "ThreadEnterGHC",
   "ThreadRunGHC",
   "ThreadEnterInterp",
   "ThreadKilled",
   "ThreadComplete"
 };
-
-char *threadReturnCode_strs[] = {
-  "HeapOverflow",                      /* might also be StackOverflow */
-  "StackOverflow",
-  "ThreadYielding",
-  "ThreadBlocked",
-  "ThreadFinished"
-};
 #endif
 
 #if defined(PAR)
@@ -1444,7 +1439,7 @@ StgInt forkProcess(StgTSO* tso) {
   pid_t pid;
   StgTSO* t,*next;
   StgMainThread *m;
-  rtsBool killerIsMainThread = rtsFalse;
+  rtsBool doKill;
 
   IF_DEBUG(scheduler,sched_belch("forking!"));
 
@@ -1459,62 +1454,43 @@ StgInt forkProcess(StgTSO* tso) {
   tso->link = END_TSO_QUEUE;
 
   /* When clearing out the threads, we need to ensure
-     that a 'main thread' is left behind.
-     careful about leaving a main thread behind.
+     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.)
     
-     ==> if the killing thread isn't a main thread, we
-     turn it into one.
+     [ 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
+     ]
   */
-  for (m = main_threads; m != NULL; m = m->link) {
-    if (m->tso->id == tso->id) {
-      killerIsMainThread=rtsTrue;
-      break;
-    }
-  }
-
   /* 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() */
 
-  if (!killerIsMainThread) {
-    /* Add it to main_threads */
-    m = stgMallocBytes(sizeof(StgMainThread), "forkProcess");
-    
-    m->tso = tso;
-    m->ret = NULL; /* can't really do better */
-    m->stat = NoStatus;
-#if defined(RTS_SUPPORTS_THREADS)
-    initCondition(&m->wakeup);
-#endif
-    /* Hook it up to the main_threads list. */
-    m->link = main_threads;
-    main_threads = m;
-  }
   for (t = all_threads; t != END_TSO_QUEUE; t = next) {
     next = t->link;
     
-    /* Don't kill current thread */
+    /* Don't kill the current thread.. */
     if (t->id == tso->id) continue;
-    if (!killerIsMainThread) { 
-      deleteThread(t);
-      /* Signal the abrupt completion of a now-killed main thread. */
-      for (m = main_threads; m != NULL; m = m->link) {
+    doKill=rtsTrue;
+    /* ..or a main thread */
+    for (m = main_threads; m != NULL; m = m->link) {
        if (m->tso->id == t->id) {
-         m->stat = Killed;
-         if (m->ret) { *(m->ret) = NULL; }
-#if defined(RTS_SUPPORTS_THREADS)
-         broadcastCondition(&m->wakeup);
-#endif
-#if defined(DEBUG)
-         removeThreadLabel((StgWord)m->tso);
-#endif
+         doKill=rtsFalse;
          break;
        }
-      }
     }
-    /* ToDo..?: kill other entries along main_threads except the
-     * killing (main) thread.
-     */
+    if (doKill) {
+      deleteThread(t);
+    }
   }
   }
   return pid;
@@ -2496,6 +2472,30 @@ GetRoots(evac_fn evac)
 #if defined(PAR) || defined(GRAN)
   markSparkQueue(evac);
 #endif
+
+#ifndef mingw32_TARGET_OS
+  // mark the signal handlers (signals should be already blocked)
+  markSignalHandlers(evac);
+#endif
+
+  // main threads which have completed need to be retained until they
+  // are dealt with in the main scheduler loop.  They won't be
+  // retained any other way: the GC will drop them from the
+  // all_threads list, so we have to be careful to treat them as roots
+  // here.
+  { 
+      StgMainThread *m;
+      for (m = main_threads; m != NULL; m = m->link) {
+         switch (m->tso->what_next) {
+         case ThreadComplete:
+         case ThreadKilled:
+             evac((StgClosure **)&m->tso);
+             break;
+         default:
+             break;
+         }
+      }
+  }
 }
 
 /* -----------------------------------------------------------------------------
@@ -2511,7 +2511,7 @@ GetRoots(evac_fn evac)
    This needs to be protected by the GC condition variable above.  KH.
    -------------------------------------------------------------------------- */
 
-void (*extra_roots)(evac_fn);
+static void (*extra_roots)(evac_fn);
 
 void
 performGC(void)
@@ -3282,7 +3282,6 @@ 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
@@ -3297,6 +3296,8 @@ raiseAsync(StgTSO *tso, StgClosure *exception)
     nat i;
     StgAP_UPD * ap;
 
+    ASSERT((P_)su > (P_)sp);
+    
     /* If we find a CATCH_FRAME, and we've got an exception to raise,
      * then build the THUNK raise(exception), and leave it on
      * top of the CATCH_FRAME ready to enter.
@@ -3344,8 +3345,6 @@ raiseAsync(StgTSO *tso, StgClosure *exception)
      */
     ap = (StgAP_UPD *)allocate(AP_sizeW(words));
     
-    ASSERT(words >= 0);
-    
     ap->n_args = words;
     ap->fun    = (StgClosure *)sp[0];
     sp++;
@@ -3568,11 +3567,12 @@ detectBlackHoles( void )
 //@subsection Debugging Routines
 
 /* -----------------------------------------------------------------------------
-   Debugging: why is a thread blocked
+ * Debugging: why is a thread blocked
+ * [Also provides useful information when debugging threaded programs
+ *  at the Haskell source code level, so enable outside of DEBUG. --sof 7/02]
    -------------------------------------------------------------------------- */
 
-#ifdef DEBUG
-
+static
 void
 printThreadBlockage(StgTSO *tso)
 {
@@ -3620,6 +3620,7 @@ printThreadBlockage(StgTSO *tso)
   }
 }
 
+static
 void
 printThreadStatus(StgTSO *tso)
 {
@@ -3646,15 +3647,15 @@ printAllThreads(void)
   ullong_format_string(TIME_ON_PROC(CurrentProc), 
                       time_string, rtsFalse/*no commas!*/);
 
-  sched_belch("all threads at [%s]:", time_string);
+  fprintf(stderr, "all threads at [%s]:\n", time_string);
 # elif defined(PAR)
   char time_string[TIME_STR_LEN], node_str[NODE_STR_LEN];
   ullong_format_string(CURRENT_TIME,
                       time_string, rtsFalse/*no commas!*/);
 
-  sched_belch("all threads at [%s]:", time_string);
+  fprintf(stderr,"all threads at [%s]:\n", time_string);
 # else
-  sched_belch("all threads:");
+  fprintf(stderr,"all threads:\n");
 # endif
 
   for (t = all_threads; t != END_TSO_QUEUE; t = t->global_link) {
@@ -3666,6 +3667,8 @@ printAllThreads(void)
   }
 }
     
+#ifdef DEBUG
+
 /* 
    Print a whole blocking queue attached to node (debugging only).
 */