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