Itimer.c 5.25 KB
Newer Older
1 2
/* -----------------------------------------------------------------------------
 *
3
 * (c) The GHC Team, 1995-1999
4 5 6 7
 *
 * Interval timer for profiling and pre-emptive scheduling.
 *
 * ---------------------------------------------------------------------------*/
8

9 10 11 12 13 14 15 16 17 18 19
/*
 * The interval timer is used for profiling and for context switching in the
 * threaded build.  Though POSIX 1003.1b includes a standard interface for
 * such things, no one really seems to be implementing them yet.  Even 
 * Solaris 2.3 only seems to provide support for @CLOCK_REAL@, whereas we're
 * keen on getting access to @CLOCK_VIRTUAL@.
 * 
 * Hence, we use the old-fashioned @setitimer@ that just about everyone seems
 * to support.  So much for standards.
 */
#include "Rts.h"
20
#include "RtsFlags.h"
sof's avatar
sof committed
21
#include "Timer.h"
22
#include "Itimer.h"
23
#include "Proftimer.h"
24
#include "Schedule.h"
25 26 27 28 29 30 31 32 33 34 35 36

/* As recommended in the autoconf manual */
# ifdef TIME_WITH_SYS_TIME
#  include <sys/time.h>
#  include <time.h>
# else
#  ifdef HAVE_SYS_TIME_H
#   include <sys/time.h>
#  else
#   include <time.h>
#  endif
# endif
sof's avatar
sof committed
37

38 39 40 41
#ifdef HAVE_SIGNAL_H
# include <signal.h>
#endif

42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
/* Major bogosity:
 * 
 * In the threaded RTS, we can't set the virtual timer because the
 * thread which has the virtual timer might be sitting waiting for a
 * capability, and the virtual timer only ticks in CPU time.
 *
 * So, possible solutions:
 *
 * (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.
 *
 * (2) save/restore the virtual timer around excursions into STG land.
 *     Sounds great, but I tried it and the resolution of the virtual timer
 *     isn't good enough (on Linux) - most of our excursions fall
 *     within the timer's resolution and we never make any progress.
 *   
 * (3) have a virtual timer in every OS thread.  Might be reasonable,
 *     because most of the time there is only ever one of these
 *     threads running, so it approximates a single virtual timer.
 *     But still quite bogus (and I got crashes when I tried this).
 *
 * For now, we're using (1), but this needs a better solution. --SDM
 */
#ifdef RTS_SUPPORTS_THREADS
#define ITIMER_FLAVOUR  ITIMER_REAL
#define ITIMER_SIGNAL   SIGALRM
#else
#define ITIMER_FLAVOUR  ITIMER_VIRTUAL
#define ITIMER_SIGNAL   SIGVTALRM
#endif

sof's avatar
sof committed
76 77
static
int
sof's avatar
sof committed
78
install_vtalrm_handler(TickProc handle_tick)
sof's avatar
sof committed
79 80 81 82 83 84 85 86
{
    struct sigaction action;

    action.sa_handler = handle_tick;

    sigemptyset(&action.sa_mask);
    action.sa_flags = 0;

87
    return sigaction(ITIMER_SIGNAL, &action, NULL);
sof's avatar
sof committed
88
}
89

sof's avatar
sof committed
90
int
sof's avatar
sof committed
91
startTicker(nat ms, TickProc handle_tick)
92 93
{
# ifndef HAVE_SETITIMER
94
  /*    debugBelch("No virtual timer on this system\n"); */
95 96 97 98
    return -1;
# else
    struct itimerval it;

sof's avatar
sof committed
99
    install_vtalrm_handler(handle_tick);
sof's avatar
sof committed
100

101 102
    timestamp = getourtimeofday();

103 104 105
    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;
106
    return (setitimer(ITIMER_FLAVOUR, &it, NULL));
107 108 109
# endif
}

sof's avatar
sof committed
110
int
sof's avatar
sof committed
111
stopTicker()
sof's avatar
sof committed
112 113
{
# ifndef HAVE_SETITIMER
114
  /*    debugBelch("No virtual timer on this system\n"); */
sof's avatar
sof committed
115 116 117 118 119 120 121
    return -1;
# else
    struct itimerval it;
  
    it.it_value.tv_sec = 0;
    it.it_value.tv_usec = 0;
    it.it_interval = it.it_value;
122
    return (setitimer(ITIMER_FLAVOUR, &it, NULL));
sof's avatar
sof committed
123 124 125
# endif
}

126 127
# if 0
/* This is a potential POSIX version */
sof's avatar
sof committed
128
int
sof's avatar
sof committed
129
startTicker(nat ms)
130 131 132 133 134
{
    struct sigevent se;
    struct itimerspec it;
    timer_t tid;

135 136
    timestamp = getourtimeofday();

137
    se.sigev_notify = SIGEV_SIGNAL;
138 139
    se.sigev_signo = ITIMER_SIGNAL;
    se.sigev_value.sival_int = ITIMER_SIGNAL;
140
    if (timer_create(CLOCK_VIRTUAL, &se, &tid)) {
141
	barf("can't create virtual timer");
142 143 144 145
    }
    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;
sof's avatar
sof committed
146
    return timer_settime(tid, TIMER_RELTIME, &it, NULL);
147 148
}

sof's avatar
sof committed
149
int
sof's avatar
sof committed
150
stopTicker()
sof's avatar
sof committed
151
{
sof's avatar
sof committed
152 153 154
    struct sigevent se;
    struct itimerspec it;
    timer_t tid;
155

sof's avatar
sof committed
156
    timestamp = getourtimeofday();
157

sof's avatar
sof committed
158
    se.sigev_notify = SIGEV_SIGNAL;
159 160
    se.sigev_signo = ITIMER_SIGNAL;
    se.sigev_value.sival_int = ITIMER_SIGNAL;
sof's avatar
sof committed
161 162 163 164 165 166 167
    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);
168
}
sof's avatar
sof committed
169 170
# endif

sof's avatar
sof committed
171 172
#if 0
/* Currently unused */
173 174 175 176 177 178
void
block_vtalrm_signal(void)
{
    sigset_t signals;
    
    sigemptyset(&signals);
179
    sigaddset(&signals, ITIMER_SIGNAL);
180 181 182 183 184 185 186 187 188 189

    (void) sigprocmask(SIG_BLOCK, &signals, NULL);
}

void
unblock_vtalrm_signal(void)
{
    sigset_t signals;
    
    sigemptyset(&signals);
190
    sigaddset(&signals, ITIMER_SIGNAL);
191 192 193

    (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
}
sof's avatar
sof committed
194
#endif
195

196 197 198
/* gettimeofday() takes around 1us on our 500MHz PIII.  Since we're
 * only calling it 50 times/s, it shouldn't have any great impact.
 */
199 200 201 202 203
unsigned int 
getourtimeofday(void)
{
  struct timeval tv;
  gettimeofday(&tv, (struct timezone *) NULL);
204 205
  return (tv.tv_sec * TICK_FREQUENCY +
	  tv.tv_usec * TICK_FREQUENCY / 1000000);
206
}
sof's avatar
sof committed
207