[project @ 2000-08-03 11:28:35 by simonmar]
[ghc-hetmet.git] / ghc / rts / Schedule.c
index 1f78e73..ed0389f 100644 (file)
@@ -1,5 +1,5 @@
 /* ---------------------------------------------------------------------------
- * $Id: Schedule.c,v 1.64 2000/04/05 15:28:59 simonmar Exp $
+ * $Id: Schedule.c,v 1.74 2000/08/03 11:28:35 simonmar Exp $
  *
  * (c) The GHC Team, 1998-2000
  *
@@ -232,6 +232,8 @@ static StgTSO * createThread_     ( nat size, rtsBool have_lock, StgInt pri );
 static StgTSO * createThread_     ( nat size, rtsBool have_lock );
 #endif
 
+static void     detectBlackHoles  ( void );
+
 #ifdef DEBUG
 static void sched_belch(char *s, ...);
 #endif
@@ -514,44 +516,54 @@ schedule( void )
     }
     
     /* check for signals each time around the scheduler */
-#ifndef __MINGW32__
+#ifndef mingw32_TARGET_OS
     if (signals_pending()) {
       start_signal_handlers();
     }
 #endif
 
-    /* Detect deadlock: when we have no threads to run, there are
-     * no threads waiting on I/O or sleeping, and all the other
-     * tasks are waiting for work, we must have a deadlock.  Inform
-     * all the main threads.
+    /* 
+     * Detect deadlock: when we have no threads to run, there are no
+     * threads waiting on I/O or sleeping, and all the other tasks are
+     * waiting for work, we must have a deadlock of some description.
+     *
+     * We first try to find threads blocked on themselves (ie. black
+     * holes), and generate NonTermination exceptions where necessary.
+     *
+     * If no threads are black holed, we have a deadlock situation, so
+     * inform all the main threads.
      */
 #ifdef SMP
     if (blocked_queue_hd == END_TSO_QUEUE
        && run_queue_hd == END_TSO_QUEUE
-       && (n_free_capabilities == RtsFlags.ParFlags.nNodes)
-       ) {
-      StgMainThread *m;
-      for (m = main_threads; m != NULL; m = m->link) {
-         m->ret = NULL;
-         m->stat = Deadlock;
-         pthread_cond_broadcast(&m->wakeup);
-      }
-      main_threads = NULL;
+       && (n_free_capabilities == RtsFlags.ParFlags.nNodes))
+    {
+       IF_DEBUG(scheduler, sched_belch("deadlocked, checking for black holes..."));
+       detectBlackHoles();
+       if (run_queue_hd == END_TSO_QUEUE) {
+           StgMainThread *m;
+           for (m = main_threads; m != NULL; m = m->link) {
+               m->ret = NULL;
+               m->stat = Deadlock;
+               pthread_cond_broadcast(&m->wakeup);
+           }
+           main_threads = NULL;
+       }
     }
 #else /* ! SMP */
-    /* 
-       In GUM all non-main PEs come in here with no work;
-       we ignore multiple main threads for now 
-
     if (blocked_queue_hd == END_TSO_QUEUE
-       && run_queue_hd == END_TSO_QUEUE) {
-      StgMainThread *m = main_threads;
-      m->ret = NULL;
-      m->stat = Deadlock;
-      main_threads = m->link;
-      return;
+       && run_queue_hd == END_TSO_QUEUE)
+    {
+       IF_DEBUG(scheduler, sched_belch("deadlocked, checking for black holes..."));
+       detectBlackHoles();
+       if (run_queue_hd == END_TSO_QUEUE) {
+           StgMainThread *m = main_threads;
+           m->ret = NULL;
+           m->stat = Deadlock;
+           main_threads = m->link;
+           return;
+       }
     }
-    */
 #endif
 
 #ifdef SMP
@@ -820,6 +832,7 @@ schedule( void )
   
     /* grab a thread from the run queue
      */
+    ASSERT(run_queue_hd != END_TSO_QUEUE);
     t = POP_RUN_QUEUE();
     IF_DEBUG(sanity,checkTSO(t));
 
@@ -837,21 +850,21 @@ schedule( void )
     
     cap->rCurrentTSO = t;
     
-    /* set the context_switch flag
+    /* context switches are now initiated by the timer signal, unless
+     * the user specified "context switch as often as possible", with
+     * +RTS -C0
      */
-    if (run_queue_hd == END_TSO_QUEUE)
-      context_switch = 0;
+    if (RtsFlags.ConcFlags.ctxtSwitchTicks == 0
+       && (run_queue_hd != END_TSO_QUEUE
+           || blocked_queue_hd != END_TSO_QUEUE))
+       context_switch = 1;
     else
-      context_switch = 1;
+       context_switch = 0;
 
     RELEASE_LOCK(&sched_mutex);
 
-#if defined(GRAN) || defined(PAR)    
-    IF_DEBUG(scheduler, belch("-->> Running TSO %ld (%p) %s ...", 
+    IF_DEBUG(scheduler, sched_belch("-->> Running TSO %ld (%p) %s ...", 
                              t->id, t, whatNext_strs[t->what_next]));
-#else
-    IF_DEBUG(scheduler,sched_belch("running thread %d", t->id));
-#endif
 
     /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
     /* Run the current thread 
@@ -979,7 +992,9 @@ schedule( void )
                         t->id, t, whatNext_strs[t->what_next]);
                }
                );
+
       threadPaused(t);
+
       IF_DEBUG(sanity,
               //belch("&& Doing sanity check on yielding TSO %ld.", t->id);
               checkTSO(t));
@@ -1044,7 +1059,7 @@ schedule( void )
        * case it'll be on the relevant queue already.
        */
       IF_DEBUG(scheduler,
-              fprintf(stderr, "--<< thread %d (%p) stopped ", t->id, t);
+              fprintf(stderr, "--<< thread %d (%p) stopped: ", t->id, t);
               printThreadBlockage(t);
               fprintf(stderr, "\n"));
 
@@ -1062,8 +1077,10 @@ schedule( void )
        * more main threads, we probably need to stop all the tasks until
        * we get a new one.
        */
+      /* We also end up here if the thread kills itself with an
+       * uncaught exception, see Exception.hc.
+       */
       IF_DEBUG(scheduler,belch("--++ thread %d (%p) finished", t->id, t));
-      t->what_next = ThreadComplete;
 #if defined(GRAN)
       endThread(t, CurrentProc); // clean-up the thread
 #elif defined(PAR)
@@ -1074,7 +1091,7 @@ schedule( void )
       break;
       
     default:
-      barf("doneThread: invalid thread return code");
+      barf("schedule: invalid thread return code %d", (int)ret);
     }
     
 #ifdef SMP
@@ -1097,7 +1114,7 @@ schedule( void )
 #ifdef SMP
       IF_DEBUG(scheduler,sched_belch("doing GC"));
 #endif
-      GarbageCollect(GetRoots);
+      GarbageCollect(GetRoots,rtsFalse);
       ready_to_gc = rtsFalse;
 #ifdef SMP
       pthread_cond_broadcast(&gc_pending_cond);
@@ -1176,7 +1193,7 @@ suspendThread( Capability *cap )
   ACQUIRE_LOCK(&sched_mutex);
 
   IF_DEBUG(scheduler,
-          sched_belch("thread %d did a _ccall_gc\n", cap->rCurrentTSO->id));
+          sched_belch("thread %d did a _ccall_gc", cap->rCurrentTSO->id));
 
   threadPaused(cap->rCurrentTSO);
   cap->rCurrentTSO->link = suspended_ccalling_threads;
@@ -1358,9 +1375,6 @@ createThread_(nat size, rtsBool have_lock)
   SET_HDR((StgClosure*)tso->sp,(StgInfoTable *)&stg_stop_thread_info,CCS_SYSTEM);
   tso->su = (StgUpdateFrame*)tso->sp;
 
-  IF_DEBUG(scheduler,belch("---- Initialised TSO %ld (%p), stack size = %lx words", 
-                          tso->id, tso, tso->stack_size));
-
   // ToDo: check this
 #if defined(GRAN)
   tso->link = END_TSO_QUEUE;
@@ -1510,7 +1524,9 @@ scheduleThread(StgTSO *tso)
   PUSH_ON_RUN_QUEUE(tso);
   THREAD_RUNNABLE();
 
+#if 0
   IF_DEBUG(scheduler,printTSO(tso));
+#endif
   RELEASE_LOCK(&sched_mutex);
 }
 
@@ -1581,7 +1597,13 @@ initScheduler(void)
   context_switch = 0;
   interrupted    = 0;
 
-  enteredCAFs = END_CAF_LIST;
+  RtsFlags.ConcFlags.ctxtSwitchTicks =
+      RtsFlags.ConcFlags.ctxtSwitchTime / TICK_MILLISECS;
+
+#ifdef INTERPRETER
+  ecafList = END_ECAF_LIST;
+  clearECafTable();
+#endif
 
   /* Install the SIGHUP handler */
 #ifdef SMP
@@ -1711,6 +1733,33 @@ exitScheduler( void )
  * will be in the main_thread struct.
  * -------------------------------------------------------------------------- */
 
+int 
+howManyThreadsAvail ( void )
+{
+   int i = 0;
+   StgTSO* q;
+   for (q = run_queue_hd; q != END_TSO_QUEUE; q = q->link)
+      i++;
+   for (q = blocked_queue_hd; q != END_TSO_QUEUE; q = q->link)
+      i++;
+   return i;
+}
+
+void
+finishAllThreads ( void )
+{
+   do {
+      while (run_queue_hd != END_TSO_QUEUE) {
+         waitThread ( run_queue_hd, NULL );
+      }
+      while (blocked_queue_hd != END_TSO_QUEUE) {
+         waitThread ( blocked_queue_hd, NULL );
+      }
+   } while 
+      (blocked_queue_hd != END_TSO_QUEUE || 
+        run_queue_hd != END_TSO_QUEUE);
+}
+
 SchedulerStatus
 waitThread(StgTSO *tso, /*out*/StgClosure **ret)
 {
@@ -1952,7 +2001,13 @@ void (*extra_roots)(void);
 void
 performGC(void)
 {
-  GarbageCollect(GetRoots);
+  GarbageCollect(GetRoots,rtsFalse);
+}
+
+void
+performMajorGC(void)
+{
+  GarbageCollect(GetRoots,rtsTrue);
 }
 
 static void
@@ -1967,7 +2022,7 @@ performGCWithRoots(void (*get_roots)(void))
 {
   extra_roots = get_roots;
 
-  GarbageCollect(AllRoots);
+  GarbageCollect(AllRoots,rtsFalse);
 }
 
 /* -----------------------------------------------------------------------------
@@ -2701,6 +2756,7 @@ raiseAsync(StgTSO *tso, StgClosure *exception)
       sp[0] = (W_)ap;
       tso->sp = sp;
       tso->what_next = ThreadEnterGHC;
+      IF_DEBUG(sanity, checkTSO(tso));
       return;
     }
 
@@ -2855,6 +2911,61 @@ resurrectThreads( StgTSO *threads )
   }
 }
 
+/* -----------------------------------------------------------------------------
+ * Blackhole detection: if we reach a deadlock, test whether any
+ * threads are blocked on themselves.  Any threads which are found to
+ * be self-blocked get sent a NonTermination exception.
+ *
+ * This is only done in a deadlock situation in order to avoid
+ * performance overhead in the normal case.
+ * -------------------------------------------------------------------------- */
+
+static void
+detectBlackHoles( void )
+{
+    StgTSO *t = all_threads;
+    StgUpdateFrame *frame;
+    StgClosure *blocked_on;
+
+    for (t = all_threads; t != END_TSO_QUEUE; t = t->global_link) {
+
+       if (t->why_blocked != BlockedOnBlackHole) {
+           continue;
+       }
+
+       blocked_on = t->block_info.closure;
+
+       for (frame = t->su; ; frame = frame->link) {
+           switch (get_itbl(frame)->type) {
+
+           case UPDATE_FRAME:
+               if (frame->updatee == blocked_on) {
+                   /* We are blocking on one of our own computations, so
+                    * send this thread the NonTermination exception.  
+                    */
+                   IF_DEBUG(scheduler, 
+                            sched_belch("thread %d is blocked on itself", t->id));
+                   raiseAsync(t, (StgClosure *)NonTermination_closure);
+                   goto done;
+               }
+               else {
+                   continue;
+               }
+
+           case CATCH_FRAME:
+           case SEQ_FRAME:
+               continue;
+               
+           case STOP_FRAME:
+               break;
+           }
+           break;
+       }
+
+    done:
+    }   
+}
+
 //@node Debugging Routines, Index, Exception Handling Routines, Main scheduling code
 //@subsection Debugging Routines