1 /* -----------------------------------------------------------------------------
2 * $Id: Itimer.c,v 1.19 2000/10/06 11:05:57 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))
56 handle_tick(UINT uID STG_UNUSED, UINT uMsg STG_UNUSED, DWORD dwUser STG_UNUSED,
57 DWORD dw1 STG_UNUSED, DWORD d STG_UNUSED);
59 handle_tick(int unused STG_UNUSED);
62 /* -----------------------------------------------------------------------------
65 We use the ticker for time profiling.
67 SMP note: this signal could be delivered to *any* thread. We have
68 to ensure that it doesn't matter which thread actually runs the
70 -------------------------------------------------------------------------- */
74 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
76 handle_tick(UINT uID STG_UNUSED, UINT uMsg STG_UNUSED, DWORD dwUser STG_UNUSED,
77 DWORD dw1 STG_UNUSED, DWORD d STG_UNUSED)
79 handle_tick(int unused STG_UNUSED)
88 /* so we can get a rough indication of the current time at any point
89 * without having to call gettimeofday() (see Select.c):
91 ticks_since_timestamp++;
93 ticks_to_ctxt_switch--;
94 if (ticks_to_ctxt_switch <= 0) {
95 ticks_to_ctxt_switch = RtsFlags.ConcFlags.ctxtSwitchTicks;
96 context_switch = 1; /* schedule a context switch */
102 * Handling timer events under cygwin32 is not done with signal/setitimer.
103 * Instead of the two steps of first registering a signal handler to handle
104 * \tr{SIGVTALRM} and then start generating them via @setitimer()@, we use
105 * the Multimedia API (MM) and its @timeSetEvent@. (Internally, the MM API
106 * creates a separate thread that will notify the main thread of timer
107 * expiry). -- SOF 7/96
109 * 11/98: if the cygwin DLL supports setitimer(), then use it instead.
112 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
114 LPTIMECALLBACK vtalrm_cback;
117 initialize_virtual_timer(nat ms)
120 /* On Win32 setups that don't have support for
121 setitimer(), we use the MultiMedia API's timer
124 As the delivery of ticks isn't free, we only
125 enable it if we really needed, i.e., when profiling.
126 (GetTickCount is used for threadDelay)
129 static unsigned int vtalrm_id;
132 delay = timeBeginPeriod(1);
133 if (delay == TIMERR_NOCANDO) { /* error of some sort. */
137 timeSetEvent(ms, /* event every `delay' milliseconds. */
138 1, /* precision is within 1 ms */
140 TIME_CALLBACK_FUNCTION, /* ordinary callback */
143 timeKillEvent(vtalrm_id);
154 initialize_virtual_timer(nat ms)
156 # ifndef HAVE_SETITIMER
157 /* fprintf(stderr, "No virtual timer on this system\n"); */
162 timestamp = getourtimeofday();
163 ticks_since_timestamp = 0;
165 it.it_value.tv_sec = ms / 1000;
166 it.it_value.tv_usec = 1000 * (ms - (1000 * it.it_value.tv_sec));
167 it.it_interval = it.it_value;
168 return (setitimer(ITIMER_VIRTUAL, &it, NULL));
172 #endif /* !cygwin32_TARGET_OS */
175 /* This is a potential POSIX version */
177 initialize_virtual_timer(nat ms)
180 struct itimerspec it;
183 timestamp = getourtimeofday();
184 ticks_since_timestamp = 0;
186 se.sigev_notify = SIGEV_SIGNAL;
187 se.sigev_signo = SIGVTALRM;
188 se.sigev_value.sival_int = SIGVTALRM;
189 if (timer_create(CLOCK_VIRTUAL, &se, &tid)) {
190 barf("can't create virtual timer");
192 it.it_value.tv_sec = ms / 1000;
193 it.it_value.tv_nsec = 1000000 * (ms - 1000 * it.it_value.tv_sec);
194 it.it_interval = it.it_value;
195 timer_settime(tid, TIMER_RELTIME, &it, NULL);
199 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
201 install_vtalrm_handler(void)
203 vtalrm_cback = handle_tick;
209 install_vtalrm_handler(void)
211 struct sigaction action;
213 action.sa_handler = handle_tick;
215 sigemptyset(&action.sa_mask);
218 return sigaction(SIGVTALRM, &action, NULL);
222 block_vtalrm_signal(void)
226 sigemptyset(&signals);
227 sigaddset(&signals, SIGVTALRM);
229 (void) sigprocmask(SIG_BLOCK, &signals, NULL);
233 unblock_vtalrm_signal(void)
237 sigemptyset(&signals);
238 sigaddset(&signals, SIGVTALRM);
240 (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
244 /* gettimeofday() takes around 1us on our 500MHz PIII. Since we're
245 * only calling it 50 times/s, it shouldn't have any great impact.
247 #if !defined(mingw32_TARGET_OS)
249 getourtimeofday(void)
252 gettimeofday(&tv, (struct timezone *) NULL);
253 return (tv.tv_sec * TICK_FREQUENCY +
254 tv.tv_usec * TICK_FREQUENCY / 1000000);
258 getourtimeofday(void)
260 return (unsigned int)GetTickCount() * 1000;