/* -----------------------------------------------------------------------------
*
- * (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
# include <signal.h>
#endif
+#include <string.h>
+
/* Major bogosity:
*
* In the threaded RTS, we can't set the virtual timer because the
* For now, we're using (1), but this needs a better solution. --SDM
*/
-#if defined(HAVE_TIMER_CREATE) && defined(HAVE_TIMER_SETTIME)
+#if defined(USE_TIMER_CREATE)
-# define USE_TIMER_CREATE
# define ITIMER_SIGNAL SIGVTALRM
# ifdef THREADED_RTS
# define TIMER_FLAVOUR CLOCK_REALTIME
#elif defined(HAVE_SETITIMER)
-# define USE_ITIMER
-# ifdef THREADED_RTS
+# 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
#endif
#if defined(USE_TIMER_CREATE)
-timer_t timer;
+static timer_t timer;
#endif
+static nat itimer_interval = 50;
+
static
void
install_vtalrm_handler(TickProc handle_tick)
}
void
-startTicker(nat ms, TickProc handle_tick)
+initTicker (nat ms, TickProc handle_tick)
{
install_vtalrm_handler(handle_tick);
timestamp = getourtimeofday();
#endif
+ itimer_interval = ms;
+
#if defined(USE_TIMER_CREATE)
{
- struct itimerspec it;
struct sigevent ev;
+ // Keep programs like valgrind happy
+ memset(&ev, 0, sizeof(ev));
+
ev.sigev_notify = SIGEV_SIGNAL;
ev.sigev_signo = ITIMER_SIGNAL;
-
- it.it_value.tv_sec = ms / 1000;
- it.it_value.tv_nsec = (ms % 1000) * 1000000;
- it.it_interval = it.it_value;
-
+
if (timer_create(TIMER_FLAVOUR, &ev, &timer) != 0) {
sysErrorBelch("timer_create");
stg_exit(EXIT_FAILURE);
}
+ }
+#endif
+}
+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);
{
struct itimerval it;
- it.it_value.tv_sec = ms / 1000;
- it.it_value.tv_usec = (ms % 1000) * 1000;
+ 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) {
#endif
}
-#if 0
-/* Currently unused */
void
-block_vtalrm_signal(void)
+exitTicker(void)
{
- sigset_t signals;
-
- sigemptyset(&signals);
- sigaddset(&signals, ITIMER_SIGNAL);
-
- (void) sigprocmask(SIG_BLOCK, &signals, NULL);
-}
-
-void
-unblock_vtalrm_signal(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.
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));
}