[project @ 2001-11-26 12:58:17 by simonpj]
[ghc-hetmet.git] / ghc / rts / Itimer.c
1 /* -----------------------------------------------------------------------------
2  * $Id: Itimer.c,v 1.27 2001/11/26 12:58:17 simonpj 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 CALLBACK
65 handle_tick(UINT uID STG_UNUSED, UINT uMsg STG_UNUSED, DWORD dwUser STG_UNUSED,
66             DWORD dw1 STG_UNUSED, DWORD d STG_UNUSED)
67 #else
68 handle_tick(int unused STG_UNUSED)
69 #endif
70 {
71   total_ticks++;
72
73 #ifdef PROFILING
74   handleProfTick();
75 #endif
76
77   if (RtsFlags.ConcFlags.ctxtSwitchTicks > 0) {
78       ticks_to_ctxt_switch--;
79       if (ticks_to_ctxt_switch <= 0) {
80           ticks_to_ctxt_switch = RtsFlags.ConcFlags.ctxtSwitchTicks;
81           context_switch = 1;   /* schedule a context switch */
82       }
83   }
84 }
85
86
87 /*
88  * Handling timer events under cygwin32 is not done with signal/setitimer.
89  * Instead of the two steps of first registering a signal handler to handle
90  * \tr{SIGVTALRM} and then start generating them via @setitimer()@, we use
91  * the Multimedia API (MM) and its @timeSetEvent@. (Internally, the MM API
92  * creates a separate thread that will notify the main thread of timer
93  * expiry). -- SOF 7/96
94  *
95  * 11/98: if the cygwin DLL supports setitimer(), then use it instead.
96  */
97
98 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
99
100 LPTIMECALLBACK vtalrm_cback;
101
102 int
103 initialize_virtual_timer(nat ms)
104 {
105   /* On Win32 setups that don't have support for
106      setitimer(), we use the MultiMedia API's timer
107      support.
108      
109      The delivery of ticks isn't free; the performance hit should be checked.
110   */
111   unsigned int delay;
112   static unsigned int vtalrm_id = 0;
113   static unsigned int period = -1;
114  
115   /* A zero argument value means shutdown. */
116   if (ms != 0) {
117     TIMECAPS tc;
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     vtalrm_id =
130       timeSetEvent(ms,      /* event every `delay' milliseconds. */
131                    1,       /* precision is within 1 ms */
132                    vtalrm_cback,
133                    TIME_CALLBACK_FUNCTION,     /* ordinary callback */
134                    TIME_PERIODIC);
135   } else {
136     /* Shutdown the MM timer */
137     if ( vtalrm_id != 0 ) {
138       timeKillEvent(vtalrm_id);
139     }
140     if (period > 0) {
141       timeEndPeriod(period);
142     }
143   }
144
145 #ifdef PROFILING
146   initProfTimer();
147 #endif
148
149   return 0;
150 }
151  
152 #else
153
154 int
155 initialize_virtual_timer(nat ms)
156 {
157 # ifndef HAVE_SETITIMER
158   /*    fprintf(stderr, "No virtual timer on this system\n"); */
159     return -1;
160 # else
161     struct itimerval it;
162
163     timestamp = getourtimeofday();
164
165 #ifdef PROFILING
166     initProfTimer();
167 #endif
168
169     it.it_value.tv_sec = ms / 1000;
170     it.it_value.tv_usec = 1000 * (ms - (1000 * it.it_value.tv_sec));
171     it.it_interval = it.it_value;
172     return (setitimer(ITIMER_VIRTUAL, &it, NULL));
173 # endif
174 }
175
176 #endif /* !{mingw,cygwin32}_TARGET_OS */
177
178 # if 0
179 /* This is a potential POSIX version */
180 int
181 initialize_virtual_timer(nat ms)
182 {
183     struct sigevent se;
184     struct itimerspec it;
185     timer_t tid;
186
187     timestamp = getourtimeofday();
188
189     initProfTimer();
190
191     se.sigev_notify = SIGEV_SIGNAL;
192     se.sigev_signo = SIGVTALRM;
193     se.sigev_value.sival_int = SIGVTALRM;
194     if (timer_create(CLOCK_VIRTUAL, &se, &tid)) {
195         barf("can't create virtual timer");
196     }
197     it.it_value.tv_sec = ms / 1000;
198     it.it_value.tv_nsec = 1000000 * (ms - 1000 * it.it_value.tv_sec);
199     it.it_interval = it.it_value;
200     return timer_settime(tid, TIMER_RELTIME, &it, NULL);
201 }
202 # endif
203
204 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
205 int
206 install_vtalrm_handler(void)
207 {
208   vtalrm_cback = handle_tick;
209   return 0;
210 }
211
212 #else
213 int
214 install_vtalrm_handler(void)
215 {
216     struct sigaction action;
217
218     action.sa_handler = handle_tick;
219
220     sigemptyset(&action.sa_mask);
221     action.sa_flags = 0;
222
223     return sigaction(SIGVTALRM, &action, NULL);
224 }
225
226 void
227 block_vtalrm_signal(void)
228 {
229     sigset_t signals;
230     
231     sigemptyset(&signals);
232     sigaddset(&signals, SIGVTALRM);
233
234     (void) sigprocmask(SIG_BLOCK, &signals, NULL);
235 }
236
237 void
238 unblock_vtalrm_signal(void)
239 {
240     sigset_t signals;
241     
242     sigemptyset(&signals);
243     sigaddset(&signals, SIGVTALRM);
244
245     (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
246 }
247 #endif
248
249 /* gettimeofday() takes around 1us on our 500MHz PIII.  Since we're
250  * only calling it 50 times/s, it shouldn't have any great impact.
251  */
252 #if !defined(mingw32_TARGET_OS)
253 unsigned int 
254 getourtimeofday(void)
255 {
256   struct timeval tv;
257   gettimeofday(&tv, (struct timezone *) NULL);
258   return (tv.tv_sec * TICK_FREQUENCY +
259           tv.tv_usec * TICK_FREQUENCY / 1000000);
260 }
261 #else
262 unsigned int
263 getourtimeofday(void)
264 {
265   return ((unsigned int)GetTickCount() * TICK_FREQUENCY) / 1000;
266 }
267 #endif