Itimer.c 5.94 KB
Newer Older
1
/* -----------------------------------------------------------------------------
rrt's avatar
rrt committed
2
 * $Id: Itimer.c,v 1.20 2001/02/13 11:10:28 rrt Exp $
3
 *
4
 * (c) The GHC Team, 1995-1999
5 6 7 8
 *
 * Interval timer for profiling and pre-emptive scheduling.
 *
 * ---------------------------------------------------------------------------*/
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.
 */
20

21
#if !defined(_AIX)
22
# define NON_POSIX_SOURCE
23
#endif
24

25
#include "Rts.h"
26
#include "RtsFlags.h"
27
#include "Itimer.h"
28
#include "Proftimer.h"
29
#include "Schedule.h"
30 31 32 33 34 35 36 37 38 39 40 41

/* 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
42 43 44 45

#if HAVE_WINDOWS_H
# include <windows.h>
#endif
sof's avatar
sof committed
46
 
47 48
lnat total_ticks = 0;

49 50 51
/* ticks left before next pre-emptive context switch */
int ticks_to_ctxt_switch = 0;

52 53 54
/* -----------------------------------------------------------------------------
   Tick handler

rrt's avatar
rrt committed
55
   We use the ticker for time profiling.
56 57 58 59

   SMP note: this signal could be delivered to *any* thread.  We have
   to ensure that it doesn't matter which thread actually runs the
   signal handler.
60 61
   -------------------------------------------------------------------------- */

sof's avatar
sof committed
62 63 64 65
static
void
#if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
CALLBACK
rrt's avatar
rrt committed
66 67 68
handle_tick(UINT uID STG_UNUSED, UINT uMsg STG_UNUSED, DWORD dwUser STG_UNUSED,
	    DWORD dw1 STG_UNUSED, DWORD d STG_UNUSED)
#else
69
handle_tick(int unused STG_UNUSED)
rrt's avatar
rrt committed
70
#endif
71 72 73 74
{
  total_ticks++;

#ifdef PROFILING
75
  handleProfTick();
76 77
#endif

78 79 80 81
  /* so we can get a rough indication of the current time at any point
   * without having to call gettimeofday() (see Select.c):
   */
  ticks_since_timestamp++;
82

83 84 85 86 87
  ticks_to_ctxt_switch--;
  if (ticks_to_ctxt_switch <= 0) {
      ticks_to_ctxt_switch = RtsFlags.ConcFlags.ctxtSwitchTicks;
      context_switch = 1;	/* schedule a context switch */
  }
88 89 90
}


91 92 93 94 95 96 97 98 99 100 101
/*
 * Handling timer events under cygwin32 is not done with signal/setitimer.
 * Instead of the two steps of first registering a signal handler to handle
 * \tr{SIGVTALRM} and then start generating them via @setitimer()@, we use
 * the Multimedia API (MM) and its @timeSetEvent@. (Internally, the MM API
 * creates a separate thread that will notify the main thread of timer
 * expiry). -- SOF 7/96
 *
 * 11/98: if the cygwin DLL supports setitimer(), then use it instead.
 */

sof's avatar
sof committed
102
#if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
sof's avatar
sof committed
103

rrt's avatar
rrt committed
104 105
LPTIMECALLBACK vtalrm_cback;

106 107
nat
initialize_virtual_timer(nat ms)
sof's avatar
sof committed
108
{
sof's avatar
sof committed
109 110 111 112
  /* On Win32 setups that don't have support for
     setitimer(), we use the MultiMedia API's timer
     support.
     
rrt's avatar
rrt committed
113
     The delivery of ticks isn't free; the performance hit should be checked.
sof's avatar
sof committed
114
  */
rrt's avatar
rrt committed
115 116
  unsigned int delay;
  static unsigned int vtalrm_id;
sof's avatar
sof committed
117
 
rrt's avatar
rrt committed
118 119 120 121 122 123 124 125 126 127 128 129 130 131
  if (ms) {
    delay = timeBeginPeriod(1);
    if (delay == TIMERR_NOCANDO) { /* error of some sort. */
      return delay;
    }
    vtalrm_id =
      timeSetEvent(ms,      /* event every `delay' milliseconds. */
		   1,       /* precision is within 1 ms */
		   vtalrm_cback,
		   TIME_CALLBACK_FUNCTION,     /* ordinary callback */
		   TIME_PERIODIC);
  } else {
    timeKillEvent(vtalrm_id);
    timeEndPeriod(1);
sof's avatar
sof committed
132
  }
133

sof's avatar
sof committed
134 135 136 137
  return 0;
}
 
#else
138

139 140
nat
initialize_virtual_timer(nat ms)
141 142
{
# ifndef HAVE_SETITIMER
andy's avatar
andy committed
143
  /*    fprintf(stderr, "No virtual timer on this system\n"); */
144 145 146 147
    return -1;
# else
    struct itimerval it;

148 149 150
    timestamp = getourtimeofday();
    ticks_since_timestamp = 0;

151 152 153 154 155 156 157
    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_VIRTUAL, &it, NULL));
# endif
}

sof's avatar
sof committed
158 159
#endif /* !cygwin32_TARGET_OS */

160 161
# if 0
/* This is a potential POSIX version */
162 163
nat
initialize_virtual_timer(nat ms)
164 165 166 167 168
{
    struct sigevent se;
    struct itimerspec it;
    timer_t tid;

169 170 171
    timestamp = getourtimeofday();
    ticks_since_timestamp = 0;

172 173 174 175
    se.sigev_notify = SIGEV_SIGNAL;
    se.sigev_signo = SIGVTALRM;
    se.sigev_value.sival_int = SIGVTALRM;
    if (timer_create(CLOCK_VIRTUAL, &se, &tid)) {
176
	barf("can't create virtual timer");
177 178 179 180 181 182 183 184
    }
    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;
    timer_settime(tid, TIMER_RELTIME, &it, NULL);
}
# endif

sof's avatar
sof committed
185 186
#if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
int
187
install_vtalrm_handler(void)
sof's avatar
sof committed
188
{
189
  vtalrm_cback = handle_tick;
sof's avatar
sof committed
190 191 192 193
  return 0;
}

#else
194
int
195
install_vtalrm_handler(void)
196 197
{
    struct sigaction action;
198

199
    action.sa_handler = handle_tick;
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227

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

    return sigaction(SIGVTALRM, &action, NULL);
}

void
block_vtalrm_signal(void)
{
    sigset_t signals;
    
    sigemptyset(&signals);
    sigaddset(&signals, SIGVTALRM);

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

void
unblock_vtalrm_signal(void)
{
    sigset_t signals;
    
    sigemptyset(&signals);
    sigaddset(&signals, SIGVTALRM);

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

230 231 232
/* gettimeofday() takes around 1us on our 500MHz PIII.  Since we're
 * only calling it 50 times/s, it shouldn't have any great impact.
 */
rrt's avatar
rrt committed
233
#if !defined(mingw32_TARGET_OS)
andy's avatar
andy committed
234 235 236 237 238
unsigned int 
getourtimeofday(void)
{
  struct timeval tv;
  gettimeofday(&tv, (struct timezone *) NULL);
239 240
  return (tv.tv_sec * TICK_FREQUENCY +
	  tv.tv_usec * TICK_FREQUENCY / 1000000);
andy's avatar
andy committed
241
}
rrt's avatar
rrt committed
242 243 244 245 246 247 248
#else
unsigned int
getourtimeofday(void)
{
  return (unsigned int)GetTickCount() * 1000;
}
#endif