[project @ 2005-05-16 14:27:07 by simonmar]
authorsimonmar <unknown>
Mon, 16 May 2005 14:27:08 +0000 (14:27 +0000)
committersimonmar <unknown>
Mon, 16 May 2005 14:27:08 +0000 (14:27 +0000)
Allow the amount of idle time which must pass before we force a major
GC to be configured at runtime with the +RTS -I<secs> option.

The idle GC only happens in the threaded RTS, and it is useful because
it can make finalizers run more promptly, and also detect cases of
deadlock.  Without the idle GC, Haskell computation must be taking
place in order for finalizers to run or deadlock to be detected, and
the only way some Haskell computation can take place is usually by
in-calls.

+RTS -I0 turns off the idle GC, the default is +RTS -I0.3.

We might need to add more tuning if it turns out that the idle GC is
problematic, for example we don't check how long the GC actually took,
and we should probably back off if major GCs are taking too long and
adversely affecting interactive responsiveness.

ghc/includes/RtsFlags.h
ghc/rts/RtsFlags.c
ghc/rts/Timer.c

index d0ab562..a4c6d9b 100644 (file)
@@ -41,6 +41,8 @@ struct GC_FLAGS {
 
     rtsBool ringBell;
     rtsBool frontpanel;
+
+    int idleGCDelayTicks;      /* in milliseconds */
 };
 
 struct DEBUG_FLAGS {  
index 3b88496..b0accf9 100644 (file)
@@ -173,6 +173,7 @@ void initRtsFlagsDefaults(void)
 #ifdef RTS_GTK_FRONTPANEL
     RtsFlags.GcFlags.frontpanel         = rtsFalse;
 #endif
+    RtsFlags.GcFlags.idleGCDelayTicks   = 300 / TICK_MILLISECS; /* ticks */
 
 #ifdef DEBUG
     RtsFlags.DebugFlags.scheduler      = rtsFalse;
@@ -346,6 +347,9 @@ usage_text[] = {
 "  -c<n>    Auto-enable compaction of the oldest generation when live data is",
 "           at least <n>% of the maximum heap size set with -M (default: 30%)",
 "  -c       Enable compaction for all major collections",
+#if defined(RTS_SUPPORTS_THREADS)
+"  -I<sec>  Perform full GC after <sec> idle time (default: 0.3, 0 == off)",
+#endif
 "",
 "  -t<file> One-line GC statistics  (default file: <program>.stat)",
 "  -s<file> Summary  GC statistics  (with -Sstderr going to stderr)",
@@ -793,6 +797,23 @@ error = rtsTrue;
                  break;
 #endif
 
+             case 'I': /* idle GC delay */
+               if (rts_argv[arg][2] == '\0') {
+                 /* use default */
+               } else {
+                   I_ cst; /* tmp */
+
+                   /* Convert to ticks */
+                   cst = (I_) ((atof(rts_argv[arg]+2) * 1000));
+                   if (cst > 0 && cst < TICK_MILLISECS) {
+                       cst = TICK_MILLISECS;
+                   } else {
+                       cst = cst / TICK_MILLISECS;
+                   }
+                   RtsFlags.GcFlags.idleGCDelayTicks = cst;
+               }
+               break;
+
              case 'S':
                  RtsFlags.GcFlags.giveStats = VERBOSE_GC_STATS;
                  goto stats;
index da4c852..99a76c3 100644 (file)
@@ -30,6 +30,9 @@
 /* ticks left before next pre-emptive context switch */
 static int ticks_to_ctxt_switch = 0;
 
+/* idle ticks left before we perform a GC */
+static int ticks_to_gc = 0;
+
 /*
  * Function: handle_tick()
  *
@@ -51,22 +54,28 @@ handle_tick(int unused STG_UNUSED)
 
 #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.
+          * If we've been inactive for idleGCDelayTicks (set by +RTS
+          * -I), 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;
+             ticks_to_gc = RtsFlags.GcFlags.idleGCDelayTicks;
              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
-              */
+             if (ticks_to_gc == 0) break; /* 0 ==> no idle GC */
+             ticks_to_gc--;
+             if (ticks_to_gc == 0) {
+                 ticks_to_gc = RtsFlags.GcFlags.idleGCDelayTicks;
+                 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;