[project @ 2000-09-11 15:04:08 by rrt]
[ghc-hetmet.git] / ghc / rts / Itimer.c
1 /* -----------------------------------------------------------------------------
2  * $Id: Itimer.c,v 1.18 2000/09/11 15:04:08 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 #endif
57 handle_tick(int unused STG_UNUSED);
58
59 /* -----------------------------------------------------------------------------
60    Tick handler
61
62    We use the ticker for time profiling.
63
64    SMP note: this signal could be delivered to *any* thread.  We have
65    to ensure that it doesn't matter which thread actually runs the
66    signal handler.
67    -------------------------------------------------------------------------- */
68
69 static
70 void
71 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
72 CALLBACK
73 #endif
74 handle_tick(int unused STG_UNUSED)
75 {
76   total_ticks++;
77
78 #ifdef PROFILING
79   handleProfTick();
80 #endif
81
82   /* so we can get a rough indication of the current time at any point
83    * without having to call gettimeofday() (see Select.c):
84    */
85   ticks_since_timestamp++;
86
87   ticks_to_ctxt_switch--;
88   if (ticks_to_ctxt_switch <= 0) {
89       ticks_to_ctxt_switch = RtsFlags.ConcFlags.ctxtSwitchTicks;
90       context_switch = 1;       /* schedule a context switch */
91   }
92 }
93
94
95 /*
96  * Handling timer events under cygwin32 is not done with signal/setitimer.
97  * Instead of the two steps of first registering a signal handler to handle
98  * \tr{SIGVTALRM} and then start generating them via @setitimer()@, we use
99  * the Multimedia API (MM) and its @timeSetEvent@. (Internally, the MM API
100  * creates a separate thread that will notify the main thread of timer
101  * expiry). -- SOF 7/96
102  *
103  * 11/98: if the cygwin DLL supports setitimer(), then use it instead.
104  */
105
106 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
107
108 /*
109   vtalrm_handler is assigned and set up in Signals.c
110
111   vtalrm_id (defined in Signals.c) holds
112   the system id for the current timer (used to 
113   later block/kill it.)
114 */
115 extern nat vtalrm_id;
116 TIMECALLBACK *vtalrm_cback;
117  
118 nat
119 initialize_virtual_timer(nat ms)
120 {
121 # ifdef PROFILING
122   /* On Win32 setups that don't have support for
123      setitimer(), we use the MultiMedia API's timer
124      support.
125      
126      As the delivery of ticks isn't free, we only
127      enable it if we really needed, i.e., when profiling.
128      (the RTS now also needs timer ticks to implement
129      threadDelay in non-profiling mode, but the pure
130      Win32 port doesn't support that.....yet.)
131   */
132   unsigned int delay,vtalrm_id;
133  
134   delay = timeBeginPeriod(1);
135   if (delay == TIMERR_NOCANDO) { /* error of some sort. */
136      return delay;
137   }
138   vtalrm_id =
139     timeSetEvent(ms,     /* event every `delay' milliseconds. */
140                 1,       /* precision is within 5 millisecs. */
141                 (LPTIMECALLBACK)vtalrm_cback,
142                 0,
143                 TIME_PERIODIC);
144 # endif
145
146   return 0;
147 }
148  
149 #else
150
151 nat
152 initialize_virtual_timer(nat ms)
153 {
154 # ifndef HAVE_SETITIMER
155   /*    fprintf(stderr, "No virtual timer on this system\n"); */
156     return -1;
157 # else
158     struct itimerval it;
159
160     timestamp = getourtimeofday();
161     ticks_since_timestamp = 0;
162
163     it.it_value.tv_sec = ms / 1000;
164     it.it_value.tv_usec = 1000 * (ms - (1000 * it.it_value.tv_sec));
165     it.it_interval = it.it_value;
166     return (setitimer(ITIMER_VIRTUAL, &it, NULL));
167 # endif
168 }
169
170 #endif /* !cygwin32_TARGET_OS */
171
172 # if 0
173 /* This is a potential POSIX version */
174 nat
175 initialize_virtual_timer(nat ms)
176 {
177     struct sigevent se;
178     struct itimerspec it;
179     timer_t tid;
180
181     timestamp = getourtimeofday();
182     ticks_since_timestamp = 0;
183
184     se.sigev_notify = SIGEV_SIGNAL;
185     se.sigev_signo = SIGVTALRM;
186     se.sigev_value.sival_int = SIGVTALRM;
187     if (timer_create(CLOCK_VIRTUAL, &se, &tid)) {
188         barf("can't create virtual timer");
189     }
190     it.it_value.tv_sec = ms / 1000;
191     it.it_value.tv_nsec = 1000000 * (ms - 1000 * it.it_value.tv_sec);
192     it.it_interval = it.it_value;
193     timer_settime(tid, TIMER_RELTIME, &it, NULL);
194 }
195 # endif
196
197 #if defined(mingw32_TARGET_OS) || (defined(cygwin32_TARGET_OS) && !defined(HAVE_SETITIMER))
198 int
199 install_vtalrm_handler(void)
200 {
201   vtalrm_cback = handle_tick;
202   return 0;
203 }
204
205 #else
206 int
207 install_vtalrm_handler(void)
208 {
209     struct sigaction action;
210
211     action.sa_handler = handle_tick;
212
213     sigemptyset(&action.sa_mask);
214     action.sa_flags = 0;
215
216     return sigaction(SIGVTALRM, &action, NULL);
217 }
218
219 void
220 block_vtalrm_signal(void)
221 {
222     sigset_t signals;
223     
224     sigemptyset(&signals);
225     sigaddset(&signals, SIGVTALRM);
226
227     (void) sigprocmask(SIG_BLOCK, &signals, NULL);
228 }
229
230 void
231 unblock_vtalrm_signal(void)
232 {
233     sigset_t signals;
234     
235     sigemptyset(&signals);
236     sigaddset(&signals, SIGVTALRM);
237
238     (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
239 }
240 #endif
241
242 /* gettimeofday() takes around 1us on our 500MHz PIII.  Since we're
243  * only calling it 50 times/s, it shouldn't have any great impact.
244  */
245 #if !defined(mingw32_TARGET_OS)
246 unsigned int 
247 getourtimeofday(void)
248 {
249   struct timeval tv;
250   gettimeofday(&tv, (struct timezone *) NULL);
251   return (tv.tv_sec * TICK_FREQUENCY +
252           tv.tv_usec * TICK_FREQUENCY / 1000000);
253 }
254 #else
255 unsigned int
256 getourtimeofday(void)
257 {
258   return (unsigned int)GetTickCount() * 1000;
259 }
260 #endif