/* flag set by signal handler to precipitate a context switch */
int context_switch = 0;
+/* flag that tracks whether we have done any execution in this time slice. */
+nat recent_activity = ACTIVITY_YES;
+
/* if this flag is set as well, give up execution */
rtsBool interrupted = rtsFalse;
errno = t->saved_errno;
cap->r.rInHaskell = rtsTrue;
+ recent_activity = ACTIVITY_YES;
+
switch (prev_what_next) {
case ThreadKilled:
static void
scheduleDetectDeadlock(void)
{
+
+#if defined(PARALLEL_HASKELL)
+ // ToDo: add deadlock detection in GUM (similar to SMP) -- HWL
+ return;
+#endif
+
/*
* Detect deadlock: when we have no threads to run, there are no
* threads blocked, waiting for I/O, or sleeping, and all the
*/
if ( EMPTY_THREAD_QUEUES() )
{
-#if !defined(PARALLEL_HASKELL) && !defined(RTS_SUPPORTS_THREADS)
+#if defined(RTS_SUPPORTS_THREADS)
+ /*
+ * In the threaded RTS, we only check for deadlock if there
+ * has been no activity in a complete timeslice. This means
+ * we won't eagerly start a full GC just because we don't have
+ * any threads to run currently.
+ */
+ if (recent_activity != ACTIVITY_INACTIVE) return;
+#endif
+
IF_DEBUG(scheduler, sched_belch("deadlocked, forcing major GC..."));
// Garbage collection can release some new threads due to
// exception. Any threads thus released will be immediately
// runnable.
GarbageCollect(GetRoots,rtsTrue);
+ recent_activity = ACTIVITY_DONE_GC;
if ( !EMPTY_RUN_QUEUE() ) return;
-#if defined(RTS_USER_SIGNALS)
+#if defined(RTS_USER_SIGNALS) && !defined(RTS_SUPPORTS_THREADS)
/* If we have user-installed signal handlers, then wait
* for signals to arrive rather then bombing out with a
* deadlock.
}
#endif
+#if !defined(RTS_SUPPORTS_THREADS)
/* Probably a real deadlock. Send the current main thread the
* Deadlock exception (or in the SMP build, send *all* main
* threads the deadlock exception, since none of them can make
barf("deadlock: main thread blocked in a strange way");
}
}
-
-#elif defined(RTS_SUPPORTS_THREADS)
- // ToDo: add deadlock detection in threaded RTS
-#elif defined(PARALLEL_HASKELL)
- // ToDo: add deadlock detection in GUM (similar to SMP) -- HWL
#endif
}
}
interrupted = 1;
context_switch = 1;
threadRunnable();
+ /* ToDo: if invoked from a signal handler, this threadRunnable
+ * only works if there's another thread (not this one) waiting to
+ * be woken up.
+ */
}
/* -----------------------------------------------------------------------------
extern int RTS_VAR(context_switch);
extern rtsBool RTS_VAR(interrupted);
+/*
+ * flag that tracks whether we have done any execution in this time slice.
+ */
+#define ACTIVITY_YES 0 /* there has been activity in the current slice */
+#define ACTIVITY_MAYBE_NO 1 /* no activity in the current slice */
+#define ACTIVITY_INACTIVE 2 /* a complete slice has passed with no activity */
+#define ACTIVITY_DONE_GC 3 /* like 2, but we've done a GC too */
+extern nat recent_activity;
+
/* In Select.c */
extern lnat RTS_VAR(timestamp);
#include "Proftimer.h"
#include "Schedule.h"
#include "Timer.h"
+#include "Capability.h"
#if !defined(mingw32_HOST_OS)
#include "Itimer.h"
if (ticks_to_ctxt_switch <= 0) {
ticks_to_ctxt_switch = RtsFlags.ConcFlags.ctxtSwitchTicks;
context_switch = 1; /* schedule a context switch */
+
+#if defined(RTS_SUPPORTS_THREADS)
+ /*
+ * If we've been inactive for a whole time slice, tell the
+ * scheduler to wake up and do a GC, to check for threads
+ * that are deadlocked.
+ */
+ switch (recent_activity) {
+ case ACTIVITY_YES:
+ recent_activity = ACTIVITY_MAYBE_NO;
+ break;
+ case ACTIVITY_MAYBE_NO:
+ recent_activity = ACTIVITY_INACTIVE;
+ blackholes_need_checking = rtsTrue;
+ /* hack: re-use the blackholes_need_checking flag */
+ threadRunnable();
+ /* ToDo: this threadRunnable only works if there's
+ * another thread (not this one) waiting to be woken up
+ */
+ break;
+ default:
+ break;
+ }
+#endif
}
}
}