1 /* -----------------------------------------------------------------------------
2 * $Id: Itimer.c,v 1.18 2000/09/11 15:04:08 rrt 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.
22 # define NON_POSIX_SOURCE
28 #include "Proftimer.h"
31 /* As recommended in the autoconf manual */
32 # ifdef TIME_WITH_SYS_TIME
33 # include <sys/time.h>
36 # ifdef HAVE_SYS_TIME_H
37 # include <sys/time.h>
49 /* ticks left before next pre-emptive context switch */
50 int ticks_to_ctxt_switch = 0;
54 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
57 handle_tick(int unused STG_UNUSED);
59 /* -----------------------------------------------------------------------------
62 We use the ticker for time profiling.
64 SMP note: this signal could be delivered to *any* thread. We have
65 to ensure that it doesn't matter which thread actually runs the
67 -------------------------------------------------------------------------- */
71 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
74 handle_tick(int unused STG_UNUSED)
82 /* so we can get a rough indication of the current time at any point
83 * without having to call gettimeofday() (see Select.c):
85 ticks_since_timestamp++;
87 ticks_to_ctxt_switch--;
88 if (ticks_to_ctxt_switch <= 0) {
89 ticks_to_ctxt_switch = RtsFlags.ConcFlags.ctxtSwitchTicks;
90 context_switch = 1; /* schedule a context switch */
96 * Handling timer events under cygwin32 is not done with signal/setitimer.
97 * Instead of the two steps of first registering a signal handler to handle
98 * \tr{SIGVTALRM} and then start generating them via @setitimer()@, we use
99 * the Multimedia API (MM) and its @timeSetEvent@. (Internally, the MM API
100 * creates a separate thread that will notify the main thread of timer
101 * expiry). -- SOF 7/96
103 * 11/98: if the cygwin DLL supports setitimer(), then use it instead.
106 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
109 vtalrm_handler is assigned and set up in Signals.c
111 vtalrm_id (defined in Signals.c) holds
112 the system id for the current timer (used to
113 later block/kill it.)
115 extern nat vtalrm_id;
116 TIMECALLBACK *vtalrm_cback;
119 initialize_virtual_timer(nat ms)
122 /* On Win32 setups that don't have support for
123 setitimer(), we use the MultiMedia API's timer
126 As the delivery of ticks isn't free, we only
127 enable it if we really needed, i.e., when profiling.
128 (the RTS now also needs timer ticks to implement
129 threadDelay in non-profiling mode, but the pure
130 Win32 port doesn't support that.....yet.)
132 unsigned int delay,vtalrm_id;
134 delay = timeBeginPeriod(1);
135 if (delay == TIMERR_NOCANDO) { /* error of some sort. */
139 timeSetEvent(ms, /* event every `delay' milliseconds. */
140 1, /* precision is within 5 millisecs. */
141 (LPTIMECALLBACK)vtalrm_cback,
152 initialize_virtual_timer(nat ms)
154 # ifndef HAVE_SETITIMER
155 /* fprintf(stderr, "No virtual timer on this system\n"); */
160 timestamp = getourtimeofday();
161 ticks_since_timestamp = 0;
163 it.it_value.tv_sec = ms / 1000;
164 it.it_value.tv_usec = 1000 * (ms - (1000 * it.it_value.tv_sec));
165 it.it_interval = it.it_value;
166 return (setitimer(ITIMER_VIRTUAL, &it, NULL));
170 #endif /* !cygwin32_TARGET_OS */
173 /* This is a potential POSIX version */
175 initialize_virtual_timer(nat ms)
178 struct itimerspec it;
181 timestamp = getourtimeofday();
182 ticks_since_timestamp = 0;
184 se.sigev_notify = SIGEV_SIGNAL;
185 se.sigev_signo = SIGVTALRM;
186 se.sigev_value.sival_int = SIGVTALRM;
187 if (timer_create(CLOCK_VIRTUAL, &se, &tid)) {
188 barf("can't create virtual timer");
190 it.it_value.tv_sec = ms / 1000;
191 it.it_value.tv_nsec = 1000000 * (ms - 1000 * it.it_value.tv_sec);
192 it.it_interval = it.it_value;
193 timer_settime(tid, TIMER_RELTIME, &it, NULL);
197 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
199 install_vtalrm_handler(void)
201 vtalrm_cback = handle_tick;
207 install_vtalrm_handler(void)
209 struct sigaction action;
211 action.sa_handler = handle_tick;
213 sigemptyset(&action.sa_mask);
216 return sigaction(SIGVTALRM, &action, NULL);
220 block_vtalrm_signal(void)
224 sigemptyset(&signals);
225 sigaddset(&signals, SIGVTALRM);
227 (void) sigprocmask(SIG_BLOCK, &signals, NULL);
231 unblock_vtalrm_signal(void)
235 sigemptyset(&signals);
236 sigaddset(&signals, SIGVTALRM);
238 (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
242 /* gettimeofday() takes around 1us on our 500MHz PIII. Since we're
243 * only calling it 50 times/s, it shouldn't have any great impact.
245 #if !defined(mingw32_TARGET_OS)
247 getourtimeofday(void)
250 gettimeofday(&tv, (struct timezone *) NULL);
251 return (tv.tv_sec * TICK_FREQUENCY +
252 tv.tv_usec * TICK_FREQUENCY / 1000000);
256 getourtimeofday(void)
258 return (unsigned int)GetTickCount() * 1000;