RTS tidyup sweep, first phase
[ghc-hetmet.git] / rts / posix / Itimer.c
index 715cf5a..3a09e80 100644 (file)
@@ -1,6 +1,6 @@
 /* -----------------------------------------------------------------------------
  *
- * (c) The GHC Team, 1995-1999
+ * (c) The GHC Team, 1995-2007
  *
  * Interval timer for profiling and pre-emptive scheduling.
  *
  * Hence, we use the old-fashioned @setitimer@ that just about everyone seems
  * to support.  So much for standards.
  */
+
+#include "PosixSource.h"
 #include "Rts.h"
-#include "RtsFlags.h"
-#include "Timer.h"
+
 #include "Ticker.h"
-#include "posix/Itimer.h"
+#include "Itimer.h"
 #include "Proftimer.h"
-#include "Storage.h"
 #include "Schedule.h"
-#include "posix/Select.h"
+#include "Select.h"
 
 /* As recommended in the autoconf manual */
 # ifdef TIME_WITH_SYS_TIME
@@ -42,6 +42,8 @@
 # include <signal.h>
 #endif
 
+#include <string.h>
+
 /* Major bogosity:
  * 
  * In the threaded RTS, we can't set the virtual timer because the
@@ -52,9 +54,7 @@
  *
  * (1) tick in realtime.  Not very good, because this ticker is used for
  *     profiling, and this will give us unreliable time profiling
- *     results.  Furthermore, this requires picking a single OS thread
- *     to be the timekeeper, which is a bad idea because the thread in
- *     question might just be making a temporary call into Haskell land.
+ *     results.
  *
  * (2) save/restore the virtual timer around excursions into STG land.
  *     Sounds great, but I tried it and the resolution of the virtual timer
  *
  * For now, we're using (1), but this needs a better solution. --SDM
  */
-#ifdef THREADED_RTS
-#define ITIMER_FLAVOUR  ITIMER_REAL
-#define ITIMER_SIGNAL   SIGALRM
+
+#if defined(USE_TIMER_CREATE)
+
+#  define ITIMER_SIGNAL SIGVTALRM
+#  ifdef THREADED_RTS
+#    define TIMER_FLAVOUR CLOCK_REALTIME
+#  else
+#    define TIMER_FLAVOUR CLOCK_PROCESS_CPUTIME_ID
+#  endif
+
+#elif defined(HAVE_SETITIMER)
+
+#  if defined(THREADED_RTS) || !defined(HAVE_SETITIMER_VIRTUAL)
+//   Oh dear, we have to use SIGALRM if there's no timer_create and
+//   we're using the THREADED_RTS.  This leads to problems, see bug #850.
+//   We also use it if we don't have a virtual timer (trac #2883).
+#    define ITIMER_SIGNAL  SIGALRM
+#    define ITIMER_FLAVOUR ITIMER_REAL
+#  else
+#    define ITIMER_SIGNAL  SIGVTALRM
+#    define ITIMER_FLAVOUR ITIMER_VIRTUAL
+#  endif
+
 #else
-#define ITIMER_FLAVOUR  ITIMER_VIRTUAL
-#define ITIMER_SIGNAL   SIGVTALRM
+
+#  error No way to set an interval timer.
+
 #endif
 
+#if defined(USE_TIMER_CREATE)
+static timer_t timer;
+#endif
+
+static nat itimer_interval = 50;
+
 static
-int
+void
 install_vtalrm_handler(TickProc handle_tick)
 {
     struct sigaction action;
@@ -98,120 +125,109 @@ install_vtalrm_handler(TickProc handle_tick)
     action.sa_flags = 0;
 #endif
 
-    return sigaction(ITIMER_SIGNAL, &action, NULL);
+    if (sigaction(ITIMER_SIGNAL, &action, NULL) == -1) {
+        sysErrorBelch("sigaction");
+        stg_exit(EXIT_FAILURE);
+    }
 }
 
-int
-startTicker(nat ms, TickProc handle_tick)
+void
+initTicker (nat ms, TickProc handle_tick)
 {
-# ifndef HAVE_SETITIMER
-  /*    debugBelch("No virtual timer on this system\n"); */
-    return -1;
-# else
-    struct itimerval it;
-
     install_vtalrm_handler(handle_tick);
 
 #if !defined(THREADED_RTS)
     timestamp = getourtimeofday();
 #endif
 
-    it.it_value.tv_sec = ms / 1000;
-    it.it_value.tv_usec = 1000 * (ms - (1000 * it.it_value.tv_sec));
-    it.it_interval = it.it_value;
-    return (setitimer(ITIMER_FLAVOUR, &it, NULL));
-# endif
-}
+    itimer_interval = ms;
 
-int
-stopTicker()
-{
-# ifndef HAVE_SETITIMER
-  /*    debugBelch("No virtual timer on this system\n"); */
-    return -1;
-# else
-    struct itimerval it;
-  
-    it.it_value.tv_sec = 0;
-    it.it_value.tv_usec = 0;
-    it.it_interval = it.it_value;
-    return (setitimer(ITIMER_FLAVOUR, &it, NULL));
-# endif
-}
+#if defined(USE_TIMER_CREATE)
+    {
+        struct sigevent ev;
 
-# if 0
-/* This is a potential POSIX version */
-int
-startTicker(nat ms)
-{
-    struct sigevent se;
-    struct itimerspec it;
-    timer_t tid;
+       // Keep programs like valgrind happy
+       memset(&ev, 0, sizeof(ev));
 
-#if !defined(THREADED_RTS)
-    timestamp = getourtimeofday();
+        ev.sigev_notify = SIGEV_SIGNAL;
+        ev.sigev_signo  = ITIMER_SIGNAL;
+
+        if (timer_create(TIMER_FLAVOUR, &ev, &timer) != 0) {
+            sysErrorBelch("timer_create");
+            stg_exit(EXIT_FAILURE);
+        }
+    }
 #endif
+}
 
-    se.sigev_notify = SIGEV_SIGNAL;
-    se.sigev_signo = ITIMER_SIGNAL;
-    se.sigev_value.sival_int = ITIMER_SIGNAL;
-    if (timer_create(CLOCK_VIRTUAL, &se, &tid)) {
-       barf("can't create virtual timer");
+void
+startTicker(void)
+{
+#if defined(USE_TIMER_CREATE)
+    {
+        struct itimerspec it;
+        
+        it.it_value.tv_sec = itimer_interval / 1000;
+        it.it_value.tv_nsec = (itimer_interval % 1000) * 1000000;
+        it.it_interval = it.it_value;
+        
+        if (timer_settime(timer, 0, &it, NULL) != 0) {
+            sysErrorBelch("timer_settime");
+            stg_exit(EXIT_FAILURE);
+        }
     }
-    it.it_value.tv_sec = ms / 1000;
-    it.it_value.tv_nsec = 1000000 * (ms - 1000 * it.it_value.tv_sec);
-    it.it_interval = it.it_value;
-    return timer_settime(tid, TIMER_RELTIME, &it, NULL);
+#else
+    {
+        struct itimerval it;
+
+        it.it_value.tv_sec = itimer_interval / 1000;
+        it.it_value.tv_usec = (itimer_interval % 1000) * 1000;
+        it.it_interval = it.it_value;
+        
+        if (setitimer(ITIMER_FLAVOUR, &it, NULL) != 0) {
+            sysErrorBelch("setitimer");
+            stg_exit(EXIT_FAILURE);
+        }
+    }
+#endif
 }
 
-int
-stopTicker()
+void
+stopTicker(void)
 {
-    struct sigevent se;
+#if defined(USE_TIMER_CREATE)
     struct itimerspec it;
-    timer_t tid;
 
-#if !defined(THREADED_RTS)
-    timestamp = getourtimeofday();
-#endif
-
-    se.sigev_notify = SIGEV_SIGNAL;
-    se.sigev_signo = ITIMER_SIGNAL;
-    se.sigev_value.sival_int = ITIMER_SIGNAL;
-    if (timer_create(CLOCK_VIRTUAL, &se, &tid)) {
-       barf("can't create virtual timer");
-    }
     it.it_value.tv_sec = 0;
     it.it_value.tv_nsec = 0;
     it.it_interval = it.it_value;
-    return timer_settime(tid, TIMER_RELTIME, &it, NULL);
-}
-# endif
 
-#if 0
-/* Currently unused */
-void
-block_vtalrm_signal(void)
-{
-    sigset_t signals;
-    
-    sigemptyset(&signals);
-    sigaddset(&signals, ITIMER_SIGNAL);
+    if (timer_settime(timer, 0, &it, NULL) != 0) {
+        sysErrorBelch("timer_settime");
+        stg_exit(EXIT_FAILURE);
+    }
+#else
+    struct itimerval it;
 
-    (void) sigprocmask(SIG_BLOCK, &signals, NULL);
+    it.it_value.tv_sec = 0;
+    it.it_value.tv_usec = 0;
+    it.it_interval = it.it_value;
+
+    if (setitimer(ITIMER_FLAVOUR, &it, NULL) != 0) {
+        sysErrorBelch("setitimer");
+        stg_exit(EXIT_FAILURE);
+    }
+#endif
 }
 
 void
-unblock_vtalrm_signal(void)
+exitTicker(void)
 {
-    sigset_t signals;
-    
-    sigemptyset(&signals);
-    sigaddset(&signals, ITIMER_SIGNAL);
-
-    (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
-}
+#if defined(USE_TIMER_CREATE)
+    timer_delete(timer);
+    // ignore errors - we don't really care if it fails.
 #endif
+}
 
 /* gettimeofday() takes around 1us on our 500MHz PIII.  Since we're
  * only calling it 50 times/s, it shouldn't have any great impact.
@@ -220,8 +236,12 @@ lnat
 getourtimeofday(void)
 {
   struct timeval tv;
+  nat interval;
+  interval = RtsFlags.MiscFlags.tickInterval;
+  if (interval == 0) { interval = 50; }
   gettimeofday(&tv, (struct timezone *) NULL);
-       // cast to lnat because nat may be 64 bit when int is only 32 bit
-  return ((lnat)tv.tv_sec * 1000 / RtsFlags.MiscFlags.tickInterval +
-         (lnat)tv.tv_usec / (RtsFlags.MiscFlags.tickInterval * 1000));
+
+  // Avoid overflow when we multiply seconds by 1000.  See #2848
+  return (lnat)((StgWord64)tv.tv_sec * 1000 / interval +
+                (StgWord64)tv.tv_usec / (interval * 1000));
 }