Itimer.c 5.95 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 23
#include "Ticker.h"
#include "posix/Itimer.h"
24
#include "Proftimer.h"
25
#include "Schedule.h"
26
#include "posix/Select.h"
27 28 29 30 31 32 33 34 35 36 37 38

/* 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
39

40 41 42 43
#ifdef HAVE_SIGNAL_H
# include <signal.h>
#endif

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
/* 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
 */
70
#ifdef THREADED_RTS
71 72 73 74 75 76 77
#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
78 79
static
int
sof's avatar
sof committed
80
install_vtalrm_handler(TickProc handle_tick)
sof's avatar
sof committed
81 82 83 84 85 86
{
    struct sigaction action;

    action.sa_handler = handle_tick;

    sigemptyset(&action.sa_mask);
87 88 89 90 91 92 93 94 95 96

#ifdef SA_RESTART
    // specify SA_RESTART.  One consequence if we don't do this is
    // that readline gets confused by the -threaded RTS.  It seems
    // that if a SIGALRM handler is installed without SA_RESTART,
    // readline installs its own SIGALRM signal handler (see
    // readline's signals.c), and this somehow causes readline to go
    // wrong when the input exceeds a single line (try it).
    action.sa_flags = SA_RESTART;
#else
sof's avatar
sof committed
97
    action.sa_flags = 0;
98
#endif
sof's avatar
sof committed
99

100
    return sigaction(ITIMER_SIGNAL, &action, NULL);
sof's avatar
sof committed
101
}
102

sof's avatar
sof committed
103
int
sof's avatar
sof committed
104
startTicker(nat ms, TickProc handle_tick)
105 106
{
# ifndef HAVE_SETITIMER
107
  /*    debugBelch("No virtual timer on this system\n"); */
108 109 110 111
    return -1;
# else
    struct itimerval it;

sof's avatar
sof committed
112
    install_vtalrm_handler(handle_tick);
sof's avatar
sof committed
113

114
#if !defined(THREADED_RTS)
115
    timestamp = getourtimeofday();
116
#endif
117

118 119 120
    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;
121
    return (setitimer(ITIMER_FLAVOUR, &it, NULL));
122 123 124
# endif
}

sof's avatar
sof committed
125
int
sof's avatar
sof committed
126
stopTicker()
sof's avatar
sof committed
127 128
{
# ifndef HAVE_SETITIMER
129
  /*    debugBelch("No virtual timer on this system\n"); */
sof's avatar
sof committed
130 131 132 133 134 135 136
    return -1;
# else
    struct itimerval it;
  
    it.it_value.tv_sec = 0;
    it.it_value.tv_usec = 0;
    it.it_interval = it.it_value;
137
    return (setitimer(ITIMER_FLAVOUR, &it, NULL));
sof's avatar
sof committed
138 139 140
# endif
}

141 142
# if 0
/* This is a potential POSIX version */
sof's avatar
sof committed
143
int
sof's avatar
sof committed
144
startTicker(nat ms)
145 146 147 148 149
{
    struct sigevent se;
    struct itimerspec it;
    timer_t tid;

150
#if !defined(THREADED_RTS)
151
    timestamp = getourtimeofday();
152
#endif
153

154
    se.sigev_notify = SIGEV_SIGNAL;
155 156
    se.sigev_signo = ITIMER_SIGNAL;
    se.sigev_value.sival_int = ITIMER_SIGNAL;
157
    if (timer_create(CLOCK_VIRTUAL, &se, &tid)) {
158
	barf("can't create virtual timer");
159 160 161 162
    }
    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
163
    return timer_settime(tid, TIMER_RELTIME, &it, NULL);
164 165
}

sof's avatar
sof committed
166
int
sof's avatar
sof committed
167
stopTicker()
sof's avatar
sof committed
168
{
sof's avatar
sof committed
169 170 171
    struct sigevent se;
    struct itimerspec it;
    timer_t tid;
172

173
#if !defined(THREADED_RTS)
sof's avatar
sof committed
174
    timestamp = getourtimeofday();
175
#endif
176

sof's avatar
sof committed
177
    se.sigev_notify = SIGEV_SIGNAL;
178 179
    se.sigev_signo = ITIMER_SIGNAL;
    se.sigev_value.sival_int = ITIMER_SIGNAL;
sof's avatar
sof committed
180 181 182 183 184 185 186
    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);
187
}
sof's avatar
sof committed
188 189
# endif

sof's avatar
sof committed
190 191
#if 0
/* Currently unused */
192 193 194 195 196 197
void
block_vtalrm_signal(void)
{
    sigset_t signals;
    
    sigemptyset(&signals);
198
    sigaddset(&signals, ITIMER_SIGNAL);
199 200 201 202 203 204 205 206 207 208

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

void
unblock_vtalrm_signal(void)
{
    sigset_t signals;
    
    sigemptyset(&signals);
209
    sigaddset(&signals, ITIMER_SIGNAL);
210 211 212

    (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
}
sof's avatar
sof committed
213
#endif
andy's avatar
andy committed
214

215 216 217
/* gettimeofday() takes around 1us on our 500MHz PIII.  Since we're
 * only calling it 50 times/s, it shouldn't have any great impact.
 */
218
lnat
andy's avatar
andy committed
219 220 221 222
getourtimeofday(void)
{
  struct timeval tv;
  gettimeofday(&tv, (struct timezone *) NULL);
223
  	// cast to lnat because nat may be 64 bit when int is only 32 bit
224 225
  return ((lnat)tv.tv_sec * 1000 / RtsFlags.MiscFlags.tickInterval +
	  (lnat)tv.tv_usec / (RtsFlags.MiscFlags.tickInterval * 1000));
andy's avatar
andy committed
226
}