1 /* -----------------------------------------------------------------------------
2 * $Id: Itimer.c,v 1.31 2002/08/16 13:29:06 simonmar Exp $
4 * (c) The GHC Team, 1995-1999
6 * Interval timer for profiling and pre-emptive scheduling.
8 * ---------------------------------------------------------------------------*/
11 * The interval timer is used for profiling and for context switching in the
12 * threaded build. Though POSIX 1003.1b includes a standard interface for
13 * such things, no one really seems to be implementing them yet. Even
14 * Solaris 2.3 only seems to provide support for @CLOCK_REAL@, whereas we're
15 * keen on getting access to @CLOCK_VIRTUAL@.
17 * Hence, we use the old-fashioned @setitimer@ that just about everyone seems
18 * to support. So much for standards.
21 /* This is not posix compliant. */
22 /* #include "PosixSource.h" */
27 #include "Proftimer.h"
30 /* As recommended in the autoconf manual */
31 # ifdef TIME_WITH_SYS_TIME
32 # include <sys/time.h>
35 # ifdef HAVE_SYS_TIME_H
36 # include <sys/time.h>
50 static lnat total_ticks = 0;
52 /* ticks left before next pre-emptive context switch */
53 static int ticks_to_ctxt_switch = 0;
55 /* -----------------------------------------------------------------------------
58 We use the ticker for time profiling.
60 SMP note: this signal could be delivered to *any* thread. We have
61 to ensure that it doesn't matter which thread actually runs the
63 -------------------------------------------------------------------------- */
67 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
70 handle_tick(UINT uID STG_UNUSED, UINT uMsg STG_UNUSED, DWORD dwUser STG_UNUSED,
71 DWORD dw1 STG_UNUSED, DWORD d STG_UNUSED)
73 handle_tick(int unused STG_UNUSED)
82 if (RtsFlags.ConcFlags.ctxtSwitchTicks > 0) {
83 ticks_to_ctxt_switch--;
84 if (ticks_to_ctxt_switch <= 0) {
85 ticks_to_ctxt_switch = RtsFlags.ConcFlags.ctxtSwitchTicks;
86 context_switch = 1; /* schedule a context switch */
93 * Handling timer events under cygwin32 is not done with signal/setitimer.
94 * Instead of the two steps of first registering a signal handler to handle
95 * \tr{SIGVTALRM} and then start generating them via @setitimer()@, we use
96 * the Multimedia API (MM) and its @timeSetEvent@. (Internally, the MM API
97 * creates a separate thread that will notify the main thread of timer
98 * expiry). -- SOF 7/96
100 * 11/98: if the cygwin DLL supports setitimer(), then use it instead.
103 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
105 static LPTIMECALLBACK vtalrm_cback;
106 static unsigned int vtalrm_id = 0;
107 static unsigned int period = -1;
110 startVirtTimer(nat ms)
112 /* On Win32 setups that don't have support for
113 setitimer(), we use the MultiMedia API's timer
116 The delivery of ticks isn't free; the performance hit should be checked.
121 vtalrm_cback = handle_tick;
123 if ( timeGetDevCaps(&tc, sizeof(TIMECAPS)) == TIMERR_NOERROR) {
124 period = tc.wPeriodMin;
125 delay = timeBeginPeriod(period);
126 if (delay == TIMERR_NOCANDO) { /* error of some sort. */
138 timeSetEvent(ms, /* event every `delay' milliseconds. */
139 1, /* precision is within 1 ms */
141 TIME_CALLBACK_FUNCTION, /* ordinary callback */
150 /* Shutdown the MM timer */
151 if ( vtalrm_id != 0 ) {
152 timeKillEvent(vtalrm_id);
155 timeEndPeriod(period);
164 install_vtalrm_handler(void)
166 struct sigaction action;
168 action.sa_handler = handle_tick;
170 sigemptyset(&action.sa_mask);
173 return sigaction(SIGVTALRM, &action, NULL);
177 startVirtTimer(nat ms)
179 # ifndef HAVE_SETITIMER
180 /* fprintf(stderr, "No virtual timer on this system\n"); */
185 install_vtalrm_handler();
187 timestamp = getourtimeofday();
193 it.it_value.tv_sec = ms / 1000;
194 it.it_value.tv_usec = 1000 * (ms - (1000 * it.it_value.tv_sec));
195 it.it_interval = it.it_value;
196 return (setitimer(ITIMER_VIRTUAL, &it, NULL));
203 # ifndef HAVE_SETITIMER
204 /* fprintf(stderr, "No virtual timer on this system\n"); */
209 it.it_value.tv_sec = 0;
210 it.it_value.tv_usec = 0;
211 it.it_interval = it.it_value;
212 return (setitimer(ITIMER_VIRTUAL, &it, NULL));
216 #endif /* !{mingw,cygwin32}_TARGET_OS */
219 /* This is a potential POSIX version */
221 startVirtTimer(nat ms)
224 struct itimerspec it;
227 timestamp = getourtimeofday();
233 se.sigev_notify = SIGEV_SIGNAL;
234 se.sigev_signo = SIGVTALRM;
235 se.sigev_value.sival_int = SIGVTALRM;
236 if (timer_create(CLOCK_VIRTUAL, &se, &tid)) {
237 barf("can't create virtual timer");
239 it.it_value.tv_sec = ms / 1000;
240 it.it_value.tv_nsec = 1000000 * (ms - 1000 * it.it_value.tv_sec);
241 it.it_interval = it.it_value;
242 return timer_settime(tid, TIMER_RELTIME, &it, NULL);
249 struct itimerspec it;
252 timestamp = getourtimeofday();
254 se.sigev_notify = SIGEV_SIGNAL;
255 se.sigev_signo = SIGVTALRM;
256 se.sigev_value.sival_int = SIGVTALRM;
257 if (timer_create(CLOCK_VIRTUAL, &se, &tid)) {
258 barf("can't create virtual timer");
260 it.it_value.tv_sec = 0;
261 it.it_value.tv_nsec = 0;
262 it.it_interval = it.it_value;
263 return timer_settime(tid, TIMER_RELTIME, &it, NULL);
268 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
271 block_vtalrm_signal(void)
275 sigemptyset(&signals);
276 sigaddset(&signals, SIGVTALRM);
278 (void) sigprocmask(SIG_BLOCK, &signals, NULL);
282 unblock_vtalrm_signal(void)
286 sigemptyset(&signals);
287 sigaddset(&signals, SIGVTALRM);
289 (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
293 /* gettimeofday() takes around 1us on our 500MHz PIII. Since we're
294 * only calling it 50 times/s, it shouldn't have any great impact.
296 #if !defined(mingw32_TARGET_OS)
298 getourtimeofday(void)
301 gettimeofday(&tv, (struct timezone *) NULL);
302 return (tv.tv_sec * TICK_FREQUENCY +
303 tv.tv_usec * TICK_FREQUENCY / 1000000);
307 getourtimeofday(void)
309 return ((unsigned int)GetTickCount() * TICK_FREQUENCY) / 1000;