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