[project @ 2002-05-21 14:58:49 by wolfgang]
[ghc-hetmet.git] / ghc / rts / Itimer.c
1 /* -----------------------------------------------------------------------------
2  * $Id: Itimer.c,v 1.29 2001/11/27 01:57:59 sof 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 lnat total_ticks = 0;
47
48 /* ticks left before next pre-emptive context switch */
49 int ticks_to_ctxt_switch = 0;
50
51 /* -----------------------------------------------------------------------------
52    Tick handler
53
54    We use the ticker for time profiling.
55
56    SMP note: this signal could be delivered to *any* thread.  We have
57    to ensure that it doesn't matter which thread actually runs the
58    signal handler.
59    -------------------------------------------------------------------------- */
60
61 static
62 void
63 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
64
65 CALLBACK
66 handle_tick(UINT uID STG_UNUSED, UINT uMsg STG_UNUSED, DWORD dwUser STG_UNUSED,
67             DWORD dw1 STG_UNUSED, DWORD d STG_UNUSED)
68 #else
69 handle_tick(int unused STG_UNUSED)
70 #endif
71 {
72   total_ticks++;
73
74 #ifdef PROFILING
75   handleProfTick();
76 #endif
77
78   if (RtsFlags.ConcFlags.ctxtSwitchTicks > 0) {
79       ticks_to_ctxt_switch--;
80       if (ticks_to_ctxt_switch <= 0) {
81           ticks_to_ctxt_switch = RtsFlags.ConcFlags.ctxtSwitchTicks;
82           context_switch = 1;   /* schedule a context switch */
83       }
84   }
85 }
86
87
88 /*
89  * Handling timer events under cygwin32 is not done with signal/setitimer.
90  * Instead of the two steps of first registering a signal handler to handle
91  * \tr{SIGVTALRM} and then start generating them via @setitimer()@, we use
92  * the Multimedia API (MM) and its @timeSetEvent@. (Internally, the MM API
93  * creates a separate thread that will notify the main thread of timer
94  * expiry). -- SOF 7/96
95  *
96  * 11/98: if the cygwin DLL supports setitimer(), then use it instead.
97  */
98
99 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
100
101 static LPTIMECALLBACK vtalrm_cback;
102 static unsigned int vtalrm_id = 0;
103 static unsigned int period = -1;
104
105 int
106 startVirtTimer(nat ms)
107 {
108   /* On Win32 setups that don't have support for
109      setitimer(), we use the MultiMedia API's timer
110      support.
111      
112      The delivery of ticks isn't free; the performance hit should be checked.
113   */
114   unsigned int delay;
115   TIMECAPS tc;
116   
117   vtalrm_cback = handle_tick;
118   
119   if ( timeGetDevCaps(&tc, sizeof(TIMECAPS)) == TIMERR_NOERROR) {
120     period = tc.wPeriodMin;
121     delay = timeBeginPeriod(period);
122     if (delay == TIMERR_NOCANDO) { /* error of some sort. */
123       return -1;
124     }
125   } else {
126     return -1;
127   }
128     
129 #ifdef PROFILING
130   initProfTimer();
131 #endif
132
133   vtalrm_id =
134     timeSetEvent(ms,      /* event every `delay' milliseconds. */
135                  1,       /* precision is within 1 ms */
136                  vtalrm_cback,
137                  TIME_CALLBACK_FUNCTION,     /* ordinary callback */
138                  TIME_PERIODIC);
139
140   return 0;
141 }
142
143 int
144 stopVirtTimer()
145 {
146     /* Shutdown the MM timer */
147   if ( vtalrm_id != 0 ) {
148     timeKillEvent(vtalrm_id);
149   }
150   if (period > 0) {
151     timeEndPeriod(period);
152   }
153   
154   return 0;
155 }
156  
157 #else
158 static
159 int
160 install_vtalrm_handler(void)
161 {
162     struct sigaction action;
163
164     action.sa_handler = handle_tick;
165
166     sigemptyset(&action.sa_mask);
167     action.sa_flags = 0;
168
169     return sigaction(SIGVTALRM, &action, NULL);
170 }
171
172 int
173 startVirtTimer(nat ms)
174 {
175 # ifndef HAVE_SETITIMER
176   /*    fprintf(stderr, "No virtual timer on this system\n"); */
177     return -1;
178 # else
179     struct itimerval it;
180
181     install_vtalrm_handler();
182
183     timestamp = getourtimeofday();
184
185 #ifdef PROFILING
186     initProfTimer();
187 #endif
188
189     it.it_value.tv_sec = ms / 1000;
190     it.it_value.tv_usec = 1000 * (ms - (1000 * it.it_value.tv_sec));
191     it.it_interval = it.it_value;
192     return (setitimer(ITIMER_VIRTUAL, &it, NULL));
193 # endif
194 }
195
196 int
197 stopVirtTimer()
198 {
199 # ifndef HAVE_SETITIMER
200   /*    fprintf(stderr, "No virtual timer on this system\n"); */
201     return -1;
202 # else
203     struct itimerval it;
204   
205     it.it_value.tv_sec = 0;
206     it.it_value.tv_usec = 0;
207     it.it_interval = it.it_value;
208     return (setitimer(ITIMER_VIRTUAL, &it, NULL));
209 # endif
210 }
211
212 #endif /* !{mingw,cygwin32}_TARGET_OS */
213
214 # if 0
215 /* This is a potential POSIX version */
216 int
217 startVirtTimer(nat ms)
218 {
219     struct sigevent se;
220     struct itimerspec it;
221     timer_t tid;
222
223     timestamp = getourtimeofday();
224
225 #ifdef PROFILING
226     initProfTimer();
227 #endif
228
229     se.sigev_notify = SIGEV_SIGNAL;
230     se.sigev_signo = SIGVTALRM;
231     se.sigev_value.sival_int = SIGVTALRM;
232     if (timer_create(CLOCK_VIRTUAL, &se, &tid)) {
233         barf("can't create virtual timer");
234     }
235     it.it_value.tv_sec = ms / 1000;
236     it.it_value.tv_nsec = 1000000 * (ms - 1000 * it.it_value.tv_sec);
237     it.it_interval = it.it_value;
238     return timer_settime(tid, TIMER_RELTIME, &it, NULL);
239 }
240
241 int
242 stopVirtTimer()
243 {
244     struct sigevent se;
245     struct itimerspec it;
246     timer_t tid;
247
248     timestamp = getourtimeofday();
249
250     se.sigev_notify = SIGEV_SIGNAL;
251     se.sigev_signo = SIGVTALRM;
252     se.sigev_value.sival_int = SIGVTALRM;
253     if (timer_create(CLOCK_VIRTUAL, &se, &tid)) {
254         barf("can't create virtual timer");
255     }
256     it.it_value.tv_sec = 0;
257     it.it_value.tv_nsec = 0;
258     it.it_interval = it.it_value;
259     return timer_settime(tid, TIMER_RELTIME, &it, NULL);
260 }
261
262 # endif
263
264 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
265 #else
266 void
267 block_vtalrm_signal(void)
268 {
269     sigset_t signals;
270     
271     sigemptyset(&signals);
272     sigaddset(&signals, SIGVTALRM);
273
274     (void) sigprocmask(SIG_BLOCK, &signals, NULL);
275 }
276
277 void
278 unblock_vtalrm_signal(void)
279 {
280     sigset_t signals;
281     
282     sigemptyset(&signals);
283     sigaddset(&signals, SIGVTALRM);
284
285     (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
286 }
287 #endif
288
289 /* gettimeofday() takes around 1us on our 500MHz PIII.  Since we're
290  * only calling it 50 times/s, it shouldn't have any great impact.
291  */
292 #if !defined(mingw32_TARGET_OS)
293 unsigned int 
294 getourtimeofday(void)
295 {
296   struct timeval tv;
297   gettimeofday(&tv, (struct timezone *) NULL);
298   return (tv.tv_sec * TICK_FREQUENCY +
299           tv.tv_usec * TICK_FREQUENCY / 1000000);
300 }
301 #else
302 unsigned int
303 getourtimeofday(void)
304 {
305   return ((unsigned int)GetTickCount() * TICK_FREQUENCY) / 1000;
306 }
307 #endif