e40c83dae7d5fed4758c12eab479aea4678ad10c
[ghc-hetmet.git] / ghc / rts / Itimer.c
1 /* -----------------------------------------------------------------------------
2  * $Id: Itimer.c,v 1.30 2002/07/17 09:21:49 simonmar Exp $
3  *
4  * (c) The GHC Team, 1995-1999
5  *
6  * Interval timer for profiling and pre-emptive scheduling.
7  *
8  * ---------------------------------------------------------------------------*/
9
10 /*
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@.
16  * 
17  * Hence, we use the old-fashioned @setitimer@ that just about everyone seems
18  * to support.  So much for standards.
19  */
20
21 /* This is not posix compliant. */
22 /* #include "PosixSource.h" */
23
24 #include "Rts.h"
25 #include "RtsFlags.h"
26 #include "Itimer.h"
27 #include "Proftimer.h"
28 #include "Schedule.h"
29
30 /* As recommended in the autoconf manual */
31 # ifdef TIME_WITH_SYS_TIME
32 #  include <sys/time.h>
33 #  include <time.h>
34 # else
35 #  ifdef HAVE_SYS_TIME_H
36 #   include <sys/time.h>
37 #  else
38 #   include <time.h>
39 #  endif
40 # endif
41
42 #if HAVE_WINDOWS_H
43 # include <windows.h>
44 #endif
45  
46 #ifdef HAVE_SIGNAL_H
47 # include <signal.h>
48 #endif
49
50 lnat total_ticks = 0;
51
52 /* ticks left before next pre-emptive context switch */
53 int ticks_to_ctxt_switch = 0;
54
55 /* -----------------------------------------------------------------------------
56    Tick handler
57
58    We use the ticker for time profiling.
59
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
62    signal handler.
63    -------------------------------------------------------------------------- */
64
65 static
66 void
67 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
68
69 CALLBACK
70 handle_tick(UINT uID STG_UNUSED, UINT uMsg STG_UNUSED, DWORD dwUser STG_UNUSED,
71             DWORD dw1 STG_UNUSED, DWORD d STG_UNUSED)
72 #else
73 handle_tick(int unused STG_UNUSED)
74 #endif
75 {
76   total_ticks++;
77
78 #ifdef PROFILING
79   handleProfTick();
80 #endif
81
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 */
87       }
88   }
89 }
90
91
92 /*
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
99  *
100  * 11/98: if the cygwin DLL supports setitimer(), then use it instead.
101  */
102
103 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
104
105 static LPTIMECALLBACK vtalrm_cback;
106 static unsigned int vtalrm_id = 0;
107 static unsigned int period = -1;
108
109 int
110 startVirtTimer(nat ms)
111 {
112   /* On Win32 setups that don't have support for
113      setitimer(), we use the MultiMedia API's timer
114      support.
115      
116      The delivery of ticks isn't free; the performance hit should be checked.
117   */
118   unsigned int delay;
119   TIMECAPS tc;
120   
121   vtalrm_cback = handle_tick;
122   
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. */
127       return -1;
128     }
129   } else {
130     return -1;
131   }
132     
133 #ifdef PROFILING
134   initProfTimer();
135 #endif
136
137   vtalrm_id =
138     timeSetEvent(ms,      /* event every `delay' milliseconds. */
139                  1,       /* precision is within 1 ms */
140                  vtalrm_cback,
141                  TIME_CALLBACK_FUNCTION,     /* ordinary callback */
142                  TIME_PERIODIC);
143
144   return 0;
145 }
146
147 int
148 stopVirtTimer()
149 {
150     /* Shutdown the MM timer */
151   if ( vtalrm_id != 0 ) {
152     timeKillEvent(vtalrm_id);
153   }
154   if (period > 0) {
155     timeEndPeriod(period);
156   }
157   
158   return 0;
159 }
160  
161 #else
162 static
163 int
164 install_vtalrm_handler(void)
165 {
166     struct sigaction action;
167
168     action.sa_handler = handle_tick;
169
170     sigemptyset(&action.sa_mask);
171     action.sa_flags = 0;
172
173     return sigaction(SIGVTALRM, &action, NULL);
174 }
175
176 int
177 startVirtTimer(nat ms)
178 {
179 # ifndef HAVE_SETITIMER
180   /*    fprintf(stderr, "No virtual timer on this system\n"); */
181     return -1;
182 # else
183     struct itimerval it;
184
185     install_vtalrm_handler();
186
187     timestamp = getourtimeofday();
188
189 #ifdef PROFILING
190     initProfTimer();
191 #endif
192
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));
197 # endif
198 }
199
200 int
201 stopVirtTimer()
202 {
203 # ifndef HAVE_SETITIMER
204   /*    fprintf(stderr, "No virtual timer on this system\n"); */
205     return -1;
206 # else
207     struct itimerval it;
208   
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));
213 # endif
214 }
215
216 #endif /* !{mingw,cygwin32}_TARGET_OS */
217
218 # if 0
219 /* This is a potential POSIX version */
220 int
221 startVirtTimer(nat ms)
222 {
223     struct sigevent se;
224     struct itimerspec it;
225     timer_t tid;
226
227     timestamp = getourtimeofday();
228
229 #ifdef PROFILING
230     initProfTimer();
231 #endif
232
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");
238     }
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);
243 }
244
245 int
246 stopVirtTimer()
247 {
248     struct sigevent se;
249     struct itimerspec it;
250     timer_t tid;
251
252     timestamp = getourtimeofday();
253
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");
259     }
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);
264 }
265
266 # endif
267
268 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
269 #else
270 void
271 block_vtalrm_signal(void)
272 {
273     sigset_t signals;
274     
275     sigemptyset(&signals);
276     sigaddset(&signals, SIGVTALRM);
277
278     (void) sigprocmask(SIG_BLOCK, &signals, NULL);
279 }
280
281 void
282 unblock_vtalrm_signal(void)
283 {
284     sigset_t signals;
285     
286     sigemptyset(&signals);
287     sigaddset(&signals, SIGVTALRM);
288
289     (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
290 }
291 #endif
292
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.
295  */
296 #if !defined(mingw32_TARGET_OS)
297 unsigned int 
298 getourtimeofday(void)
299 {
300   struct timeval tv;
301   gettimeofday(&tv, (struct timezone *) NULL);
302   return (tv.tv_sec * TICK_FREQUENCY +
303           tv.tv_usec * TICK_FREQUENCY / 1000000);
304 }
305 #else
306 unsigned int
307 getourtimeofday(void)
308 {
309   return ((unsigned int)GetTickCount() * TICK_FREQUENCY) / 1000;
310 }
311 #endif