[project @ 2000-11-14 10:56:08 by simonmar]
[ghc-hetmet.git] / ghc / rts / Itimer.c
1 /* -----------------------------------------------------------------------------
2  * $Id: Itimer.c,v 1.19 2000/10/06 11:05:57 rrt 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 #if !defined(_AIX)
22 # define NON_POSIX_SOURCE
23 #endif
24
25 #include "Rts.h"
26 #include "RtsFlags.h"
27 #include "Itimer.h"
28 #include "Proftimer.h"
29 #include "Schedule.h"
30
31 /* As recommended in the autoconf manual */
32 # ifdef TIME_WITH_SYS_TIME
33 #  include <sys/time.h>
34 #  include <time.h>
35 # else
36 #  ifdef HAVE_SYS_TIME_H
37 #   include <sys/time.h>
38 #  else
39 #   include <time.h>
40 #  endif
41 # endif
42
43 #if HAVE_WINDOWS_H
44 # include <windows.h>
45 #endif
46  
47 lnat total_ticks = 0;
48
49 /* ticks left before next pre-emptive context switch */
50 int ticks_to_ctxt_switch = 0;
51
52 static
53 void
54 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
55 CALLBACK
56 handle_tick(UINT uID STG_UNUSED, UINT uMsg STG_UNUSED, DWORD dwUser STG_UNUSED,
57             DWORD dw1 STG_UNUSED, DWORD d STG_UNUSED);
58 #else
59 handle_tick(int unused STG_UNUSED);
60 #endif
61
62 /* -----------------------------------------------------------------------------
63    Tick handler
64
65    We use the ticker for time profiling.
66
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
69    signal handler.
70    -------------------------------------------------------------------------- */
71
72 static
73 void
74 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
75 CALLBACK
76 handle_tick(UINT uID STG_UNUSED, UINT uMsg STG_UNUSED, DWORD dwUser STG_UNUSED,
77             DWORD dw1 STG_UNUSED, DWORD d STG_UNUSED)
78 #else
79 handle_tick(int unused STG_UNUSED)
80 #endif
81 {
82   total_ticks++;
83
84 #ifdef PROFILING
85   handleProfTick();
86 #endif
87
88   /* so we can get a rough indication of the current time at any point
89    * without having to call gettimeofday() (see Select.c):
90    */
91   ticks_since_timestamp++;
92
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 */
97   }
98 }
99
100
101 /*
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
108  *
109  * 11/98: if the cygwin DLL supports setitimer(), then use it instead.
110  */
111
112 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
113
114 LPTIMECALLBACK vtalrm_cback;
115
116 nat
117 initialize_virtual_timer(nat ms)
118 {
119 # ifdef PROFILING
120   /* On Win32 setups that don't have support for
121      setitimer(), we use the MultiMedia API's timer
122      support.
123      
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)
127   */
128   unsigned int delay;
129   static unsigned int vtalrm_id;
130  
131   if (ms) {
132     delay = timeBeginPeriod(1);
133     if (delay == TIMERR_NOCANDO) { /* error of some sort. */
134       return delay;
135     }
136     vtalrm_id =
137       timeSetEvent(ms,      /* event every `delay' milliseconds. */
138                    1,       /* precision is within 1 ms */
139                    vtalrm_cback,
140                    TIME_CALLBACK_FUNCTION,     /* ordinary callback */
141                    TIME_PERIODIC);
142   } else {
143     timeKillEvent(vtalrm_id);
144     timeEndPeriod(1);
145   }
146 # endif
147
148   return 0;
149 }
150  
151 #else
152
153 nat
154 initialize_virtual_timer(nat ms)
155 {
156 # ifndef HAVE_SETITIMER
157   /*    fprintf(stderr, "No virtual timer on this system\n"); */
158     return -1;
159 # else
160     struct itimerval it;
161
162     timestamp = getourtimeofday();
163     ticks_since_timestamp = 0;
164
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));
169 # endif
170 }
171
172 #endif /* !cygwin32_TARGET_OS */
173
174 # if 0
175 /* This is a potential POSIX version */
176 nat
177 initialize_virtual_timer(nat ms)
178 {
179     struct sigevent se;
180     struct itimerspec it;
181     timer_t tid;
182
183     timestamp = getourtimeofday();
184     ticks_since_timestamp = 0;
185
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");
191     }
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);
196 }
197 # endif
198
199 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
200 int
201 install_vtalrm_handler(void)
202 {
203   vtalrm_cback = handle_tick;
204   return 0;
205 }
206
207 #else
208 int
209 install_vtalrm_handler(void)
210 {
211     struct sigaction action;
212
213     action.sa_handler = handle_tick;
214
215     sigemptyset(&action.sa_mask);
216     action.sa_flags = 0;
217
218     return sigaction(SIGVTALRM, &action, NULL);
219 }
220
221 void
222 block_vtalrm_signal(void)
223 {
224     sigset_t signals;
225     
226     sigemptyset(&signals);
227     sigaddset(&signals, SIGVTALRM);
228
229     (void) sigprocmask(SIG_BLOCK, &signals, NULL);
230 }
231
232 void
233 unblock_vtalrm_signal(void)
234 {
235     sigset_t signals;
236     
237     sigemptyset(&signals);
238     sigaddset(&signals, SIGVTALRM);
239
240     (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
241 }
242 #endif
243
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.
246  */
247 #if !defined(mingw32_TARGET_OS)
248 unsigned int 
249 getourtimeofday(void)
250 {
251   struct timeval tv;
252   gettimeofday(&tv, (struct timezone *) NULL);
253   return (tv.tv_sec * TICK_FREQUENCY +
254           tv.tv_usec * TICK_FREQUENCY / 1000000);
255 }
256 #else
257 unsigned int
258 getourtimeofday(void)
259 {
260   return (unsigned int)GetTickCount() * 1000;
261 }
262 #endif